2

C#から呼び出すことができるようにラップしているCdllがあります。1つの関数は、イベントを使用して状態が変化したときに通知しますが、それに対処するためにいくつかの調査が必要でした。うまく機能しているようですが、誰かが私よりもこれについての経験があり、アドバイスを提供できるかどうか知りたいです。

この関数は、dllの.hファイルで次のように定義されています。

int NotifyStateChange(OVERLAPPED *overlapped);  
typedef int (*NOTIFY_STATE_CHANGE_PROC)(OVERLAPPED *);  

それを呼び出すサンプルCコード:

OVERLAPPED overlapped;  
overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  
fns.NotifyStateChange(&overlapped);  
WaitForSingleObject(overlapped.hEvent, INFINITE);  
// ...query for the new state or whatever...

これは私がC#でそれにアプローチした方法です:

[DllImport("myfuncs.dll")]
unsafe public static extern int NotifyStateChange(NativeOverlapped* lpOverlapped);

static private ManualResetEvent m_stateChangeEvent = new ManualResetEvent(false);

public static DeviceState WaitForStateChange()
{
    unsafe
    {
        Overlapped overlapped = new Overlapped(0, 0, 
            m_stateChangeEvent.SafeWaitHandle.DangerousGetHandle(), null);
        IOCompletionCallback callback = StateChangeCallback;
        byte[] userData = new byte[100];
        NativeOverlapped* nativeOverlapped = overlapped.Pack(callback, userData);
        NotifyStateChange(nativeOverlapped);
        m_stateChangeEvent.WaitOne();
        Overlapped.Unpack(nativeOverlapped);
        Overlapped.Free(nativeOverlapped);
    }
    return GetCurrentState();
}

[ComVisibleAttribute(true)]
unsafe static public void StateChangeCallback (
    uint errorCode, 
    uint bytesTransferred, 
    NativeOverlapped* overlapped)
{
    m_stateChangeEvent.Set();
}

私がはっきりしないことの1つは、userDataの必要性です。NotifyStateChangeはイベントを発生させるだけで、データを返しません。nullのuserDataをPack()に渡すことは問題なく機能しているように見えましたが、私が気付いていない内部でuserDataで何かが起こっているのではないかと心配しています。

これを行うためのより適切な方法があるかどうかについてのアドバイスに感謝します。

エリック

4

3 に答える 3

4

すでにイベントハンドルをブロックしているので、Overlappedクラスとコールバックを使用する必要はありません。NativeOverlapped.EventHandleメンバーをDangerousGetHandle()の戻り値に設定するだけです。DLLがイベントを設定します。

また、NotifyStateChangeの引数をポインターではなくrefとして宣言すると、安全でないキーワードを使用する必要がなくなります。

于 2009-12-30T22:21:28.090 に答える
4

ああ、まさにそれが、私がスタックオーバーフローに難しい質問をする理由です。今では明らかなようです。これが非常に単純化されたコードです...

    [DllImport("myfuncs.dll")]
    public static extern int NotifyStateChange(ref NativeOverlapped lpOverlapped);

    public static DeviceState WaitForStateChange()
    {
        ManualResetEvent stateChangeEvent = new ManualResetEvent(false);
        NativeOverlapped nativeOverlapped = new NativeOverlapped();
        nativeOverlapped.EventHandle = 
            stateChangeEvent.SafeWaitHandle.DangerousGetHandle();
        NotifyStateChange(ref nativeOverlapped);
        stateChangeEvent.WaitOne();

        return GetCurrentState();
    }

ありがとう!

エリック

于 2009-12-30T22:40:02.653 に答える
1

DLL の設計者は、単純な HANDLE を使用してイベントを通知することができたときに、何らかの理由で OVERLOAPPED 構造を使用することにしたようです。オーバーラップされたデータ (ポインターとオフセット) を使用して、DLL が I/O 操作を実行するために OVERLAPPED 構造体を使用していないように見えるため、明らかに、彼らは OVERLAPPED 構造体の他のフィールドを調べません。したがって、上記の DLL についてあまり知らなくても、使用法が正しいように見え、データに nil を、オフセットに 0 を渡すことができます。

しかし、これは単なる推測であることを覚えておいてください。可能であれば、DLL の作成者に連絡して、ポインターとオフセットを無視するという仮定が正しいかどうかを明確にするように依頼してください。

ちなみに、とにかくコールバックを取得する場合は、状態の変更を待っているスレッドをブロックしません。コールバックを使用してイベント (状態の変化) を処理するだけで、システムのスケーリングが大幅に向上します。

于 2009-12-30T21:28:53.147 に答える