Предотвратить исключение C ++ DLL с помощью try catch внутренне - PullRequest
2 голосов
/ 10 мая 2011

Я разрабатываю C ++ DLL, которая выделяет массив для основного приложения.Функция возвращает код ошибки, а не указатель на новый созданный массив, поэтому адрес первого члена будет записан в параметре функции.Пример:

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

Итак, в основном я вызываю функцию следующим образом:

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

Теперь myArray указывает на новый созданный массив.

ВОПРОС 1:Есть ли лучший способ сделать это?

Чем интереснее вопрос.Если я передаю NULL в качестве параметра для foo, я генерирую исключение нарушения прав доступа, потому что

*arrayPtr = array;

попытается записать в 0x00000.

Итак, я добавил блок try-catch

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

Я ожидаю, что когда я вызову foo с NULL в качестве параметра, он вернет 1. Не верно!Он генерирует исключение.

ВОПРОС 2: Почему блок try-catch в DLL не работает?

Спасибо всем!

PS: использование try-catchгенерация того же исключения непосредственно в основном не генерирует исключение (или, что лучше, оно корректно обрабатывается блоком try-catch).

Ответы [ 5 ]

1 голос
/ 10 мая 2011

Предполагая, что вы используете VC ++, try..catch не будет отлавливать нарушения доступа по умолчанию, поскольку модель обработки исключений по умолчанию перехватывает только синхронные исключения, а нарушения доступа асинхронные исключения. Это задокументировано здесь: / EH (модель обработки исключений)

Если вы измените настройки своего проекта на использование /EHa вместо /EHsc, тогда ваш try..catch обнаружит нарушение прав доступа.

Тем не менее, почему бы не явно проверить NULL? Использование исключений для управления потоком имеет вид bad .

int foo(int** arrayPtr) {
    if (!arrayPtr)
        return 1;
    *arrayPtr = new int[10];
    return 0;
}
1 голос
/ 10 мая 2011
  1. Это в значительной степени способ сделать это.Обязательно предоставьте функцию для удаления блока памяти, выделенного вызовами "foo" (на тот случай, если ваша dll использует CRT, отличный от основного приложения).

  2. Нарушения доступане следует выдавать исключения C ++, хотя в VC ++ есть некоторые настройки, которые делают преобразование SEH в сопоставление с исключением C ++, что обычно считается плохой идеей.

0 голосов
/ 10 мая 2011
  1. Другой вариант - вернуть указатель вместо кода.Возвращение NULL для неудачного размещения кажется довольно очевидным.

2a.Передача NULL в вашу функцию выглядит скорее как ошибка на вызывающей стороне.Завершение работы программы с нарушением прав доступа не лишено смысла.Это покажет вызывающему, где его ошибка!

2b.Предложение catch может перехватывать только те исключения, которые вызываются из кода C ++.Аппаратные ловушки, как нарушение прав доступа, не обнаруживаются.

Если вы не хотите, чтобы new также бросал, вы можете использовать new(std::nothrow) int[10];

0 голосов
/ 10 мая 2011

Некоторые системы вообще не позволяют перехватывать исключения с нулевыми ссылками, поэтому полагаться на их обработку - плохая идея. Особенно в ситуации, когда вы можете просто сделать проверку. Ваша foo функция должна выглядеть примерно так:

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;
}

Лучшим подходом было бы передать указатель по ссылке. Таким образом, указатель на указатель NULL не может быть передан, и ваша проблема фактически исчезнет.

// 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; 
}

Ваш код вызова становится:

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

Я думаю, что также стоит указать, что с вашим исходным кодом, даже если бы он работал так, как вы ожидали, ваш блок catch мог бы просочиться в выделенный массив, поскольку вы не очищали его:

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 голосов
/ 10 мая 2011

На вопрос 1:
Я не вижу, что плохо с функцией. Можете ли вы определить, что вы имеете в виду под лучше.
Вы можете использовать std :: shared_ptr с типом массива или boost :: shared_array, чтобы лучше обрабатывать ресурсы. Но это зависит от интерфейса, который вы хотите использовать.

На вопрос 2:

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

Когда arrayPtr равен NULL, это приведет к нарушению прав доступа. Вы не можете поймать их с помощью блоков ++ / try / catch.

...