【问题标题】:Prevent C++ DLL exception using try catch internally在内部使用 try catch 防止 C++ DLL 异常
【发布时间】:2011-08-22 13:25:42
【问题描述】:

我正在开发一个为主应用程序分配一个数组的 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;
}

我希望,当我以 NULL 作为参数调用 foo 时,它会返回 1。不是真的!它会生成一个异常。

问题 2:为什么 DLL 中的 try-catch 块不起作用?

谢谢大家!

P.S.:直接在 main 中使用 try-catch 生成相同的异常不会生成异常(或者更好的是,它由 try-catch 块正确处理)。

【问题讨论】:

  • 你为什么不做一个 if(!arrayPtr) 检查?此外,如果出现异常,您的代码会泄漏分配的数组,因为您没有删除它...

标签: c++ exception dll try-catch


【解决方案1】:

假设您使用的是 VC++,try..catch 默认不会捕获访问冲突,因为默认的异常处理模型只捕获 同步 异常,而访问冲突是 异步 异常.这记录在这里:/EH (Exception Handling Model)

如果您将项目设置更改为使用/EHa 而不是/EHsc,那么您的try..catch 将捕获访问冲突。

也就是说,为什么不明确检查NULL?使用流控制异常是不好的形式。

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

【讨论】:

  • 抱歉,没有提到我的 IDE。我正在使用 Borland(一个非常旧的版本......)。有人知道如何设置 /EHa 选项吗?检查 NULL 似乎很容易,但如果有人给我发送一个随机指针怎么办? (即地址 0x1,或者不同进程使用的内存位置)当然是他的错,但是......
  • @il_mix :“当然是他的错,但是……” 没错,没错。最好检查 NULL 并保留它。
【解决方案2】:
  1. 差不多就是这样。只需确保公开一个函数来删除调用“foo”分配的内存块(以防您的 dll 使用与主应用程序不同的 CRT)。

  2. 访问冲突不应该引发 C++ 异常,尽管 VC++ 中有一些设置会使 SEH 异常映射到 C++ 异常,这通常被认为是一个坏主意。

【讨论】:

  • try-catch 无法处理访问冲突....有趣!我会对此进行更多调查。我已经公开了一种删除 DLL 分配的内存的方法。
【解决方案3】:

问题1:
我看不出这个功能有什么问题。你能用“更好”来定义你的意思吗?
您可以使用带有数组类型的 std::shared_ptr 或 boost::shared_array 来获得更好的资源处理。但这取决于您要使用的界面。

问题2:

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

当 arrayPtr 为 NULL 时,这将创建访问冲突。您无法使用 c++ try/catch 块捕获那些。

【讨论】:

    【解决方案4】:
    1. 另一个选项是返回指针而不是代码。分配失败时返回 NULL 似乎很明显。

    2a。将 NULL 传递给您的函数似乎更像是调用方的错误。以访问冲突终止程序并非不合理。这将向调用者显示他的错误在哪里!

    2b。 catch 子句只能捕获从 C++ 代码抛出的异常。硬件陷阱(如访问冲突)不会被捕获。

    如果你不想new也扔,你可以使用new(std::nothrow) int[10];

    【讨论】:

    • 我要返回错误码而不是指针,所以需要在函数传递的参数中写入数组地址。
    • @il_max - 是的,这是一种选择。不过,我不会太努力地检测 NULL 参数。通过 NULL 的人可能也不会检查返回码!
    【解决方案5】:

    有些系统根本不允许您捕获空引用异常,因此依赖处理它们是一个坏主意。特别是在您可以简单地进行检查的情况下。您的 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; 
    } 
    

    【讨论】:

    • 我会尝试按引用的方式。关于数组的异常删除,当然需要先删除再返回。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-30
    • 1970-01-01
    • 1970-01-01
    • 2012-05-04
    • 2013-12-05
    相关资源
    最近更新 更多