C++/CLI 未处理的异常将 3 个或更多参数传递给委托
C++/CLI unhandled exception passing 3 or more parameters to delegate
刚刚偶然发现了奇怪的行为。
我有一个非托管的 class(实际上是一些本机库的包装器):
//.h
class Wrapper
{
private:
void(*pCallback)(int, int /*, int*/);
public:
void SetCallback(void(*callback)(int, int /*, int*/));
void InvokeCallback();
};
//.cpp
void Wrapper::SetCallback(void(*callback)(int, int /*, int*/))
{
pCallback = callback;
}
void Wrapper::InvokeCallback()
{
pCallback(0, 0 /*, 0*/); //(1)
//(3)
}
和托管 class 这是 winforms 控件并使用上述非托管包装器:
public ref class MineControl : public System::Windows::Forms::Control
{
private:
Wrapper *pWrapper;
delegate void CallbackDelegate(int, int /*, int*/);
public:
MineControl()
{
/* rest of initialization here */
pWrapper = new Wrapper;
auto dlg = gcnew CallbackDelegate(this, &MineControl::Method);
auto ptr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dlg);
void(*callback)(int, int /*, int*/) = (void(*)(int, int /*, int*/))(ptr.ToPointer());
pWrapper->SetCallback(callback);
pWrapper->InvokeCallback();
}
void Method(int a, int b /*, int c*/)
{
//some code or even nothing at all
//(2)
}
}
这很好用。
直到我取消注释第三个参数。之后我在 (1)
上设置了断点。我可以输入 MineControl::Mehod
- (2)
。但是退出此方法时一切都失败了。点 (3)
变得不可到达。我在退出该方法时遇到未处理的异常。此外,VS 仍然无法处理该异常(调试非托管和托管代码的所有设置都已设置 - 这是 VS 无法捕获异常的唯一情况)。所以 Windows 尝试处理它 - 标准 App has stopped working
window 有两个选项 - 调试和关闭程序。但我无法调试,因为 VS 仍处于连接状态,要么不想分离,要么应用程序在 VS 分离时死机。
我可以将所有参数包装到某个结构中,这会很有效。但是有人能解释一下为什么添加第三个参数使得无法从托管代码返回到非托管代码吗?
我不知道发生了什么。
环境:VS2013,x86 项目,.net4.5
好的,我会post自己回答。解决方案实际上在 中。
默认调用约定是 sdtcall
但在我的例子中我需要 cdecl
调用约定。
使用 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
属性装饰委托解决了我的问题。
还有一点很有价值,那就是将委托保留在局部变量中是个坏主意。
刚刚偶然发现了奇怪的行为。
我有一个非托管的 class(实际上是一些本机库的包装器):
//.h
class Wrapper
{
private:
void(*pCallback)(int, int /*, int*/);
public:
void SetCallback(void(*callback)(int, int /*, int*/));
void InvokeCallback();
};
//.cpp
void Wrapper::SetCallback(void(*callback)(int, int /*, int*/))
{
pCallback = callback;
}
void Wrapper::InvokeCallback()
{
pCallback(0, 0 /*, 0*/); //(1)
//(3)
}
和托管 class 这是 winforms 控件并使用上述非托管包装器:
public ref class MineControl : public System::Windows::Forms::Control
{
private:
Wrapper *pWrapper;
delegate void CallbackDelegate(int, int /*, int*/);
public:
MineControl()
{
/* rest of initialization here */
pWrapper = new Wrapper;
auto dlg = gcnew CallbackDelegate(this, &MineControl::Method);
auto ptr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dlg);
void(*callback)(int, int /*, int*/) = (void(*)(int, int /*, int*/))(ptr.ToPointer());
pWrapper->SetCallback(callback);
pWrapper->InvokeCallback();
}
void Method(int a, int b /*, int c*/)
{
//some code or even nothing at all
//(2)
}
}
这很好用。
直到我取消注释第三个参数。之后我在 (1)
上设置了断点。我可以输入 MineControl::Mehod
- (2)
。但是退出此方法时一切都失败了。点 (3)
变得不可到达。我在退出该方法时遇到未处理的异常。此外,VS 仍然无法处理该异常(调试非托管和托管代码的所有设置都已设置 - 这是 VS 无法捕获异常的唯一情况)。所以 Windows 尝试处理它 - 标准 App has stopped working
window 有两个选项 - 调试和关闭程序。但我无法调试,因为 VS 仍处于连接状态,要么不想分离,要么应用程序在 VS 分离时死机。
我可以将所有参数包装到某个结构中,这会很有效。但是有人能解释一下为什么添加第三个参数使得无法从托管代码返回到非托管代码吗?
我不知道发生了什么。
环境:VS2013,x86 项目,.net4.5
好的,我会post自己回答。解决方案实际上在 sdtcall
但在我的例子中我需要 cdecl
调用约定。
使用 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
属性装饰委托解决了我的问题。
还有一点很有价值,那就是将委托保留在局部变量中是个坏主意。