开发者

Prevent C++ DLL exception using try catch internally

开发者 https://www.devze.com 2023-03-04 17:27 出处:网络
I\'m developing a C++ DLL that allocate an array for the main application. The function return an error code and not t开发者_Go百科he pointer to the new created array, so the address of the first memb

I'm developing a C++ DLL that allocate an array for the main application. The function return an error code and not t开发者_Go百科he pointer to the new created array, so the address of the first member will be written in a parameter of the function. Example:

int foo(int** arrayPtr) {
  int* array = new int[10];
  *arrayPtr = array;
  return 0;
}

So, in the main I call the function that way:

int* myArray;
int ret;
ret = foo(&myArray);

Now myArray points to the new created array.

QUESTION 1: Is there a better way to do this?

Than the more interesting question. If I pass NULL as parameter for foo, I generate an Access Violation exception because

*arrayPtr = array;

will try to write in 0x00000.

So, I added a try-catch block

int foo(int** arrayPtr) {
  int* array = new int[10];
  try {
    *arrayPtr = array;
  } catch(...) {
    return 1;
  }
  return 0;
}

I expect that , when I call foo with NULL as parameter, it will return 1. Not true! It generate an exception.

QUESTION 2: Why the try-catch block in the DLL doesn't work?

Thanks to everyone!

P.S.: using try-catch for generating the same exception directly in the main doesn't generate an exception (or better, it's correctly handled by the try-catch block).


Assuming you're using VC++, try..catch will not catch access violations by default because the default exception handling model only catches synchronous exceptions and access violations are asynchronous exceptions. This is documented here: /EH (Exception Handling Model)

If you change your project settings to use /EHa instead of /EHsc then your try..catch will catch the access violation.

That said, why not explicitly check for NULL? Using exceptions for flow control is bad form.

int foo(int** arrayPtr) {
    if (!arrayPtr)
        return 1;
    *arrayPtr = new int[10];
    return 0;
}


  1. That's pretty much the way to do it. Just be sure to expose a function to delete the memory block allocated by calls to "foo" (just in case your dll uses a different CRT than the main app).

  2. Access violations are not supposed to throw C++ exceptions, although there is some setting in VC++ that would make a SEH exception to be mapped to a C++ one, which is generally considered a bad idea.


To question 1:
I don't see what is bad with the function. Can you define what you mean with better.
You could use std::shared_ptr with an array type or boost::shared_array to have a better resource handling. But this depends on the interface you want to use.

To question 2:

try {
    *arrayPtr = array;
  } catch(...) {
    return 1;
  }

When arrayPtr is NULL this will create an access violation. You cannot catch those with c++ try/catch blocks.


  1. The other option is to return the pointer instead of a code. Returning NULL for a failed allocation seems pretty obvious.

2a. Passing NULL to your function seems more like an error on the calling side. Terminating the program with access violation in not unreasonable. That will show the caller where his error is!

2b. A catch clause can only catch exceptions that are thrown from the C++ code. Hardware traps, like an access violation, are not caught.

If you don't want new to throw either, you can use new(std::nothrow) int[10];


Some systems won't let you catch null reference exceptions at all, so relying on handling for them is a bad idea. Particularly in a situation where you can simply do a check. Your foo function should look something like this:

int foo(int** arrayPtr) 
{   
    // If a valid pointer has been passed in...
    if(arrayPtr) {
        // update the pointer to point at the allocated memory.
        *arrayPtr = new int[10];   
        return 0; 
    } 
    return 1;
}

A better approach would be to pass the pointer in by reference. That way there's no way that a NULL pointer pointer can be passed in and your problem effectively goes away.

// Pass in a reference to an array pointer.  The reference can't be NULL...
int foo(int* &arrayPtr) 
{   
    // update the reference
    arrayPtr = new int[10];   
    return 0; 
}

Your calling code then becomes:

int* myArray; 
int ret; 
ret = foo(myArray); // Pass in by reference

I think it's also worth pointing out that with your original code, even if it had worked as you expected your catch block would have been leaking the allocated array, since you weren't cleaning it up:

int foo(int** arrayPtr) 
{   
    int* array = new int[10];   
    try {     
        *arrayPtr = array;   
    } 
    catch(...) 
    {     
        // EVEN IF THIS HAD WORKED AS INTENDED, YOU LEAK array BECAUSE IT'S NOT DELETED
        // delete array; // is missing
        return 1;       
    }   
    return 0; 
} 
0

精彩评论

暂无评论...
验证码 换一张
取 消