我的 C# 应用程序需要与用 C++ 编写的 DLL 对话。我没有此 DLL 的代码,但我有使用该 DLL 并可以运行的演示应用程序(也在 C++ 中)的代码。
这是来自 C++ 演示应用程序的有趣代码。它有两个函数,其中一个函数接受 4 个委托(delegate)的回调集。
typedef BOOL (CBAPI* OPENSCANNERSDK)(HWND hwnd, TCallBackSet* callbackSet, wchar_t* configPath);
typedef void (CBAPI* CLOSESCANNERSDK)();
typedef struct TCallBackSet
{
TOnScannerStatusEvent scannerStatusEvent;
TOnScannerNotifyEvent scannerNotifyEvent;
TOnRFIDStatusEvent rfidStatusEvent;
TOnRFIDNotifyEvent rfidNotifyEvent;
} TCallBackSet;
typedef void (cdecl* TOnScannerStatusEvent ) (int scannerStatus);
typedef void (cdecl* TOnScannerNotifyEvent) (int scannerNotify, int lParam);
typedef void (cdecl* TOnRFIDStatusEvent ) (int rfidStatus);
typedef void (cdecl* TOnRFIDNotifyEvent ) (int rfidnotify);
所以,我必须调用 OpenScannerSDK,传递一个带有指向某些函数的指针的回调集,做一些事情,最后调用 CloseScannerSDK。
我已经在我的 C# 应用程序中这样声明了:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_OpenScannerSDK")]
extern public static bool OpenScannerSDK(IntPtr hwnd, TCallBackSet callbackSet,
[MarshalAs(UnmanagedType.LPWStr)]string configPath);
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_CloseScannerSDK")]
extern public static void CloseScannerSDK();
[StructLayout(LayoutKind.Sequential)]
public class TCallBackSet
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TOnScannerStatusEvent(int scannerStatus);
[MarshalAs(UnmanagedType.FunctionPtr)]
public TOnScannerStatusEvent ScannerStatusEvent;
(I have removed the other 3 callbacks for brevity)
}
最后我像这样使用这个库:
var callback = new TCallBackSet() { ... set the delegates ... }
OpenScannerSDK(IntPtr.Zero, callback, ".");
... do other stuff...
CloseScannerSDK();
所有这一切似乎都有效 - OpenScannerSDK 和 CloseScannerSDK 以及我在它们之间使用的所有其他工具都可以正常工作。问题是,一旦应用程序试图退出,我就会在 KERNELBASE.dll 中得到一个 APPCRASH。我在崩溃报告文件中没有看到任何相关信息。我注意到,如果我不调用 OpenScannerSDK,而只是调用与委托(delegate)无关的 DLL 的其他一些函数,则 APPCRASH 不会发生。
我也为委托(delegate)尝试了 GC.KeepAlive,但没有效果。
最佳答案
几年前,我在将其他 C DLL 移植到 C# 时遇到了类似的问题。
C# 中的函数指针表示为委托(delegate)。在内部,委托(delegate)是类实例,它们由 GC 以与收集其他对象相同的方式收集。
可能在您的 DLL 中有一些方法可以“停止”API,使其停止调用 C++ 函数指针。您必须在关闭应用程序之前调用它。
可能您的应用程序收集委托(delegate)对象,当 C++ DLL 尝试从非托管代码调用时,发现无效的对象引用。
在 C# 中将这些委托(delegate)的引用保存在私有(private)字段中很有趣,以避免在应用程序运行时收集它们。此问题的症状是应用程序间歇性崩溃。
希望这对您有所帮助。
关于将 C++ 函数与委托(delegate)一起使用后,C# 应用程序在退出时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24740804/