使用参数的 C# 委托回调会导致从 DLL 调用时C++访问冲突

C# delegate callback with params cause AccessViolation when calling from C++ DLL

本文关键字:DLL 调用 访问冲突 C++ 参数 回调      更新时间:2023-10-16

我有一个非托管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(( 时,我收到访问违规异常。我尝试通过以下方式声明委托:

public delegate void MyDelegate(
[MarshalAs(UnmanagedType.LPStr)]string address, 
[MarshalAs(UnmanagedType.LPStr)]string username);

我还添加了以下声明:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void MyDelegate(...)

我没有查看您代码的每个细节,但一些常见的 p/incall 问题会立即出现:

  1. 不能将C++字符串用作互操作类型。您应该在C++端使用以空结尾的字符数组。使用c_str()方法获取这些 std::string .
  2. 委托可能具有错误的调用约定。非托管代码(可能(使用 cdecl,因此在 C# 中声明委托类型时需要使用 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 属性。
  3. 您的 C# 委托很容易被提前收集(而您的非托管代码仍包含对它的引用(,因为没有对它的托管引用。通常,可以通过在 C# 代码中的静态变量中保留对委托的引用来解决此问题。