Keeping PInvoked method alive

孤街浪徒 提交于 2020-01-04 14:37:31

问题


Here's my C code:

    LIBRARY_API bool __cdecl Initialize(void (*FirstScanForDevicesDoneFunc)(void));

And here's C# PINvoke code to work with this DLL:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void FirstScanForDevicesDoneFunc();

    [DllImport("Native.dll", CallingConvention=CallingConvention.Cdecl)]
    public static extern bool Initialize(FirstScanForDevicesDoneFunc);

When I call this method like this:

    static public void Init() {
        Initialize(FirstScanForDevicesDone);
    }

    static public void FirstScanForDevicesDone() {
        //do something
    }

At the end of Init() when callback comes I get NullReferenceException. So I use Thread to keep it and everything works fine, but I'm not happy with this solution:

    static public bool Working = true;

    static public void Init() {
        new Thread(() =>
        {
            Thread.CurrentThread.IsBackground = true;

            Initialize(FirstScanForDevicesDone);
            while (Working)
            {
                Thread.Sleep(1000);
            }
        }).Start();
    }

Is there a more sophisticated way to keep this working?


回答1:


So I use Thread to keep it and everything works fine

No, the thread doesn't actually fix the problem. It only looks like it does, a side-effect of testing the Debug build and using a debugger. It will still crash the exact same way after you ship the Release build to your customer. Worst kind of failure of course. Why it seems like the thread solves it is explained in this post.

You need to fix the real problem, you are passing a delegate object to the native code but after the Init() method completes there is no visible reference to the object anymore. The garbage collector cannot peek inside the native code to see it in use. So the next garbage collection is going to destroy the object, kaboom when the native code makes the callback after that. Do note that you normally get a strongly worded warning about that from a debugger assistant, do make sure you didn't solve the problem by shooting the messenger.

Proper fix is:

   static FirstScanForDevicesDoneFunc callbackDelegate;

   static public void Init() {
        callbackDelegate = new FirstScanForDevicesDoneFunc(FirstScanForDeviceDone);
        Initialize(callbackDelegate);
   }

The callbackDelegate variable ensures that the GC can always see a reference to the object. You set it back to null when the native code cannot make callbacks anymore. Typically never.



来源:https://stackoverflow.com/questions/25972982/keeping-pinvoked-method-alive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!