C# GC 可以移动静态 class 方法吗?

Can C# GC move static class method?

我知道,GC 可以移动对象,所以如果您想暂时避免这种情况,可以使用

GCHandle.Alloc(..., GCHandleType.Pinned)

我想将一个C#函数传递给C++ DLL,它会被调用多次。 我看到 关于如何将 C# 函数传递给 C++ DLL 的回答,我的方法是:

C++:

#include <string>

typedef void(*MyFunction)(const char*);

void foo(MyFunction func) {
    for (int i = 0; i < 1000; i++) {
        std::string s = std::to_string(i);
        func(s.c_str());
    }
}

C#:

class Program {
    delegate void MyFunction([MarshalAs(UnmanagedType.LPStr)] string s);

    [DllImport(..., CallingConvention = CallingConvention.Cdecl)]
    static extern foo(MyFunction function);

    static void Baz(string s) {
         Console.WriteLine(s);
    }

    private static MyFunction delegateInstance;

    static void Main(string[] args) {
        delegateInstance = Baz;
        foo(delegateInstance);
    }
}

如果为 x64 构建,此程序运行良好,但如果为 x86 构建,则静默死亡。我认为 GC 在某个时候移动了我的 Baz 函数,而 C++ DLL 正在尝试调用它。

如果是,我该如何避免?

编辑

看起来委托函数必须用 UnmanagedFunctionPointer 声明才能看起来像

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void MyFunction([MarshalAs(UnmanagedType.LPStr)] string s);

现在它工作正常,但有谁知道,为什么有必要并且在 x64 上工作?

x64 上只有一种调用约定(不是 cdecl)。您的 CallingConvention.Cdecl 仅适用于 x86(存在多个有效调用约定)。因此,在 x64 上指定的“错误”调用约定下一切正常也就不足为奇了,因为只有一种可能的调用约定,因此您的 CallingConvention 被忽略了。

但是切换到 x86 并突然指定错误的调用约定很重要。

另请注意,调用约定错误会导致您的应用立即崩溃。 GC 问题往往会在几秒钟后出现——事情会工作一段时间,然后停止工作。应该很容易区分与 GC 相关的问题(工作一段时间,然后停止)和其他类型的 p/invoke 问题(导致立即崩溃)