【问题标题】:Access violation after catching dll exception捕获 dll 异常后访问冲突
【发布时间】:2010-11-01 08:45:16
【问题描述】:

我必须在运行时动态地将模块加载为 dll,因为它们事先并不知道,只是它们符合类接口。我注意到的是,在我捕获 dll 抛出的异常(在主线程的主程序中)之后,调用了正确的析构函数并销毁了模块并卸载了 dll,但随后作为 catch 块末尾的 }逐行执行时由 Visual Studio C++ 调试器到达,我得到另一个异常,该异常使程序崩溃

xxxxx.exe 中 0x68ad2377 (msvcr90d.dll) 处的第一次机会异常:0xC0000005:访问冲突读取位置 0x02958f14。

如果我启用对异常的中断,则在第二个异常上中断会将位置显示为

msvcr90d.dll!__DestructExceptionObject(EHExceptionRecord * pExcept=0x0017ee4c, unsigned char fThrowNotAllowed=0) 第 1803 行 + 0xf 字节

但看起来帧堆栈可能已损坏。我不知道为什么会抛出这个异常。

我的代码结构简化版如下:

一个非常简化的程序结构:

//shared header:
class Module
{
public:
    virtual void Foo(void) = 0;
};


//dll:
class SomeSpecificModule : public Module
{
public:
    virtual void Foo(void);
};

void SomeSpecificModule::Foo(void)
{
    throw 1;
}

extern "C" __declspec(dllexport) Module* GetModule()
{
    return new SomeSpecificModule;
}


//program:
typedef ptrGetModule* (*GetModule)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    ptrGetModule GetModule = (ptrGetModule)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

【问题讨论】:

  • 请发布整个堆栈跟踪

标签: c++ exception dll virtual access-violation


【解决方案1】:

要记住的是,C 运行时库的每个副本都有自己的状态。如果 SomeSpecificModule.dll 静态链接到 C 运行时库,则可能会发生此类问题。如果是这种情况,请尝试链接 C 运行时库的 DLL 版本。您还必须确保 SomeSpecificModule.dll 的编译和链接方式与您的主模块完全相同。

您提到 DLL 被卸载并调用了正确的析构函数,听起来您的真实程序比您发布的示例要多得多。如果您在 try 块中卸载了 SomeSpecificModule.dll,那么您已经卸载了 SomeSpecificModule::Foo() 的异常记录,我猜这就是您在 msvcr90d.dll!__DestructExceptionObject(EHExceptionRecord * ... 发生崩溃的原因

但是,通常跨 DLL 边界抛出异常是自找麻烦。如果您抛出非 POD 对象,您可能会遇到由不同堆中的不同 C 运行时库分配的内存、不同的编译器设置、STL 版本......你明白了。

更改您的代码,这样您就不会跨越 DLL 边界。有一天,您团队中的某个人更改了编译器设置或更改了第三方标头#define,并且您的程序开始崩溃,您将很难追查根本原因。

无论如何,没有看到真正的代码,我只是想猜测可能会出现什么问题。希望对您有所帮助。

【讨论】:

    【解决方案2】:

    当您的 DLL 引发异常时需要调用的大部分堆栈展开代码都在 DLL 中。如果卸载 DLL,如何调用该代码?

    不要跨动态链接的模块边界抛出异常。

    【讨论】:

      【解决方案3】:

      您是否在实际代码中按值处理异常?在这种情况下,catch 块末尾复制的异常对象的析构函数中可能存在异常。

      【讨论】:

        【解决方案4】:

        我没有在这段代码中看到 DLL 被卸载的地方(正如你所说的那样)。可以发一下相关代码吗?

        DLL 的卸载可能很关键,因为您的 DLL 包含销毁对象、展开堆栈等所需的代码,并且从您发布的内容中不清楚 DLL 的卸载时间。

        【讨论】:

          【解决方案5】:

          检查项目设置,如果您的应用程序是多线程的,那么您应该链接到多线程 DLL

          【讨论】:

            【解决方案6】:

            Canopus:当我抛出一个 int 作为异常时,也会发生同样的事情。

            TK___:我在所有项目中都链接到多线程 dll。

            Assaf 和 Shing Yip:这些 dll 确实是由 FreeLibrary() 在它们的包装器的析构函数中卸载的,因为包装器对象我推送到 tr1::shared_ptr 的向量中(因为包装器本身作为资源是不可复制的持有者,因此不能放入仅存在于 try{} 范围内的 STL 向量中。这似乎是正确的做法,因此我可以确保在出现错误情况时进行清理,包括 DLL 卸载,而且我更喜欢 RAII 风格的设计。如果这是问题的根源,那么我想知道使用哪种设计才能正确运行并且从软件工程的角度来看仍然看起来不错。 然而,让我怀疑这可能不是问题的原因是,当我单步执行抛出异常时发生的析构函数调用时,FreeLibrary() 运行时没有错误,我可以继续单步执行,直到我到达结束 }的捕获{}。

            Magnus Skog:在发布模式下,我也会遇到崩溃,而不是捕获异常然后继续正常执行。 动态内存由 1) operator new 在少数情况下处理,2) tr1::shared_ptr 和 3) _mm_malloc/_mm_free 在我需要对齐的地方。

            【讨论】:

            • 仅供参考。回复答案的首选方法是在答案中添加评论(您还不能这样做,因为您的代表不够高)或编辑原始问题。您确实应该使用此处的额外信息来扩展原始问题。如果他们有任何类似的症状,它将帮助未来的提问者找到这个问题。此外,它只会更好地“叙述”问题和解决方案。
            【解决方案7】:

            这可能是在黑暗中拍摄,但值得一试。

            您的应用程序似乎是在 DEBUG 中编译的,因为错误显示在 msvcr90d.dll 中。您使用的 dll 是否也在 DEBUG 中编译?在使用 dll 时,使用 msvcr90.dll 创建内存并使用 msvcr90d.dll 释放内存是一个常见问题。

            我认为你的函数指针 typedef 看起来有点可疑。我会这样写:

            typedef Module* (*moduleFnType)();
            
            int main(void)
            {
                HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
                moduleFnType GetModule = (moduleFnType)GetProcAddress(hMod, "GetModule");
                try
                {
                    Module *d = GetModule();
                    d->Foo();
                }
                catch (...)
                {
                    cout << '!' << endl;
                }
                return 0;
            }
            

            你的 typedef 没有说明函数 GetModule 的返回类型。

            【讨论】:

              猜你喜欢
              • 2010-10-02
              • 2022-01-07
              • 2015-08-28
              • 2023-03-19
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多