我在一个具有try catch块的click处理程序中有一个简单的函数。如果我在这个try catch块中抛出异常,它会成功捕获异常。
如果我在抛出异常之前调用非托管DLL,则异常未处理且未被捕获。
什么是unamanged DLL调用,这可能会破坏我的程序异常处理?
如果我在调试模式下运行程序,它会捕获异常,即使对于所有异常都没有“中断异常”。应用程序不会崩溃并按预期运行。
如果我将程序运行为“start without debugging”并在崩溃时点击调试我得到以下错误“堆栈cookie检测代码检测到基于堆栈的缓冲区溢出”
修改 堆栈溢出似乎打破了异常处理
我附上了一个产生崩溃的简化程序。
ISOConnection _comm; //This is instantiated at another time in the same thread
//C# test function that crashes when run without a debugger attached
bool DoMagic()
{
try
{
//if I uncomment this line the exception becomes unhandled and cannot be caught
//_comm.ConnectISO15765();
throw new Exception();
}
catch (Exception ex)
{
MessageBox.Show("Caught exception")
}
//Within ISOConnection class
public void ConnectISO15765(){
...
lock(syncLock){
uint returnCode = J2534Interface.PassThruConnect((uint)DeviceId, (uint)ProtocolID.ISO15765, (uint)ConnectFlag.NONE, (uint)BaudRate.ISO15765, ref ChannelId);
//C# UnmanagedFunctionPointer allocation code
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint PassThruConnect(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelId);
public PassThruConnect Connect;
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
m_pDll = NativeMethods.LoadLibrary(path);
...
pAddressOfFunctionToCall = NativeMethods.GetProcAddress(m_pDll, "PassThruConnect");
if (pAddressOfFunctionToCall != IntPtr.Zero)
Connect = (PassThruConnect)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(PassThruConnect));
//C++ function declaration
long PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long Baudrate, unsigned long *pChannelID);
更新
如果我用以下内容替换对UnmanagedFunctionPointer PassThurConnect的调用,则不会发生崩溃
[DllImport("op20pt32.dll", EntryPoint = "PassThruConnect", CallingConvention = CallingConvention.Cdecl)]
public static extern uint PassThruConnect2(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelId);
在分配UnmanagedFunctionPointer会导致缺少调试器来创建堆栈溢出崩溃时,是否有我没有执行的操作或者我执行不正确?
甚至更奇怪的是这段代码似乎在几周前开始运作。主要的变化是try catch在另一个线程中而我没有使用lock(syncLock)。现在一切都在一个线程中,但是在BackgroundWorker中运行时也发生了同样的崩溃。
更新#2问题半解决
好的,所以我一个接一个地通过我的提交回滚,直到它工作。改变的是我从.NET 3.5到.NET 4.0
无论是否附加调试器,.NET 3.5都不会崩溃。如果未附加调试器,.NET 4.0会崩溃。为了排除我的代码中的错误,我只删除了我的日志的ConcurrentQueue(我使用的唯一4.0功能)并将我当前的代码库转换回3.5并且我没有收到此错误。
要100%确定这是4.0的问题我然后将我的代码库从3.5转换回4.0并将ConcurrentQueue保留了(字面上只是更改了构建选项并进行了重建)并且StackOverflow崩溃了。 / p>
我更愿意使用4.0,任何想法如何调试此问题?
编辑:.NET 4.6.1也崩溃了
更新#3 http://codenition.blogspot.com.au/2010/05/pinvokestackimbalance-in-net-40i-beg.html
显然,在.NET 3.5中基本上忽略了pinvokestackimbalance,所以问题仍然存在,它只是不会使我的应用程序崩溃。
将以下代码添加到App.Config会导致.NET在转换回托管代码时修复堆栈。虽然性能很小,但它可以解决问题。
虽然这确实解决了这个问题,但我想知道我的UnmanagedFunctionPointer有什么问题导致问题出现。
<configuration>
<runtime>
<NetFx40_PInvokeStackResilience enabled="1"/>
编辑:此帖子不重复,另一个被删除...
答案 0 :(得分:2)
好的问题是调用约定应该是StdCall而不是Cdecl
这是有道理的,因为通用J2534 API文档指定了以下标头。虽然我提供的头文件没有这个规范。
extern "C" long WINAPI PassThruConnect
(
unsigned long ProtocolID;
unsigned long Flags
unsigned long *pChannelID
)
其中WINAPI也称为StdCall,而不是大多数C / C ++库通常使用的Cdecl。
.NET 3.5允许错误的调用约定,并且将修复&#34;堆栈。从4.0开始,情况不再如此,并引发了PinvokeStackImbalance异常。
您可以使用以下代码添加到App.Config
来强制4.0修复堆栈<configuration>
<runtime>
<NetFx40_PInvokeStackResilience enabled="1"/>
或者您可以通过将Cdecl更改为StdCall来简单地修复您的调用约定:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate uint PassThruConnect(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelID);