这实际上是一个已经解决的问题,但它太深奥了,我以为我会为其他用户分享它.
也许其他人可能会提出理由?
无论如何,我正在研究用托管C++编写的"混合模式".NET应用程序,但是它与现有的本机库有很多链接.
问题是,未处理的托管异常最终成为Win32访问冲突.我的意思是,不是显示漂亮的.NET对话框,而是使用未处理的托管异常,而不是我将获得旧样式"未处理的win32异常发生在......"消息.
这是有趣的事情:如果我在调试器中启动应用程序,则会正确地拾取抛出的托管异常.即调试器向我显示该行.
但是,正常执行时,它将变为此访问冲突.在那一点附加调试器将产生很少的有用信息(它甚至不会显示合理的堆栈跟踪).
所以,对我而言,它表明在未处理的托管异常到达异常处理程序之前,本机代码中正在发生某些事情.
所以无论如何,我设法通过将我的项目与Visual Studio 2008生成的干净的新C++托管项目进行比较来解决问题.
解决方法是执行以下操作:
将/ SUBSYSTEM标志(项目属性 - >链接器 - >系统 - >子系统)从/SUBSYSTEM:WINDOWS 更改为 "未设置"
从使用旧样式WinMain()切换到使用新样式main().
也就是过去
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
Run Code Online (Sandbox Code Playgroud)
它现在是
int main(array<System::String ^> ^args)
Run Code Online (Sandbox Code Playgroud)
[为什么我使用这个奇怪的_tWinMain?这是多年前在创建示例混合模式Windows应用程序时由Visual Studio .NET IDE生成的.它一直很好(直到现在)所以我从来没有打扰过它._tWinMain只是WinMain的一个宏]
我做了这个改变,问题就消失了.未处理的.NET异常现在被正确捕获,因此我现在可以实际调试它们.
我还对干净的示例C++应用程序进行了相反的更改,并证明了它是原因.
所以,我真正的问题是,到底发生了什么?
是不是只使用了旧式WinMain而不是新款main(array <String^>^)
?
我是否应该向微软报告此事(任何人都会关心;-))?
我有一个本机C++ DLL,我希望有一个C++/CLI包装层.根据我的理解,如果你简单地将C++/CLI类添加到项目中,VS将编译为混合模式,但我显然是错误的,因为VS似乎甚至没有触及托管代码.
所以,对于一个已经存在的本地代码库什么究竟,一步一步的,你需要做的,创建一个混合模式DLL,这样我可以从任何.NET语言链接到该代码?
*我需要这样做,因为我的本机代码使用我无法P/Invoke进入的C++类.
对于我的C++/CLI项目,我只是尝试测量C++/CLI函数指针与.NET代理的成本.
我的期望是,C++/CLI函数指针比.NET代理更快.所以我的测试分别计算整个5秒内.NET委托和本机函数指针的调用次数.
现在结果对我来说(现在仍然令人震惊):
这意味着,本机C++/CLI函数指针的使用率比使用C++/CLI代码中的托管委托慢近3倍.怎么可能?在性能关键部分使用接口,委托或抽象类时,我应该使用托管构造吗?
连续调用的函数:
__int64 DoIt(int n, __int64 sum)
{
if ((n % 3) == 0)
return sum + n;
else
return sum + 1;
}
Run Code Online (Sandbox Code Playgroud)
调用该方法的代码尝试使用所有参数以及返回值,因此没有任何优化(希望如此).这是代码(对于.NET委托):
__int64 executions;
__int64 result;
System::Diagnostics::Stopwatch^ w = gcnew System::Diagnostics::Stopwatch();
System::Func<int, __int64, __int64>^ managedPtr = gcnew System::Func<int, __int64, __int64>(&DoIt);
w->Restart();
executions = 0;
result = 0;
while (w->ElapsedMilliseconds < 5000)
{
for (int i=0; i < 1000000; i++)
result += managedPtr(i, executions);
executions++;
}
System::Console::WriteLine(".NET …
Run Code Online (Sandbox Code Playgroud) 我正在尝试从本机C++可执行文件调试C#dll.我有一个C#COM对象,通过IDispatch从本机代码加载和运行.一切都是在Debug中构建的,包括C#和C++代码.虽然我可以看到所有的c ++代码,并且所有的c ++ dll都加载了符号并可用于调试,断点等C#代码拒绝播放.
我所看到的是C#dll都拒绝加载它们的符号pdbs,在模块窗口中报告"跳过ngen binary的加载符号".
顺便说一句,我在这里调试C#解决方案,我已经在COM项目的调试设置中将本机可执行文件设置为"启动外部程序".
现在我可以启动C++可执行文件然后附加到它,然后所有工作都按照我的预期 - 符号加载,我可以在C#中设置断点.
这是使用Visual Studio 2013u4.是否有启用混合模式调试的设置?一个问题是本机代码是使用VS2010构建的.
这是模块窗口 - 注意所有pdbs和dll都在一个目录中,你可以看到加载了c ++ dll,但没有加载C#.
这是模块窗口 - 注意EvCom dll(COM对象)的第3个条目,我假设它是启用调试的条目.
在输出窗口中没有任何兴趣,当加载COM dll时,我看到以下内容(在连接到运行进程的情况下,另一个只有2个Loaded行而不是3个).
'Explorer.exe' (Win32): Loaded 'C:\Dev\...\lib\debug\EvCom.dll'.
'Explorer.exe' (Win32): Loaded 'C:\Dev\...\lib\debug\EvCom.dll'.
'Explorer.exe' (Win32): Unloaded 'C:\...\lib\debug\EvCom.dll'
'Explorer.exe' (Win32): Loaded 'C:\Dev\...\lib\debug\EvCom.dll'.
Run Code Online (Sandbox Code Playgroud)
有一点感兴趣 - 我在调试设置中检查了"使用托管兼容模式",并且在开始调试时仍然没有加载我的符号,它只在模块列表中显示1个条目.这次为C#dlls说"符号文件中没有原生符号".
看起来问题是无法在VS2013(或2012)中选择调试器类型.这篇连接文章通过一些解决方法建议其"按设计".
我在混合模式C++/CLR .NET应用程序中遇到内存泄漏问题.
(它是使用"/ clr"编译器设置链接到VS2008 C++/CLR Windows窗体应用程序的C++本机静态库)
典型行为:应用程序开始使用30 MB(私有内存).然后泄漏内存减慢,比如在模拟重负载下运行时每小时一MB.这模拟了数天或数周的应用程序.
我尝试使用几种工具来跟踪内存泄漏,包括Visual Studio CRT库附带的CRT调试内容.我还使用了商业泄漏检测工具("Memory Validator").
两者都报告在关机时可忽略不计的内存泄漏(一些小的条目,相当于我不担心的几KB).此外,我可以看到,在运行时,跟踪的内存似乎没有那么多(所以我不相信它只是被占用的内存,只在app退出时释放).我得到大约5 MB的列出内存(总计> 30MB).
设置工具(Memory Validator)以跟踪所有内存使用情况(包括malloc,new,虚拟内存分配和一大堆其他类型的内存分配).基本上,已选择要跟踪的内存的每个设置.
.NET镜像报告它使用大约1.5 MB的内存(来自perfmon).
这是最后一点信息:我们有一个版本的应用程序作为本机控制台应用程序运行(纯粹是原生的 - 根本不是CLR).这与混合模式的95%相同,除了没有UI的东西.这根本不会泄漏内存,并且在大约5MB的私有字节处达到峰值.
所以基本上我试图在这里得到的是,我认为任何本机代码都不会泄漏内存.
另一个难题:我发现这是指在2.0框架(我是)时混合模式应用程序中的内存泄漏:http://support.microsoft.com/kb/961870
不幸的是,细节非常稀疏,所以我不确定它是否相关.我确实尝试针对3.5框架而不是2.0但仍然有同样的问题(也许我没有做到这一点).
有人有什么建议吗?
一些可能对我有帮助的事情:
谢谢你的帮助
约翰
根据这个问题,可以使用C++/CLI 无缝地组合托管代码和非托管代码.我不太明白 - 不管怎么说管理和非托管之间都不应该有编组?
例如,我有InnerLibrary,它被编译为带有头文件的本机C++ .dll和调用InnerLibrary代码的C++/CLI OuterLibrary.会不会有编组?谁将实施它,它的成本是多少?
我遇到了我创建的C++/CLI混合模式DLL的问题.卸载时抛出异常,因为使用它的.NET应用程序退出.在DLL_PROCESS_DETACH
执行之后,DLL使用自动注册的atexit() / __onexit()
函数执行运行时清理并抛出以下异常:
Unhandled exception at 0x752bb9bc (KernelBase.dll) in psstestm.exe:
0xC0020001: The string binding is invalid.
Run Code Online (Sandbox Code Playgroud)
我已经将问题跟踪到一个atexit()
由静态boost异常对象注册的调用get_static_exception_object()
.
function_to_call 0x0f560410 _t2m@???__Fep@?1???$get_static_exception_object@Ubad_exception_@exception_detail@boost@@@exception_detail@boost@@YA?AVexception_ptr@1@XZ@YAXXZ@?A0x0a546e27@@YAXXZ void (void)*
Run Code Online (Sandbox Code Playgroud)
我使用boost_1_47大部分静态链接,除了boost :: thread,动态链接以避免加载器锁.我也尝试动态链接所有的助手,这没有帮助.所有的升压包括都被#pragma unmanaged
块包围.
我希望有人有类似的问题或知道解决方案?
谢谢,马克
这是异常发生之前的调用堆栈:
psscorem.dll!_CRT_INIT(void * hDllHandle=0x0f4b0000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 413 C
psscorem.dll!__DllMainCRTStartup(void * hDllHandle=0x0f4b0000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 526 + 0x11 bytes C
psscorem.dll!_DllMainCRTStartup(void * hDllHandle=0x0f4b0000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 476 + 0x11 bytes …
Run Code Online (Sandbox Code Playgroud) 从Win32进程使用.NET DLL有哪些选择?我需要基本上使用Win32进程中的C#DLL.
我现在有一个可能的解决方案,需要将C#DLL添加到GAC(使用RegAsm.exe),然后通过COM包装调用调用C#DLL.然而,这个解决方案非常重.它要求在应该运行此Win32进程的所有计算机上将.NET DLL添加到GAC.
是否可以这样做而无需在RegAsm
使用C#DLL之前调用?
是否存在将单个.NET程序集拆分为完整程序集子集的实用程序?即ILMerge.exe的"功能逆转"?
当然,如果它必须跟踪类,函数等之间的依赖性等,那么这个工具很难产生.
但是,我正在寻找的情况是我有一个非常大(数百MB)的混合模式程序集,主要是静态类和静态方法,基本上只是一个函数库.虽然,与一些DLLMain初始化和类似.
我想要的是能够在我希望保留在子集程序集中的特定静态类上指定静态方法列表.从技术上讲,这应该是可能的,因为程序集只是具有标准格式的二进制信息.
这是否存在或如何制作?或者为什么这不切实际?
像大多数人一样,你可能知道存在很多不同的机制来从堆栈跟踪开始,从windows api开始,并继续深入神奇的汇编世界 - 让我列举一些我已经研究过的链接.
总之,让我提一下,我想要有混合模式(托管和非托管)/ 64位+ AnyCPU应用程序的内存泄漏分析机制,并且从所有windows api的CaptureStackBackTrace最适合我的需求,但正如我所分析的 - 它不支持托管代码堆栈行走.但是该函数API最接近我所需要的(因为它还计算回跟踪散列 - 特定调用堆栈的唯一标识符).
我已经排除了查找内存泄漏的不同方法 - 我尝试过的大多数软件要么崩溃要么不能正常工作,要么产生不好的结果.
此外,我不想重新编译现有的软件并覆盖malloc/new其他机制 - 因为它是一项繁重的任务(我们拥有大量代码库和大量的dll).此外,我怀疑这不是我需要执行的一次性工作 - 问题以1 - 2年周期回归,具体取决于谁和编码,所以我更希望在应用程序本身内置内存泄漏检测(内存) api挂钩)而不是一遍又一遍地解决这个问题.
http://www.codeproject.com/Articles/11132/Walking-the-callstack
使用StackWalk64 Windows API函数,但不能与托管代码一起使用.此外64位支持还不完全清楚 - 我已经看到64位问题的一些解决方法 - 我怀疑当在同一线程内完成堆栈遍历时,此代码不能完全正常工作.
然后存在进程黑客:http: //processhacker.sourceforge.net/
其中也使用StackWalk64,但扩展了它的回调函数(第7和第8个参数)以支持混合模式堆栈行走.在使用7/8回调函数进行了大量复杂操作之后,我还成功地获得了支持混合模式支持的StackWalk64(将堆栈跟踪作为向量捕获 - 其中每个指针指向调用过去的汇编/ dll位置).但正如您可能猜到的那样 - StackWalk64的性能不足以满足我的需求 - 即使使用C#端的简单消息框,应用程序只需"挂起"一段时间直到它正确启动.
我没有看到CaptureStackBackTrace函数调用的这么大的延迟,所以我认为StackWalk64的性能不足以满足我的需求.
还存在基于COM的堆栈跟踪确定方法 - 如下所示:http: //www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter
http://blog.steveniemitz.com/building-a-mixed-mode-stack-walker-part-1/
但是我害怕 - 它需要COM,并且线程需要进行初始化,并且由于内存api挂钩我不应该触摸任何线程中的com状态,因为它可能导致更严重的问题(例如,不正确的公寓初始化,其他毛刺)
现在我已经达到了Windows API不能满足我自己需求的程度,我需要手动遍历调用堆栈.例如,可以找到这样的例子:
http://www.codeproject.com/Articles/11221/Easy-Detection-of-Memory-Leaks 请参阅函数FillStackInfo/32位,不支持托管代码.
有几个关于反转堆栈跟踪的提及 - 例如在以下链接上:
特别是1,3,4个链接提供了一些有趣的夜间阅读.:-)
但即便如此,它们也是相当有趣的机制,其中任何一个都没有完全可用的演示示例.
我想其中一个例子是Wine的dbghelp实现(用于linux的Windows"模拟器"),它也显示了StackWalk64最终的工作原理,但我怀疑它与DWARF2文件格式的可执行文件有很大关系,因此它与当前的Windows PE不完全相同可执行文件格式.
有人可以指点我很好地实现堆栈行走,在64位架构上工作,支持混合模式(可以跟踪本机和托管内存分配),这完全是在寄存器/调用堆栈/代码分析中绑定的.(1,3,4的组合实施)
有人与Microsoft开发团队有任何良好的联系,他们可能会回答这个问题吗?
mixed-mode ×10
.net ×7
c++-cli ×4
c# ×3
c++ ×2
memory-leaks ×2
atexit ×1
boost ×1
debugging ×1
delegates ×1
dll ×1
il ×1
ilmerge ×1
interop ×1
marshalling ×1
performance ×1
stack-trace ×1
windows ×1