27

キオスク アプリケーションのすべての外部ハードウェア I/O を処理する C# で記述された Windows サービスがあります。新しいデバイスの 1 つは、ネイティブ DLL の API を備えた USB デバイスです。適切な P/Invoke ラッパー クラスを作成しました。ただし、この API は Windows アプリケーションへの HWnd で初期化する必要があります。これは、メッセージ ポンプを使用して非同期イベントを発生させるためです。

Windows メッセージ ポンプに依存しない API を提供するようハードウェア メーカーに要求する以外に、この API に渡すことができる Windows サービスの新しいスレッドでメッセージ ポンプを手動でインスタンス化する方法はありますか? ? 実際に完全なアプリケーション クラスを作成する必要がありますか?それとも、メッセージ ポンプをカプセル化する下位レベルの .NET クラスがありますか?

4

3 に答える 3

42

ご提案いただきありがとうございます。リチャードと怠け者、コメントで提供したリンクは非常に役に立ちました。また、Application.Run でメッセージ ポンプを手動で開始するために、サービスがデスクトップとやり取りできるようにする必要もありませんでした。どうやら、Windows にメッセージ ポンプを自動的に開始させたい場合にのみ、サービスがデスクトップとやり取りできるようにする必要があるようです。

みんなの啓発のために、このサードパーティ API のメッセージ ポンプを手動で開始するために私が最終的に行ったことは次のとおりです。

internal class MessageHandler : NativeWindow
{
    public event EventHandler<MessageData> MessageReceived;

    public MessageHandler ()
    {
        CreateHandle(new CreateParams());
    }

    protected override void WndProc(ref Message msg)
    {
        // filter messages here for your purposes

        EventHandler<MessageData> handler = MessageReceived;
        if (handler != null) handler(ref msg);

        base.WndProc(ref msg);
    }
}

public class MessagePumpManager
{
    private readonly Thread messagePump;
    private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);

    public StartMessagePump()
    {
        // start message pump in its own thread
        messagePump = new Thread(RunMessagePump) {Name = "ManualMessagePump"};
        messagePump.Start();
        messagePumpRunning.WaitOne();
    }

    // Message Pump Thread
    private void RunMessagePump()
    {
        // Create control to handle windows messages
        MessageHandler messageHandler = new MessageHandler();

        // Initialize 3rd party dll 
        DLL.Init(messageHandler.Handle);

        Console.WriteLine("Message Pump Thread Started");
        messagePumpRunning.Set();
        Application.Run();
    }
}

これを機能させるには、いくつかのハードルを克服する必要がありました。1 つは、Application.Run を実行するのと同じスレッドでフォームを作成する必要があることです。また、Handle プロパティには同じスレッドからしかアクセスできないため、そのスレッドでも DLL を単純に初期化するのが最も簡単であることがわかりました。とにかく、GUIスレッドから初期化されることを期待しています。

また、私の実装では、MessagePumpManager クラスは Singleton インスタンスであるため、デバイス クラスのすべてのインスタンスに対して 1 つのメッセージ ポンプのみが実行されます。コンストラクターでスレッドを開始する場合は、必ずシングルトン インスタンスを遅延初期化してください。静的コンテキスト (private static MessagePumpManager instance = new MessagePumpManager(); など) からスレッドを開始すると、ランタイムは新しく作成されたスレッドにコンテキストを切り替えることはなく、メッセージ ポンプの開始を待機している間にデッドロックが発生します。

于 2010-03-16T19:33:54.553 に答える
2

You have to make a Form, Windows services do not interact with the desktop by default, so you have to set the service to interact with the desktop and installing it can be a bit of a pain. The Form will not be visible though. Microsoft has been deliberately making this harder and harder to do because of security issues.

于 2010-03-14T22:14:45.330 に答える
1

CreateWindowEx の呼び出しで HWND_MESSAGE パラメータによって示される、メッセージのみのウィンドウを作成するだけです。確かに、これは C コードですが、これらの構造体とP/Invoke呼び出しは C# で簡単に作成できます。

WNDCLASS w;
HWND handle;
w.hInstance = (HINSTANCE)GetModuleHandle(...); // Associate this module with the window.
w.lpfnWndProc = ... // Your windowproc
w.lpszClassName = ... // Name of your window class

RegisterClass(&w)
handle = CreateWindowEx(0, w.lpszClassName, w.lpszClassName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wc.hInstance, NULL);
于 2010-03-14T21:45:27.260 に答える