1

我完成了我的小应用程序,并试图确保我没有内存泄漏和错误。查看我的输出后,我注意到我的一个函数抛出了 First-Chance 异常,但该函数运行良好并且不会崩溃。

该函数调用 CLR C++ DLL 中的另一个函数。我删除了 DLL 函数中的几乎所有代码只是为了进行测试,但仍然抛出异常,所以我知道问题出在我的 EXE 函数中。

这是EXE函数调用DLL函数的代码。

LPCTSTR CHAXC0RDlg::Encrypt(LPCTSTR strValue)
{
    const char* Return;
    HINSTANCE hDLL = LoadLibrary(L"Library.dll");

    if(hDLL)
    {
        FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

        if(hMethod)
        {
            typedef const char* (*FunctionSig)(LPCTSTR);
            FunctionSig MethodCall = FunctionSig(hMethod);

            Return = MethodCall(strValue);
            FreeLibrary(hDLL);
        }
    }

    return _tcsdup(CString(Return));
}

这是 DLL 函数(如您所见,我删除了所有代码,除了生成返回值的代码作为测试):

const char* Encrypt(LPCTSTR strPValue)
{ 
    String^ strValue = gcnew String(strPValue);
    string strReturn = (const char*)(Marshal::StringToHGlobalAnsi(strValue)).ToPointer();

    char* csValue = new char[strReturn.size()];
    strcpy(csValue, strReturn.c_str());
    return const_cast<const char*> (csValue);
}

EXE 函数在“ const char* Return = MethodCall(strValue);”上引发异常(我启用了对这个异常的中断,这就是我所知道的)。

为什么这个函数会抛出这个异常?

谢谢你!

编辑

更新:我的字符集是 UNICODE。

更新#2:根据我在建议和答案中读到的内容,您假设此代码不起作用,但确实有效。我启用了对第一次机会异常的中断(是的,我确实知道什么是第一次机会异常),因为我希望这个程序质量很好,所有错误都消失了。代码运行良好,我只是想弄清楚为什么会抛出第一次机会异常,因为我喜欢成为一个更好的程序员。所以我想解决这个问题。

更新#3:我现在让我的代码检查 hDLL 和 hMethod 的值,并且在运行此函数时两者都不为空。问题似乎在于对 DLL 的调用。我假设函数签名是 100% 正确的,因为这段代码确实有效,它只是抛出了第一次机会异常。

更新#4:我在上面的函数中添加了新的更改,并添加了 DLL 函数代码。DLL 函数是一个 CLR C++ DLL。如前所述,我删除了 DLL 函数中的所有代码,以确保它不是我的 DLL。

4

5 回答 5

4

从代码片段中不清楚,但看起来您在用 C++/CLI 编写的托管函数上使用了 __declspec(dllexport)。String^ 和 Marshal 类清楚地表明了这一点。

在完全原生的应用程序中运行托管代码是可能的,但它需要加载和初始化 CLR。有几种方法可以做到这一点,我猜你偶然发现了最简单的方法。在托管函数上使用 __declspec(dllexport) 会强制编译器发出一个存根,该存根负责确保 CLR 已初始化并进行本机到托管代码的转换。

所以是的,当你一次调用这个导出函数时,一大堆代码会在你背后执行。这是存根需要加载和初始化 CLR 并加载包含托管代码的程序集的时间。看到这段代码抛出和处理异常通常不用担心。无论如何,您对此无能为力,您没有源代码。您可以通过自己托管 CLR 来减少意外事件。这需要一大块 COM 代码,不太确定是否值得付出代价。

从您提供的明确证据来看,这实际上不会导致问题,这是功能,而不是错误。请注意,您可以使用此类代码触发真实的、未处理的异常。如果托管代码抛出托管异常,这在托管代码中很常见,那么您将受到异常代码 0xe0434f4d 的 Windows SEH 异常的打击。这总是很糟糕,您可以使用 __try 和 __catch 关键字来捕获它,但您无法得到一个体面的诊断,因为当堆栈展开回您的代码时,所有有关托管异常的信息都是奇闻趣事。

于 2012-04-06T18:14:04.847 回答
1
  • 函数签名是否 100% 正确?还是缺少调用约定声明?(如WINAPI、STDCALL)。不太可能是您的问题的原因,但否则会让 oyu 陷入困境(请参阅 TONT
  • 有什么例外?如果抛出异常,请告诉调试器中断(在我的 Visual Studio 中,它是调试/异常,启用“抛出”......好吧,所有这些。
  • 您可能使用错误的 API(但这只会在以后出现)
  • 输入字符串是否有效?所有值都会发生这种情况吗?

您将看到“C++ 异常”或“访问冲突”之类的内容,以及其他诊断信息(如果幸运的话)。

这种第一次机会异常是“正常的”的可能性很小 - 即它总是发生在有效输入中。不过这种情况很少见,因为正常的执行路径不应该抛出异常。


返回值:

如果返回值在 DLL 内部的静态/内部缓冲区中(不需要释放),则在卸载 DLL 时它变得无效。

如果返回值是由 DLL 动态分配的(例如 DLL 执行 new[] 或 malloc 或 _tcsdup),则 DLL 还必须释放字符串,否则字符串将泄漏。不要释放调用者中的字符串,因为DLL 和调用者可能使用也可能不使用不同的堆。

检查 DLL 的文档,它应该说明谁需要释放返回的指针,以及返回的指针如何和/或多长时间有效。

于 2012-04-03T06:57:44.413 回答
1

仅当您第一次从 DLL 调用导出的函数时才会引发异常。它与第一次加载/初始化 CLR 有关,与您的代码没有任何关系(发生在您的代码有机会执行之前)。对 Encrypt 的后续调用不会生成此异常。似乎可以安全地忽略它。

于 2012-04-09T21:44:41.993 回答
0

目前尚不清楚您的项目中使用了哪个“字符集”(请参阅​​配置属性/常规)。您在“LoadLibary”中使用 L 宏,而不是在“GetProcAddress”中使用它。

HINSTANCE hDLL = LoadLibrary(L"Library.dll");
FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

尝试改用 _T 宏:

HINSTANCE hDLL = LoadLibrary(_T("Library.dll"));
FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

由于您的项目设置,它会为您放置“L”。

编辑:

尝试

_declspec( dllexport ) const char* Encrypt(LPCTSTR strPValue)
{
...
}

并确保您的模块定义文件包含

LIBRARY      "Library"

EXPORTS
    Encrypt @1

SECTIONS

 .data READ WRITE

也不是为什么不使用

const char* Encrypt(char* strPValue)

?

于 2012-04-03T06:22:27.097 回答
0

我把你的代码放在VS2010上,调用者(EXE)是C++控制台应用程序。并且 C++/CLI DLL 是使用 V90 工具集构建的,但我没有看到任何第一次机会异常被抛出。

我很确定你的头文件中有 _declspec( dllexport ),但我不确定你是如何定义它的。我的是这样的。

extern "C"
{
    __declspec(dllexport) const char* Encrypt(LPCTSTR strPValue);
}

并且没有模块定义文件(.def)。你能告诉我你的输出窗口上显示了什么吗?

于 2012-04-08T05:48:10.340 回答