使PInvoked方法保持活动状态 [英] Keeping PInvoked method alive

查看:120
本文介绍了使PInvoked方法保持活动状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的C代码:

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

这是可用于此DLL的C#PINvoke代码:

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

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

当我这样调用此方法时:

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

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

Init()的结尾,当回调发生时,我得到 NullReferenceException .所以我使用Thread来保持它正常运行,但是我对这种解决方案不满意:

    static public bool Working = true;

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

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

是否有更复杂的方法来保持此功能?

解决方案

所以我使用Thread来保持它正常运行

否,该线程实际上无法解决问题.它只是像它们一样看起来,这是测试Debug版本并使用调试器的副作用.在将Release版本交付给客户之后,它仍然会以完全相同的方式崩溃.当然最糟糕的失败. 这篇文章中解释了为什么它看起来像线程解决了它的原因.

您需要解决实际的问题,您正在将委托对象传递给本机代码,但是在Init()方法完成后,就不再有对该对象的可见引用.垃圾收集器无法窥视本机代码以查看其使用情况.因此,下一个垃圾回收将在本机代码之后进行回调时破坏对象kaboom.请注意,通常情况下,调试助手会发出强烈警告,警告您,请确保未通过射击Messenger来解决问题.

正确的解决方法是:

   static FirstScanForDevicesDoneFunc callbackDelegate;

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

callbackDelegate 变量可确保GC始终可以看到对该对象的引用.当本机代码无法再进行回调时,请将其设置回null.通常从不.

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?

解决方案

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.

这篇关于使PInvoked方法保持活动状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆