【问题标题】:C# delegate callback with params cause AccessViolation when calling from C++ DLL从 C++ DLL 调用时,带有参数的 C# 委托回调导致 AccessViolation
【发布时间】:2019-10-27 12:16:39
【问题描述】:

我有一个非托管 C++ DLL 和一个 .net 应用程序,它们使用 P/Invoke 进行通信。 我需要从 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(...)

【问题讨论】:

  • C++ 字符串不能在 pinvoke 中使用。使用以空字符结尾的字符数组。因为您需要保持代表活着,所以它不会被收集。通常的方法是在静态变量中保存引用。
  • 非常感谢@DavidHeffernan。我解决了这个问题。如果您将评论作为答案发布,我将选择它作为正确答案。

标签: c# c++ .net pinvoke unmanaged


【解决方案1】:

我没有查看您代码的每一个细节,但一些常见的 p/invoke 问题马上就出来了:

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

【讨论】:

  • 代表的 CallingConvention 错误。
  • 像魅力一样工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-19
  • 1970-01-01
相关资源
最近更新 更多