获取托管类型的指针或通过句柄释放事件

时间:2018-10-26 12:27:34

标签: c# pointers events

我有一个无法真正改变的程序机制:

我有一个struct用于上下文。

我使用上下文的指针,而不是struct对象本身,因为我必须将此上下文传递给非托管代码,并且非托管代码将仅接受指针作为参数 ,以便稍后在回调时还给我。

我需要对上下文的特定功能使用任何限制性方法,以便只能同时调用一次。

这样,我将使用SemaphoreSlim,或者如果我不能使用它,那么我仍将使用ManualResetEventAutoResetEvent作为解决方法,从技术上阻止了上下文,直到非托管代码调用回调为止,这样我就可以释放锁。

因为我需要传递一个指向非托管代码的指针,所以我要使用struct来将上下文Context*转换为GCHandle.Alloc(),方法是使用GCHandle.AddrOfPinnedObject()固定由struct

问题出在我的上下文中SemaphoreSlimManual/AutoResetEventSemaphoreSlim ss1 = new SemaphoreSlim(0); SemaphoreSlim* pointerOfSemaphore = &ss1; SemaphoreSlim ss2 = *pointerOfSemaphore; ss2.Release(); 是托管类型,这使我无法获取它们,或者无法获得上下文的指针。

是否有任何解决方案来固定这些托管对象并获取它们的指针,然后将它们投射回对象?

例如:

Manual/AutoResetEvent

我知道,因为这不是值类型,所以无法通过这种方式获取其地址,但是还有其他解决方法吗?

或者,作为一种替代解决方案,我可以使用IntPtr并以WaitOne()的形式获取它们的句柄,但是我不知道如何释放其Set()事件(向{{1}发出信号})只能使用Handle

任何想法都会受到赞赏!

1 个答案:

答案 0 :(得分:0)

好吧,我发现这确实可行,但是我想知道是否有更好的解决方案:

    unsafe
    {
        AutoResetEvent myEvent = new AutoResetEvent(false);
        AutoResetEvent coEvent = new AutoResetEvent(false);

        void* eventPtr = (void*)myEvent.Handle;

        Console.WriteLine("Before release");

        ThreadPool.QueueUserWorkItem(delegate
        {
            coEvent.Handle = (IntPtr)eventPtr;

            Thread.Sleep(3000);
            coEvent.Set();
        }, null);

        Console.WriteLine("Waiting for release");

        myEvent.WaitOne();

        Console.WriteLine("Event released");
    }

(我知道我不必将IntPtr转换为void*再转换回IntPtr,我是故意这样做的,因为我以这种方式通过了它移至非托管代码。)

通过一些“未公开说明”的代码,并在here的帮助下,我对SemaphoreSlim进行了变通,并且确实可行。

    unsafe
    {
        // Create semaphore
        SemaphoreSlim mySemaphore = new SemaphoreSlim(0);

        // Get size of semaphore
        SemaphoreSlim[] mySemArr = new SemaphoreSlim[2];
        TypedReference mySemRef1 = __makeref(mySemArr[0]);
        TypedReference mySemRef2 = __makeref(mySemArr[1]);
        int mySemaphoreSize = (int)((UInt64)(*((IntPtr*)&mySemRef2)) - (UInt64)(*((IntPtr*)&mySemRef1)));

        // Get pointer of semaphore
        TypedReference mySemaphoreTypeRef = __makeref(mySemaphore);
        IntPtr mySemaphorePtr = *(IntPtr*)(&mySemaphoreTypeRef);

        Console.WriteLine("Before release");

        // Start new thread
        ThreadPool.QueueUserWorkItem(delegate
        {
            // Convert IntPtr to byte "array"
            byte* sourcePtr = (byte*)mySemaphorePtr;

            // Create dummy semaphore
            SemaphoreSlim castedSemaphore = default(SemaphoreSlim);
            TypedReference castedSemaphoreTypeRef = __makeref(castedSemaphore);
            byte* castedSemaphorePtr = (byte*)*((IntPtr*)&castedSemaphoreTypeRef);

            // Copy mySemaphore to dummy
            for (int i = 0; i < mySemaphoreSize; ++i)
                castedSemaphorePtr[i] = sourcePtr[i];

            // Wait 3 seconds
            Thread.Sleep(3000);

            // Release semaphore
            castedSemaphore.Release();
        }, null);


        // Wait for release
        Console.WriteLine("Waiting for release");
        mySemaphore.Wait();

        // Released
        Console.WriteLine("Semaphore released");
    }

简化版:

    unsafe
    {
        // Create semaphore
        SemaphoreSlim mySemaphore = new SemaphoreSlim(0);

        // Get pointer of semaphore
        TypedReference mySemaphoreTypeRef = __makeref(mySemaphore);
        byte* mySemaphorePtr = (byte*)*(IntPtr*)(&mySemaphoreTypeRef);

        Console.WriteLine("Before release");

        // Start new thread
        ThreadPool.QueueUserWorkItem(delegate
        {
            // Create dummy semaphore
            SemaphoreSlim castedSemaphore = default(SemaphoreSlim);
            TypedReference castedSemaphoreTypeRef = __makeref(castedSemaphore);
            byte* castedSemaphorePtr = (byte*)(*((IntPtr*)&castedSemaphoreTypeRef));                    

            // Copy mySemaphore to dummy
            for (int i = 0; i < sizeof(IntPtr); ++i)
                castedSemaphorePtr[i] = mySemaphorePtr[i];

            // Wait 3 seconds
            Thread.Sleep(3000);

            // Release semaphore
            castedSemaphore.Release();

        }, null);


        // Wait for release
        Console.WriteLine("Waiting for release");
        mySemaphore.Wait();

        // Released
        Console.WriteLine("Semaphore released");
    }

即使它正在工作,但我对这种解决方案并不满意。还有更好的主意吗?