Home > OS >  Can C# GC move static class method?
Can C# GC move static class method?

Time:10-18

I know, that GC can move objects, so if you want to avoid this for some time, you use

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

I want to pass a C# function to C DLL, which will be called several times. I saw this answer about how to pass C# function to C DLL and my approach is:

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);
    }
}

This program works fine if built for x64, but silently dies if built for x86. I think GC moves my Baz function at some point and C DLL is trying to call it.

If it is, how can I avoid this?

Edit

It looks like the delegate functions must be declared with UnmanagedFunctionPointer to look like

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

Now it works fine, but does anyone knows, why is it necessary and did work on x64?

CodePudding user response:

There is only one calling convention on x64 (and it isn't cdecl). Your CallingConvention.Cdecl applies to x86 only (where there are multiple valid calling conventions). It's therefore unsurprising that things worked fine with the "wrong" calling convention specified on x64, as there's only one possible calling convention and so your CallingConvention was ignored.

Switch to x86 however and suddenly specifying the wrong calling convention matters.

Also note, having the wrong calling convention would cause your app to crash immediately. GC problems tend to kick in after a number of seconds -- things work for a bit, and then stop working. It should be fairly easy to distinguish problems related to the GC (which work for a bit, and then stop) from other sorts of p/invoke problems (which cause an immediate crash)

  • Related