我需要在服務器上的一個Windows服務中運行一堆可插入進程,並且希望創建一個用戶界面,允許我與每個插件進行交互服務使用。創建一個用戶界面,用於監視和與正在運行的Windows服務進行交互
用戶界面和長時間運行的Windows服務之間通信最常用的方法是什麼?我正在考慮提供諸如數據庫之類的中間位置,並使用某種消息隊列向服務發出命令。你們有沒有實施過這種方法或其他一些優越的方法?你在這個過程中遇到了什麼問題?
我需要在服務器上的一個Windows服務中運行一堆可插入進程,並且希望創建一個用戶界面,允許我與每個插件進行交互服務使用。創建一個用戶界面,用於監視和與正在運行的Windows服務進行交互
用戶界面和長時間運行的Windows服務之間通信最常用的方法是什麼?我正在考慮提供諸如數據庫之類的中間位置,並使用某種消息隊列向服務發出命令。你們有沒有實施過這種方法或其他一些優越的方法?你在這個過程中遇到了什麼問題?
不要使用遠程處理!儘管它肯定會起作用,但微軟表示遠程處理是一項傳統技術,所有新的分佈式應用程序都應該使用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編程。
最好的辦法是在IPC通道上使用.NET Remoting。
雖然設置起來似乎很複雜,但第二次相當容易。
我建議你先玩幾個樣本,然後將可遠程對象從一個應用程序暴露給另一個應用程序。
我之前沒有使用消息隊列,所以我不能對此發表評論。
感謝您的詳細回覆!這就是爲什麼我喜歡這個網站:D – 2009-11-22 08:25:54
非常感謝你。研究Java應用程序的「Windows服務包裝器/適配器」時發現此問題;正在考慮購買昂貴的現成包裝(適用於任何Java應用程序)...閱讀這個奇妙的信息反饋,最好的道路是明確的:除塵Visual Studio和C護目鏡:-) – 2015-12-26 16:55:53