2009-11-21 22 views
11

我需要在服務器上的一個Windows服務中運行一堆可插入進程,並且希望創建一個用戶界面,允許我與每個插件進行交互服務使用。創建一個用戶界面,用於監視和與正在運行的Windows服務進行交互

用戶界面和長時間運行的Windows服務之間通信最常用的方法是什麼?我正在考慮提供諸如數據庫之類的中間位置,並使用某種消息隊列向服務發出命令。你們有沒有實施過這種方法或其他一些優越的方法?你在這個過程中遇到了什麼問題?

回答

32

不要使用遠程處理!儘管它肯定會起作用,但微軟表示遠程處理是一項傳統技術,所有新的分佈式應用程序都應該使用WCF進行開發。有關更多詳細信息,請參閱here

Windows Communication Foundation(WCF)是兩個.NET進程彼此通信的推薦方式。 WCF提供了一個統一的編程模型,通過抽象與特定通信機制(例如套接字,管道等)相關的許多複雜性,大大簡化了分佈式開發。

鑑於您的具體情況,我建議製作每個Windows服務插件一個WCF服務。對於每個WCF服務(即插件),定義它需要向您的UI展示的界面。該接口只是一個C#接口,裝飾了ServiceContract屬性。此接口包含方法,每個方法都使用OperationContract屬性進行裝飾,您的UI將用於與WCF服務(插件)進行通信。這些方法可以接受和返回任何可序列化的.NET類型,或者像往常一樣,可以接受和返回自己的自定義類型。要通過WCF使用自定義類型,只需使用DataContract屬性對它們進行裝飾,並使用DataMember屬性標記要通過WCF交換的成員。

一旦你定義了你的ServiceContract接口,定義一個實現該接口的類。每個OperationContract方法都可以完成它需要做的任何事情,例如,與數據庫交互,計算某個值等。一旦完成了這一步,就可以有效地定義WCF服務。這裏有一個簡短的例子:

using System.ServiceModel; 
namespace AdditionServiceNamespace 
{ 
    [DataContract] 
    public class Complex 
    { 
     [DataMember] 
     public int real; 
     [DataMember] 
     public int imag; 
    } 
    [ServiceContract] 
    public interface IAdditionService 
    { 
     [OperationContract] 
     Complex Add(Complex c1, Complex c2); 
    } 
    public class AdditionService : IAdditionService 
    { 
     public Complex Add(Complex c1, Complex c2) 
     { 
      Complex result = new Complex(); 
      result.real = c1.real + c2.real; 
      result.imag = c1.imag + c2.imag; 
      return result; 
     } 
    } 
} 

下一步是託管此WCF服務,以便它可供您的用戶界面使用。由於您將使用Windows服務,託管WCF服務做在OnStart()回調您的Windows服務的很輕鬆了,就像這樣:

using System.ServiceModel; 
using System.ServiceProcess; 
using AdditionServiceNamespace; 
namespace WindowsServiceNamespace 
{ 
    public class WindowsService : ServiceBase 
    { 
     static void Main() 
     { 
      ServiceBase[] ServicesToRun = new ServiceBase[] 
      { new WindowsService() }; 
      ServiceBase.Run(ServicesToRun); 
     } 
     private ServiceHost _host; 
     public WindowsService() 
     { 
      InitializeComponent(); 
     } 
     protected override void OnStart(string[] args) 
     { 
      _host = new ServiceHost(typeof(AdditionService)); 
      _host.Open(); 
     } 
     protected override void OnStop() 
     { 
      try 
      { 
       if (_host.State != CommunicationState.Closed) 
       { 
        _host.Close(); 
       } 
      } 
      catch 
      { 
       // handle exception somehow...log to event viewer, for example 
      } 
     } 
    } 
} 

唯一剩下要做的就是定義一個app.config文件爲您的Windows服務配置您的WCF服務所需的某些方面。這可能看起來有些過火,但記住兩件事。首先,當您向項目添加WCF服務類時,Visual Studio會自動爲您提供一個基本的app.config文件。其次,應用程序。config文件爲您提供了對WCF服務的大量控制,而無需更改代碼。這裏是上述示例的配套app.config文件:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <system.serviceModel> 
     <services> 
      <service name="AdditionServiceNamespace.MyAdditionService" 
        behaviorConfiguration="default"> 
       <endpoint name="AdditionService" 
        address="net.pipe://localhost/AdditionService" 
        binding="netNamedPipeBinding" 
        contract="AdditionServiceNamespace.IAdditionService" /> 
       <endpoint address="net.pipe://localhost/AdditionService/MEX" 
        binding="mexNamedPipeBinding" 
        contract="IMetadataExchange" /> 
      </service> 
     </services> 
     <behaviors> 
      <serviceBehaviors> 
       <behavior name="default> 
        <serviceMetadata /> 
       </behavior> 
      </serviceBehaviors> 
     </behaviors> 
    </system.serviceModel> 
</configuration> 

請注意,AdditionService WCF服務有兩個端點。元數據交換端點用於由客戶端生成代碼,因此現在忽略它。第一個端點配置爲使用NetNamedPipeBinding。這是綁定,如果您的UI和Windows服務將在同一臺計算機上運行(請參閱here瞭解如何選擇要使用的合適綁定的流程圖)。但是,如果您的UI和Windows服務將在不同的計算機上運行,​​則無法使用此綁定。在這種情況下,您可以使用NetTcpBinding作爲替代品。爲了替代NetTcpBinding的爲NetNamedPipeBinding,您只需將需要更改地址和端點的結合,就像這樣:

<endpoint name="AdditionService" 
      address="net.tcp://<machine hostname here>/AdditionService" 
      binding="netTcpBinding" 
      contract="AdditionServiceNamespace.IAdditionService" /> 

,不需要更改代碼!進行更改,重新啓動服務,並且WCF服務現在可用於遠程計算機。如果您願意的話,甚至可以允許多個端點用於相同的WCF服務。關鍵是,app.config文件提供了巨大的靈活性,無需更改代碼。

就是這樣!現在,您的Windows服務中託管了WCF服務供您的UI使用。

那麼UI方面,即客戶端如何工作?

這是WCF的真正實力發揮作用的地方。在開始使用WCF時,最簡單的事情就是利用Visual Studio的代碼生成功能。確保您的Windows服務(託管AdditionService的服務)正在運行。在您的UI項目中,右鍵單擊解決方案資源管理器中的項目,然後選擇添加服務參考...菜單選項。在地址框中輸入net.pipe://localhost/AdditionService,然後點擊轉到按鈕。您應該在服務列表中看到AdditionService顯示。在命名空間框中,鍵入AdditionService並單擊確定按鈕。

執行這些步驟將生成一個客戶端代理和一個正確定義的添加到您的UI項目中的app.config文件。此客戶端代理成爲您的客戶端AdditionService API,並且你使用這樣的:

using TestConsoleApp.AdditionService; 
namespace TestConsoleApp 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      AdditionServiceClient client = new AdditionServiceClient(); 
      Complex c1 = new Complex(), c2 = new Complex(); 
      c1.real = 3; c1.imag = 5; 
      c2.real = 1; c2.imag = 7; 
      Complex result = client.Add(c1, c2); 
     } 
    } 
} 

注意多麼簡單,這是。基本上,客戶端代理AdditionServiceClient被實例化。然後創建兩個Complex對象。最後,調用客戶端代理上的Add()方法,並返回Complex結果。

幕後發生的事情是,客戶端代理的Add()方法實際上是將兩個Complex對象傳遞給Windows服務中託管的AdditionService WCF服務。 AdditionService執行加法,然後返回結果。所有這些都發生在命名管道上,但是注意到根本沒有命名管道特定的代碼! WCF在由IAdditionService接口定義的編程模型背後抽象出所有這些複雜性。

我知道這是很多需要消化的信息,但我希望顯而易見的是WCF的功能強大且易於使用。當然,這個例子只會碰到WCF中可用的所有內容的一小部分。儘管如此,WCF應該是您用來在UI和Windows服務之間進行通信的機制。欲瞭解更多信息,我強烈建議Juval Lowy的書Programming WCF Services WCF的所有東西。您還可以訪問他的網站IDesign.net,獲取免費的WCF代碼示例。有關WCF的更多介紹,請在dnrTV上觀看free video。它涵蓋了WCF的目的,並通過一些易於遵循的示例來演示WCF編程。

+0

感謝您的詳細回覆!這就是爲什麼我喜歡這個網站:D – 2009-11-22 08:25:54

+0

非常感謝你。研究Java應用程序的「Windows服務包裝器/適配器」時發現此問題;正在考慮購買昂貴的現成包裝(適用於任何Java應用程序).​​..閱讀這個奇妙的信息反饋,最好的道路是明確的:除塵Visual Studio和C護目鏡:-) – 2015-12-26 16:55:53

0

最好的辦法是在IPC通道上使用.NET Remoting。

雖然設置起來似乎很複雜,但第二次相當容易。

我建議你先玩幾個樣本,然後將可遠程對象從一個應用程序暴露給另一個應用程序。

我之前沒有使用消息隊列,所以我不能對此發表評論。

+0

遠程處理已被有效棄用 - 自託管的WCF服務將成爲當代等同物,配置可能大致相同。 – Murph 2009-11-21 12:17:38

+0

@Murph:WTF?遠程處理如何被棄用,以支持WCF?你在哪裏讀過這個胡扯? – leppie 2009-11-21 12:59:32

+1

遠程處理不再是**推薦的方法; WCF是。 http://msdn.microsoft.com/en-us/library/kwdt6w2k(VS.85).aspx – 2009-11-22 02:43:06

相關問題