gpt4 book ai didi

c# - 混合模式 C++/CLI 崩溃 : heap corruption in atexit (static destructor registration)

转载 作者:太空狗 更新时间:2023-10-29 23:18:30 26 4
gpt4 key购买 nike

我正在部署一个程序,代码库是 C++/CLI 和 C# 的混合体。 C++/CLI 具有各种风格: native 、混合 (/clr) 和安全 (/clr:safe)。在我的开发环境中,我创建了一个包含所有 C++/CLI 代码的 DLL,并从 C# 代码 (EXE) 中引用它。此方法完美无缺。

对于我想要发布单个可执行文件的版本(简单地说明“为什么不将 DLL 和 EXE 分开?”是 Not Acceptable )。

到目前为止,我已经成功地用所有不同的来源编译了 EXE。但是,当我运行它时,会出现“XXXX 已停止工作”对话框,其中包含联机检查、关闭和调试选项。问题详情如下:

Problem Event Name:       APPCRASH
Fault Module Name: StackHash_8d25
Fault Module Version: 6.1.7600.16559
Fault Module Timestamp: 4ba9b29c
Exception Code: c0000374
Exception Offset: 000cdc9b
OS Version: 6.1.7600.2.0.0.256.48
Locale ID: 1033
Additional Information 1: 8d25
Additional Information 2: 8d25552d834e8c143c43cf1d7f83abb8
Additional Information 3: 7450
Additional Information 4: 74509ce510cd821216ce477edd86119c

如果我调试并将它发送到 Visual Studio,它会报告:

Unhandled exception at 0x77d2dc9b in XXX.exe: A heap has been corrupted

选择 break 会导致它在 ntdll.dll!77d2dc9b() 处停止,没有其他信息。如果我告诉 Visual Studio 继续,程序会正常启动并且似乎可以正常工作,可能是因为现在附加了调试器。

你怎么看这个?如何避免这种堆损坏?除此以外,该程序似乎运行良好。

我的简化编译脚本如下(为简洁起见,我省略了我的错误检查):

@set TARGET=x86
@set TARGETX=x86
@set OUT=%TARGETX%
@call "%VS90COMNTOOLS%\..\..\VC\vcvarsall.bat" %TARGET%

@set WIMGAPI=C:\Program Files\Windows AIK\SDKs\WIMGAPI\%TARGET%

set CL=/Zi /nologo /W4 /O2 /GS /EHa /MD /MP /D NDEBUG /D _UNICODE /D UNICODE /D INTEGRATED /Fd%OUT%\ /Fo%OUT%\
set INCLUDE=%WIMGAPI%;%INCLUDE%
set LINK=/nologo /LTCG /CLRIMAGETYPE:IJW /MANIFEST:NO /MACHINE:%TARGETX% /SUBSYSTEM:WINDOWS,6.0 /OPT:REF /OPT:ICF /DEFAULTLIB:msvcmrt.lib
set LIB=%WIMGAPI%;%LIB%
set CSC=/nologo /w:4 /d:INTEGRATED /o+ /target:module

:: Compiling resources omitted

@set CL_NATIVE=/c /FI"stdafx-native.h"
@set CL_MIXED=/c /clr /LN /FI"stdafx-mixed.h"
@set CL_PURE=/c /clr:safe /LN /GL /FI"stdafx-pure.h"

@set NATIVE=...
@set MIXED=...
@set PURE=...

cl %CL_NATIVE% %NATIVE%
cl %CL_MIXED% %MIXED%
cl %CL_PURE% %PURE%
link /LTCG /NOASSEMBLY /DLL /OUT:%OUT%\core.netmodule %OUT%\*.obj

csc %CSC% /addmodule:%OUT%\core.netmodule /out:%OUT%\GUI.netmodule /recurse:*.cs

link /FIXED /ENTRY:GUI.Program.Main /OUT:%OUT%\XXX.exe ^
/ASSEMBLYRESOURCE:%OUT%\core.resources,XXX.resources,PRIVATE /ASSEMBLYRESOURCE:%OUT%\GUI.resources,GUI.resources,PRIVATE ^
/ASSEMBLYMODULE:%OUT%\core.netmodule %OUT%\gui.res %OUT%\*.obj %OUT%\GUI.netmodule

更新 1

在使用调试符号编译并再次尝试后,我确实获得了更多信息。调用堆栈是:

msvcr90d.dll!_msize_dbg(void * pUserData, int nBlockUse)  Line 1511 + 0x30 bytes
msvcr90d.dll!_dllonexit_nolock(int (void)* func, void (void)* * * pbegin, void (void)* * * pend) Line 295 + 0xd bytes
msvcr90d.dll!__dllonexit(int (void)* func, void (void)* * * pbegin, void (void)* * * pend) Line 273 + 0x11 bytes
XXX.exe!_onexit(int (void)* func) Line 110 + 0x1b bytes
XXX.exe!atexit(void (void)* func) Line 127 + 0x9 bytes
XXX.exe!`dynamic initializer for 'Bytes::Null''() Line 7 + 0xa bytes
mscorwks.dll!6cbd1b5c()
[Frames below may be incorrect and/or missing, no symbols loaded for mscorwks.dll]
...

我的代码行“导致”这个(Bytes::Null 的动态初始值设定项)是:

Bytes Bytes::Null;

在 header 中声明为:

class Bytes { public: static Bytes Null; }

我也试过像这样在标题中做一个全局外部:

extern Bytes Null; // header
Bytes Null; // cpp file

以同样的方式失败。

似乎是 CRT atexit 函数负责,由于静态初始化程序而无意中需要。


修复

正如 Ben Voigt 所指出的,使用任何 CRT 函数(包括 native 静态初始化程序)都需要对 CRT 进行适当的初始化(这发生在 mainCRTStartupWinMainCRTStartup_DllMainCRTStartup)。我添加了一个包含 C++ mainWinMain 的混合 C++/CLI 文件:

using namespace System;
[STAThread] // required if using an STA COM objects (such as drag-n-drop or file dialogs)
int main() { // or "int __stdcall WinMain(void*, void*, wchar_t**, int)" for GUI applications
array<String^> ^args_orig = Environment::GetCommandLineArgs();
int l = args_orig->Length - 1; // required to remove first argument (program name)
array<String^> ^args = gcnew array<String^>(l);
if (l > 0) Array::Copy(args_orig, 1, args, 0, l);
return XXX::CUI::Program::Main(args); // return XXX::GUI::Program::Main(args);
}

这样做之后,程序现在更进一步了,但仍然存在问题(将在其他地方解决):

  • 当程序仅使用 C# 时,它工作正常,只要它只是调用 C++/CLI 方法、获取 C++/CLI 属性和创建托管 C++/CLI 对象
  • C# 添加到 C++/CLI 代码中的事件永远不会触发(即使它们应该触发)
  • 另一个奇怪的错误是,发生的异常是 InvalidCastException,表示无法从 X 转换为 X(其中 X 与 X 相同...)

但是,由于堆损坏已修复(通过初始化 CRT),问题就解决了。

最佳答案

编辑:发现问题,将建议的调试步骤留在下面,以防将来对任何人有帮助。

问题是您更改了入口点。您应该使用 C++/CLI 标准库提供的入口点,它设置内部资源,如 onexit 列表。

删除 /ENTRY 开关并编写一个简单的 main 函数来调用所需的启动例程。


虽然最终产品可能无法接受使用单独的 EXE 和 DLL,但最好测试一下这种更简单的配置,看看是否会遇到同样的问题。

如果您可以使用单独的 .DLL 重现堆损坏,您就会知道它位于您的 native C++ 代码中的某个位置,并且在不将 C# 混合到同一文件的情况下进行调试会容易得多。

如果您无法使用单独的 DLL 和 EXE 重现问题,那么它可能与集成过程有关(或者它可能不太明显,因为布局会根据链接的内容而变化)。

找到并消除堆损坏错误后,您可以返回到单个 .EXE。

另一种方法是构建调试数据库,这样您就可以在它崩溃时获得更好的堆栈跟踪。甚至发布版本(或者特别是发布版本)也应该使用调试信息构建。

关于c# - 混合模式 C++/CLI 崩溃 : heap corruption in atexit (static destructor registration),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4928763/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com