从 C++ DLL 调用时,带有参数的 C# 委托回调会导致 AccessViolation
C# delegate callback with params cause AccessViolation when calling from C++ DLL
我有一个非托管 C++ DLL 和一个使用 P/Invoke 进行通信的 .net 应用程序。
我需要从 C++ DLL 更新 .net 应用程序中的字典。
我尝试通过以下方式做到这一点:
public delegate void MyDelegate(string address, string username);
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveMyDelegate(MyDelegate callback);
private static SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
public void save_delegate() {
SaveMyDelegate(delegate(string address, string username) {
if (username != "") {
dict.Add(address, username);
}
});
}
在 C++ 方面我有:
typedef void (*MANAGED_CALLBACK)(string user_address, string username);
extern "C" __declspec(dllexport) void SaveMyDelegate(MANAGED_CALLBACK callback);
MANAGED_CALLBACK my_callback;
void SaveMyDelegate(MANAGED_CALLBACK callback) {
my_callback = callback;
}
extern "C" __declspec(dllexport) VOID start_collection();
VOID start_collection() {
my_callback("test", "User");
}
在 C# 上,我执行以下调用:
[DllImport("MyDLL.dll")]
public static extern void start_collection();
private void Button1_Click(object sender, EventArgs e) {
save_delegate();
start_collection();
}
当我调用 start_collection() 时,我得到一个 AccessViolation 异常。
我尝试通过以下方式声明委托:
public delegate void MyDelegate(
[MarshalAs(UnmanagedType.LPStr)]string address,
[MarshalAs(UnmanagedType.LPStr)]string username);
我也添加了这个声明:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void MyDelegate(...)
我没有查看您代码的每个细节,但一些常见的 p/invoke 问题会立即出现:
- 您不能将 C++ 字符串用作互操作类型。您应该在 C++ 端使用空终止字符数组。使用
std::string
. 的 c_str()
方法获取这些
- 委托人可能使用了错误的调用约定。非托管代码(可能)使用
cdecl
,因此您需要在 declaring the delegate type in C#. 时使用 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
属性
- 您的 C# 委托很容易被提前收集(而您的非托管代码仍然持有对它的引用),因为没有对它的托管引用。通常,这可以通过在 C# 代码的静态变量中保留对委托的引用来解决。
我有一个非托管 C++ DLL 和一个使用 P/Invoke 进行通信的 .net 应用程序。 我需要从 C++ DLL 更新 .net 应用程序中的字典。
我尝试通过以下方式做到这一点:
public delegate void MyDelegate(string address, string username);
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveMyDelegate(MyDelegate callback);
private static SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
public void save_delegate() {
SaveMyDelegate(delegate(string address, string username) {
if (username != "") {
dict.Add(address, username);
}
});
}
在 C++ 方面我有:
typedef void (*MANAGED_CALLBACK)(string user_address, string username);
extern "C" __declspec(dllexport) void SaveMyDelegate(MANAGED_CALLBACK callback);
MANAGED_CALLBACK my_callback;
void SaveMyDelegate(MANAGED_CALLBACK callback) {
my_callback = callback;
}
extern "C" __declspec(dllexport) VOID start_collection();
VOID start_collection() {
my_callback("test", "User");
}
在 C# 上,我执行以下调用:
[DllImport("MyDLL.dll")]
public static extern void start_collection();
private void Button1_Click(object sender, EventArgs e) {
save_delegate();
start_collection();
}
当我调用 start_collection() 时,我得到一个 AccessViolation 异常。 我尝试通过以下方式声明委托:
public delegate void MyDelegate(
[MarshalAs(UnmanagedType.LPStr)]string address,
[MarshalAs(UnmanagedType.LPStr)]string username);
我也添加了这个声明:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void MyDelegate(...)
我没有查看您代码的每个细节,但一些常见的 p/invoke 问题会立即出现:
- 您不能将 C++ 字符串用作互操作类型。您应该在 C++ 端使用空终止字符数组。使用
std::string
. 的 - 委托人可能使用了错误的调用约定。非托管代码(可能)使用
cdecl
,因此您需要在 declaring the delegate type in C#. 时使用 - 您的 C# 委托很容易被提前收集(而您的非托管代码仍然持有对它的引用),因为没有对它的托管引用。通常,这可以通过在 C# 代码的静态变量中保留对委托的引用来解决。
c_str()
方法获取这些
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
属性