2010-03-14 150 views
24

我有一個用C#編寫的Windows服務,它處理我們的所有外部硬件I/O服務亭應用程序。我們的新設備之一是在本機DLL中附帶API的USB設備。我有一個適當的P/Invoke包裝類創建。但是,此API必須使用HWnd初始化爲Windows應用程序,因爲它使用消息泵來引發異步事件。.NET中的消息泵Windows服務

除了向硬件製造商要求我們提供不依賴Windows消息泵的API之外,是否有任何方法可以在我的Windows服務中的新線程中手動實例化消息泵,我可以傳入此API?我是否真的必須創建一個完整的Application類,或者是否有一個封裝消息泵的較低級別的.NET類?

+4

看看這個主題,它可能會證明有幫助:http://connect.microsoft.com/VisualStudio/feedback/details/241133/detecting-a-wm-timechange-event-in-a-net-windows-service – overslacked 2010-03-14 22:34:12

+0

@overslacked,它的確如此。它說明了如何做到這一點。 – 2010-03-15 05:36:20

+0

@overslacked我知道這是一個老問題,所以毫不奇怪,MS Connect鏈接不再起作用。但是,由於該鏈接的內容似乎對這裏的討論至關重要,所以我想知道是否有新的鏈接可用。很抱歉引起麻煩! – Sabuncu 2015-09-03 17:57:11

回答

38

謝謝大家的建議。理查德& overslacked,你在評論中提供的鏈接是非常有幫助的。此外,我不必允許該服務與桌面進行交互,以便通過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(); 
    } 
} 

我必須克服一些障礙得到這個工作。一個是你需要確定在執行Application.Run的同一個線程上創建表單。您也只能從同一個線程訪問Handle屬性,所以我發現在該線程上簡單初始化DLL也是最簡單的。據我所知,無論如何,它期望從GUI線程初始化。

另外,在我的實現中,MessagePumpManager類是一個Singleton實例,因此只有一個消息泵運行於我的設備類的所有實例。如果你在你的構造函數中啓動線程,確保你真的懶 - 初始化你的單例實例。如果從靜態上下文啓動線程(例如,私有靜態MessagePumpManager實例= new MessagePumpManager();),運行時將永遠不會切換到新創建的線程,並且在等待消息泵啓動時會死鎖。

+3

感謝您花時間爲您的問題發佈解決方案 - 您只是節省了我的工作時間。 – Basic 2010-11-18 18:29:58

+0

謝謝...似乎工作,但在我的情況下,因爲我的第三個DLL發佈.net事件和這些事件在另一個線程中引發,事情不按預期工作... – JobaDiniz 2015-12-01 12:25:22

1

只需創建一個僅消息窗口,由調用CreateWindowEx中的HWND_MESSAGE參數表示。當然,這是C代碼,但是你可以很容易地在C#中調用這些結構和調用P/Invoke

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); 
+1

但是,這是從服務工作? – 2010-03-14 21:56:11

+0

當然不需要使用P/Invoke調用來創建窗口。 – 2010-03-14 22:18:05

+0

我不認爲你可以在沒有調用CreateWindowEx的情況下創建一個HWND_MESSAGE窗口。 – jaws 2010-03-14 22:22:34

2

你必須做一個表格,Windows服務不與桌面默認情況下互動,所以你必須設置爲與桌面交互服務和安裝它可以是一個有點疼痛。表單雖然不會顯示。由於安全問題,微軟一直在故意讓這件事變得越來越難。

+0

有趣的建議。但是,允許服務與桌面交互不是一種選擇。我當然不希望任何窗口可見。 API本身沒有可視化組件,只是他們決定使用消息泵來通知異步事件的調用者。 是否允許服務與桌面交互以運行消息泵? – Pickles 2010-03-15 07:40:37

+0

該窗口不可見。是的,這是一項要求。 – 2010-03-15 13:17:28

+0

請參閱https://connect.microsoft.com/VisualStudio/feedback/details/241133/detecting-a-wm-timechange-event-in-a-net-windows-service?wa=wsignin1.0它有一步一步來說明。 – 2010-03-15 13:18:05