C#:消息隊列應用程序
2024-07-21 02:19:55
供稿:網友
摘要:本文概述一種用于處理若干消息隊列的 windows 服務解決方案,重點介紹 .net 框架和 c# 應用程序。
下載 csharpmessageservice.exe 示例文件 (86 kb)
目錄
簡介
.net 框架應用程序
應用程序結構
服務類
檢測設備
安裝
總結
參考資料
簡介
microsoft 近期推出了一種用于生成集成應用程序的新平臺——microsoft .net 框架。.net 框架允許開發人員使用任何編程語言迅速生成和部署 web 服務和應用程序。microsoft intermediate language (msil) 和實時 (jit) 編譯器使這種不依賴語言的框架得以實現。
與 .net 框架同時面世的還有一種新的編程語言 c#(讀作“c sharp”)。c# 是一種簡單、新穎、面向對象和類型安全的編程語言。利用 .net 框架和 c#(除 microsoft® visual basic® 和 managed c++ 之外),用戶可以編寫功能強大的 microsoft windows® 和 web 應用程序及服務。本文提供了這樣的一個解決方案,它的重點是 .net 框架和 c# 而不是編程語言。c# 語言的介紹可以在“ c# 簡介和概述(英文)”找到。
近期的文章“msmq:可伸縮、高可用性的負載平衡解決方案(英文)”介紹了一種解決方案,用于高可用性消息隊列 (msmq) 的可伸縮負載平衡解決方案體系結構。此解決方案中涉及了一種將 windows 服務用作智能消息路由器的開發方案。這樣的解決方案以前只有 microsoft visual c++® 程序員才能實現,而 .net 框架的出現改變了這種情況。從下面的解決方案中,您可以看到這一點。
.net 框架應用程序
這里介紹的解決方案是一種用來處理若干消息隊列的 windows 服務;其中每個隊列都是由多個線程進行處理(接收和處理消息)。處理程序使用循環法技術或應用程序特定值(消息 appspecific 屬性)從目的隊列列表中路由消息,并使用消息屬性來調用組件方法。(示例進程也屬于這種情況。)在后一種情況下,組件的要求是它能夠實現給定的接口 iwebmessage。要處理錯誤,應用程序需要將不能處理的消息發送到錯誤隊列中。
消息應用程序的結構與以前的活動模板庫 (atl) 應用程序相似,它們之間的主要不同在于用于管理服務的代碼的封裝和 .net 框架組件的使用。要創建 windows 服務,.net 框架用戶僅僅需要創建一個從 servicebase(來自 system.servicecontrol 程序集)繼承的類。這毫不奇怪,因為 .net 框架是面向對象的。
應用程序結構
應用程序中主要的類是 servicecontrol,它是從 servicebase 繼承的。因而,它必須實現 onstart 和 onstop 方法,以及可選的 onpause 和 oncontinue 方法。事實上,類是在靜態方法 main 內構造的:
using system;
using system.serviceprocess;
public class servicecontrol: servicebase
{
// 創建服務對象的主入口點
public static void main()
{
servicebase.run(new servicecontrol());
}
// 定義服務參數的構造對象
public servicecontrol()
{
canpauseandcontinue = true;
servicename = "msdnmessageservice";
autolog = false;
}
protected override void onstart(string[] args) {...}
protected override void onstop() {...}
protected override void onpause() {...}
protected override void oncontinue() {...}
}
servicecontrol 類創建一系列 cworker 對象,即,為需要處理的每個消息隊列創建 cworker 類的一個實例。根據定義中處理隊列所需的線程數目,cworker 類依次創建了一系列的 cworkerthread 對象。cworkerthread 類創建的一個處理線程將執行實際的服務工作。
使用 cworker 和 cworkerthread 類的主要目的是確認服務控件 start、stop、pause 和 continue 命令。因為這些進程必須是無阻塞的,命令操作最終將在后臺處理線程上執行。
cworkerthread 是一個抽象類,被 cworkerthreadappspecific 、cworkerthreadroundrobin 和 cworkerthreadassembly 繼承。這些類以不同的方式處理消息。前兩個類通過給另一隊列發送消息來處理消息(其不同之處在于確定接收隊列路徑的方式),最后一個類則使用消息屬性來調用組件方法。
.net 框架內部的錯誤處理是以基類 exception 為基礎的。當系統引發或捕獲錯誤時,這些錯誤必須是從 exception 中導出的類。cworkerthreadexception 類就是這樣一種實現,它通過附加額外屬性(用于定義服務是否應繼續運行)來擴展基類。
最后,應用程序包含兩種結構。這些值類型定義了輔助進程或線程的運行時參數,以簡化 cworker 和 cworkerthread 對象的結構。使用值類型結構(而不是引用類型類)能夠確保這些運行時參數維護的是數值(而不是引用)。
iwebmessage 接口
cworkerthread 的實現之一是一個調用組件方法的類。這個名為 cworkerthreadassembly 的類使用 iwebmessage 接口來定義服務和組件之間的約定。
與當前版本的 microsoft visual studio® 不同,c# 接口可以在任何語言中顯式定義,而不需要創建和編譯 idl 文件。c# iwebmessage 接口的定義如下:
public interface iwebmessage
{
webmessagereturn process(string smessagelabel, string smessagebody, int iappspecific);
void release();
}
atl 代碼中的 process 方法是為處理消息而指定的。process 方法的返回代碼定義為枚舉類型 webmessagereturn:
public enum webmessagereturn
{
returngood,
returnbad,
returnabort
}
枚舉的定義如下:good 表示繼續處理,bad 表示將消息寫入錯誤隊列,abort 表示終止處理。release 方法為服務提供了輕松清除類實例的途徑。因為僅在垃圾回收的過程中才調用類實例的析構函數,所以確保所有占用昂貴資源(例如數據庫連接)的類都有一個能夠在析構之前被調用的方法,用來釋放這些資源,這是一種非常好的構思。
名稱空間
在這里先簡單介紹一下名稱空間。名稱空間允許在內部和外部表示中將應用程序組織成為邏輯元素。服務內的所有代碼都包含在 msdnmessageservice.service 名稱空間內。盡管服務代碼包含在若干文件中,但是由于它們包含在同一名稱空間中,因此用戶不需要引用其他文件。
由于 iwebmessage 接口包含在 msdnmessageservice.interface 名稱空間中,因此使用此接口的線程類具有一個接口名稱空間。
服務類
應用程序的目的是監視和處理消息隊列,每一隊列在收到消息時都執行不同的進程。應用程序是作為 windows 服務來實現的。
servicebase 類
如前所述,服務的基本結構是從 servicebase 繼承的類。重要的方法包括 onstart、onstop、onpause 和 oncontinue,每一個替代方法都與一個服務控制操作直接對應。onstart 方法的目的是創建 cworker 對象,而 cworker 類又創建 cworkerthread 對象,然后在該對象中創建執行服務工作的線程。
服務的運行時配置(以及 cworker 和 cworkerthread 對象的屬性)是在基于 xml 的配置文件中維護的。它的名稱與創建的 .exe 文件相同,但帶有一個 .cfg 后綴。配置示例如下:
<?xml version="1.0"?>
<configuration>
<processlist>
<processdefinition
processname="worker1"
processdesc="message worker with 2 threads"
processtype="appspecific"
processthreads="2"
inputqueue="./private$/test_load1"
errorqueue="./private$/test_error">
<outputlist>
<outputdefinition outputname="./private$/test_out11" />
<outputdefinition outputname="./private$/test_out12" />
</outputlist>
</processdefinition>
<processdefinition
processname="worker2"
processdesc="assembly worker with 1 thread"
processtype="assembly"
processthreads="1"
inputqueue="./private$/test_load2"
errorqueue="./private$/test_error">
<outputlist>
<outputdefinition outputname="c:/msdnmessageservice/messageexample.dll" />
<outputdefinition outputname="msdnmessageservice.messagesample.exampleclass"/>
</outputlist>
</processdefinition>
</processlist>
</configuration>
對此信息的訪問通過來自 system.configuration 程序集的 configmanager 類來管理。靜態 get 方法返回信息的集合,這些集合將被枚舉以獲得單個屬性。這些屬性集的設置決定了輔助對象的運行時特征。除了這一配置文件,您還應該創建定義 xml 文件結構的圖元文件,并在其中引用位于服務器 machine.cfg 配置文件中的圖元文件:
<?xml version ="1.0"?>
<metadata xmlns="x-schema:catmeta.xms">
<databasemeta internalname="messageservice">
<serverwiring interceptor="core_xmlinterceptor"/>
<collection
internalname="process" publicname="processlist"
publicrowname="processdefinition"
schemageneratorflags="emitxmlschema">
<property internalname="processname" type="string" metaflags="primarykey" />
<property internalname="processdesc" type="string" />
<property internalname="processtype" type="int32" defaultvalue="roundrobin" >
<enum internalname="roundrobin" value="0"/>
<enum internalname="appspecific" value="1"/>
<enum internalname="assembly" value="2"/>
</property>
<property internalname="processthreads" type="int32" defaultvalue="1" />
<property internalname="inputqueue" type="string" />
<property internalname="errorqueue" type="string" />
<property internalname="outputname" type="string" />
<querymeta internalname="all" metaflags="all" />
<querymeta internalname="querybyfile" cellname="__file" operator="equal" />
</collection>
<collection
internalname="output" publicname="outputlist"
publicrowname="outputdefinition"
schemageneratorflags="emitxmlschema">
<property internalname="processname" type="string" metaflags="primarykey" />
<property internalname="outputname" type="string" metaflags="primarykey" />
<querymeta internalname="all" metaflags="all" />
<querymeta internalname="querybyfile" cellname="__file" operator="equal" />
</collection>
</databasemeta>
<relationmeta
primarytable="process" primarycolumns="processname"
foreigntable="output" foreigncolumns="processname"
metaflags="usecontainment"/>
</metadata>
由于 service 類必須維護一個已創建輔助對象的列表,因此使用了 hashtable 集合,用于保持類型對象的名稱/數值對列表。hashtable 不僅支持枚舉,還允許通過關鍵字來查詢值。在應用程序中,xml 進程名稱是唯一的關鍵字:
private hashtable htworkers = new hashtable();
iconfigcollection cworkers = configmanager.get("processlist", new appdomainselector());
foreach (iconfigitem ciworker in cworkers)
{
workerformatter sfworker = new workerformatter();
sfworker.processname = (string)ciworker["processname"];
sfworker.processdesc = (string)ciworker["processdesc"];
sfworker.numberthreads = (int)ciworker["processthreads"];
sfworker.inputqueue = (string)ciworker["inputqueue"];
sfworker.errorqueue = (string)ciworker["errorqueue"];
// 計算并定義進程類型
switch ((int)ciworker["processtype"])
{
case 0:
sfworker.processtype = workerformatter.sfprocesstype.processroundrobin;
break;
case 1:
sfworker.processtype = workerformatter.sfprocesstype.processappspecific;
break;
case 2:
sfworker.processtype = workerformatter.sfprocesstype.processassembly;
break;
default:
throw new exception("unknown processing type");
}
// 執行更多的工作以讀取輸出信息
string sprocessname = (string)ciworker["processname"];
if (htworkers.containskey(sprocessname))
throw new argumentexception("process name must be unique: " + sprocessname);
htworkers.add(sprocessname, new cworker(sfworker));
}
在這段代碼中沒有包含的主要信息是輸出數據的獲取。每一個進程定義中都有一組相應的輸出定義項。該信息是通過如下的簡單查詢讀取的:
string squery = "select * from outputlist where processname=" +
sfworker.processname + " and selector=appdomain://";
configquery qquery = new configquery(squery);
iconfigcollection coutputs = configmanager.get("outputlist", qquery);
int isize = coutputs.count, iloop = 0;
sfworker.outputname = new string[isize];
foreach (iconfigitem cioutput in coutputs)
sfworker.outputname[iloop++] = (string)cioutput["outputname"];
cworkerthread 和 cworker 類都有相應的服務控制方法,根據服務控制操作進行調用。由于 hashtable 中引用了每一個 cworker 對象,因此需要枚舉 hashtable 的內容,以調用適當的服務控制方法:
foreach (cworker cworker in htworkers.values)
cworker.start();
類似地,實現的 onpause、oncontinue 和 onstop 方法是通過調用 cworker 對象上的相應方法來執行操作的。
cworker 類
cworker 類的主要功能是創建和管理 cworkerthread 對象。start、stop、pause 和 continue 方法調用相應的 cworkerthread 方法。實際的 cworkerthread 對象是在start 方法中創建的。與使用 hashtable 管理輔助對象引用的 service 類相似,cworker 使用 arraylist(簡單的動態數組)來維護線程對象的列表。
在這個數組內部,cworker 類創建了 cworkerthread 類的一個實現版本。cworkerthread 類(將在下面討論)是一個必須繼承的抽象類。導出類定義了消息的處理方式:
athreads = new arraylist();
for (int idx=0; idx<sfworker.numberthreads; idx++)
{
workerthreadformatter wfthread = new workerthreadformatter();
wfthread.processname = sfworker.processname;
wfthread.processdesc = sfworker.processdesc;
wfthread.threadnumber = idx;
wfthread.inputqueue = sfworker.inputqueue;
wfthread.errorqueue = sfworker.errorqueue;
wfthread.outputname = sfworker.outputname;
// 定義輔助類型,并將其插入輔助線程結構
cworkerthread wtbase;
switch (sfworker.processtype)
{
case workerformatter.sfprocesstype.processroundrobin:
wtbase = new cworkerthreadroundrobin(this, wfthread);
break;
case workerformatter.sfprocesstype.processappspecific:
wtbase = new cworkerthreadappspecific(this, wfthread);
break;
case workerformatter.sfprocesstype.processassembly:
wtbase = new cworkerthreadassembly(this, wfthread);
break;
default:
throw new exception("unknown processing type");
}
// 添加對數組的調用
athreads.insert(idx, wtbase);
}
一旦所有的對象都已創建,就可以通過調用每個線程對象的 start 方法來啟動它們:
foreach(cworkerthread cthread in athreads)
cthread.start();
stop、pause 和 continue 方法在 foreach 循環里執行的操作類似。stop 方法具有如下的垃圾收集操作:
gc.suppressfinalize(this);
在類析構函數中將調用 stop 方法,這樣,在沒有顯式調用 stop 方法的情況下也可以正確地終止對象。如果調用了 stop 方法,將不需要析構函數。suppressfinalize 方法能夠防止調用對象的 finalize 方法(析構函數的實際實現)。
cworkerthread 抽象類
cworkerthread 是一個由 cworkerthreadappspecifc、cworkerthreadroundrobin 和 cworkerthreadassembly 繼承的抽象類。無論如何處理消息,隊列的大部分處理是相同的,所以 cworkerthread 類提供了這一功能。這個類提供了抽象方法(必須被實際方法替代)以管理資源和處理消息。
類的工作再一次通過 start、stop、pause 和 continue 方法來實現。在 start 方法中引用了輸入和錯誤隊列。在 .net 框架中,消息由 system.messaging 名稱空間處理:
// 嘗試打開隊列,并設置默認的讀寫屬性
messagequeue mqinput = new messagequeue(sinputqueue);
mqinput.messagereadpropertyfilter.body = true;
mqinput.messagereadpropertyfilter.appspecific = true;
messagequeue mqerror = new messagequeue(serrorqueue);
// 如果使用 msmq com,則將格式化程序設置為 activex
mqinput.formatter = new activexmessageformatter();
mqerror.formatter = new activexmessageformatter();
一旦定義了消息隊列引用,即會創建一個線程用于實際的處理函數(稱為 processmessages)。在 .net 框架中,使用 system.threading 名稱空間很容易實現線程處理:
procmessage = new thread(new threadstart(processmessages));
procmessage.start();
processmessages 函數是基于 boolean 值的處理循環。當數值設為 false,處理循環將終止。因此,線程對象的 stop 方法只設置這一 boolean 值,然后關閉打開的消息隊列,并加入帶有主線程的線程:
// 加入服務線程和處理線程
brun = false;
procmessage.join();
// 關閉打開的消息隊列
mqinput.close();
mqerror.close();
pause 方法只設置一個 boolean 值,使處理線程休眠半秒鐘:
if (bpause)
thread.sleep(500);
最后,每一個 start、stop、pause 和 continue 方法將調用抽象的 onstart、onstop、onpause 和 oncontinue 方法。這些抽象方法為實現的類提供了掛鉤,以捕獲和釋放所需的資源。
processmessages 循環具有如下基本結構:
接收 message。
如果 message 具有成功的 receive,則調用抽象 processmessage 方法。
如果 receive 或 processmessage 失敗,將 message 發送至錯誤隊列中。
message minput;
try
{
// 從隊列中讀取,并等候 1 秒
minput = mqinput.receive(new timespan(0,0,0,1));
}
catch (messagequeueexception mqe)
{
// 將消息設置為 null
minput = null;
// 查看錯誤代碼,了解是否超時
if (mqe.errorcode != (-1072824293) ) //0xc00e001b
{
// 如果未超時,發出一個錯誤并記錄錯誤號
logerror("error: " + mqe.message);
throw mqe;
}
}
if (minput != null)
{
// 得到一個要處理的消息,調用處理消息抽象方法
try
{
processmessage(minput);
}
// 捕獲已知異常狀態的錯誤
catch (cworkerthreadexception ex)
{
processerror(minput, ex.terminate);
}
// 捕獲未知異常,并調用 terminate
catch
{
processerror(minput, true);
}
}
processerror 方法將錯誤的消息發送至錯誤隊列。另外,它也可能引發異常來終止線程。如果processmessage 方法引發了終止錯誤或 cworkerthreadexception 類型,它將執行此操作。
cworkerthread 導出類
任何從 cworkerthread 中繼承的類都必須提供 onstart、onstop、onpause、oncontinue 和 processmessage 方法。onstart 和 onstop 方法獲取并釋放處理資源。onpause 和 oncontinue 方法允許臨時釋放和重新獲取這些資源。processmessage 方法應該處理消息,并在出現失敗事件時引發 cworkerthreadexception 異常。
由于 cworkerthread 構造函數定義運行時參數,導出類必須調用基類構造函數:
public cworkerthreadderived(cworker v_cparent, workerthreadformatter v_wfthread)
: base (v_cparent, v_wfthread) {}
導出類提供了兩種類型的處理:將消息發送至另一隊列,或者調用組件方法。接收和發送消息的兩種實現使用了循環技術或應用程序偏移(保留在消息 appspecific 屬性中),作為使用哪一隊列的決定因素。此方案中的配置文件應該包括隊列路徑的列表。實現的 onstart 和 onstop 方法應該打開和關閉對這些隊列的引用:
iqueues = wfthread.outputname.length;
mqoutput = new messagequeue[iqueues];
for (int idx=0; idx<iqueues; idx++)
{
mqoutput[idx] = new messagequeue(wfthread.outputname[idx]);
mqoutput[idx].formatter = new activexmessageformatter();
}
在這些方案中,消息的處理很簡單:將消息發送必要的輸出隊列。在循環情況下,這個進程為:
try
{
mqoutput[inextqueue].send(v_minput);
}
catch (exception ex)
{
// 如果錯誤強制終止異常
throw new cworkerthreadexception(ex.message, true);
}
// 計算下一個隊列號
inextqueue++;
inextqueue %= iqueues;
后一種調用帶消息參數的組件的實現方法比較有趣。processmessage 方法使用 iwebmessage 接口調入一個 .net 組件。onstart 和 onstop 方法獲取和釋放此組件的引用。
此方案中的配置文件應該包含兩個項目:完整的類名和類所在文件的位置。按照 iwebmessage 接口中的定義,在組件上調用 process 方法。
要獲取對象引用,需要使用 activator.createinstance 方法。此函數需要一個程序集類型。在這里,它是從程序集文件路徑和類名中導出的。一旦獲取對象引用,它將被放入合適的接口:
private iwebmessage iwmsample;
private string sfilepath, stypename;
// 保存程序集路徑和類型名稱
sfilepath = wfthread.outputname[0];
stypename = wfthread.outputname[1];
// 獲取對必要對象的引用
assembly asmsample = assembly.loadfrom(sfilepath);
type typsample = asmsample.gettype(stypename);
object objsample = activator.createinstance(typsample);
// 定義給對象的必要接口
iwmsample = (iwebmessage)objsample;
獲取對象引用后,processmessage 方法將在 iwebmessage 接口上調用 process 方法:
webmessagereturn wbrsample;
try
{
// 定義方法調用的參數
string slabel = v_minput.label;
string sbody = (string)v_minput.body;
int iappspecific = v_minput.appspecific;
// 調用方法并捕捉返回代碼
wbrsample = iwmsample.process(slabel, sbody, iappspecific);
}
catch (invalidcastexception ex)
{
// 如果在消息內容中發生錯誤,則強制發出一個非終止異常
throw new cworkerthreadexception(ex.message, false);
}
catch (exception ex)
{
// 如果錯誤調用程序集,則強制發出終止異常
throw new cworkerthreadexception(ex.message, true);
}
// 如果沒有錯誤,則檢查對象調用的返回狀態
switch (wbrsample)
{
case webmessagereturn.returnbad:
throw new cworkerthreadexception
("unable to process message: message marked bad", false);
case webmessagereturn.returnabort:
throw new cworkerthreadexception
("unable to process message: process terminating", true);
default:
break;
}
提供的示例組件將消息正文寫入數據庫表。如果捕獲到嚴重數據庫錯誤,您可能希望終止處理過程,但是在這里,僅僅將消息標記為錯誤的消息。
由于此示例中創建的類實例可能會獲取并保留昂貴的數據庫資源,所以用 onpause 和 oncontinue 方法釋放和重新獲取對象引用。
檢測設備
就象在所有優秀的應用程序中一樣,檢測設備用于監測應用程序的狀態。.net 框架大大簡化了將事件日志、性能計數器和 windows 管理檢測設備 (wmi) 納入應用程序的過程。消息應用程序使用時間日志和性能計數器,二者都是來自 system.diagnostics 程序集。
在 servicebase 類中,您可以自動啟用事件日志。另外,servicebase eventlog 成員支持寫入應用程序事件日志:
eventlog.writeentry(smymessage, eventlogentrytype.information);
對于寫入事件日志而不是應用程序日志的應用程序,它能夠很容易地創建和獲取 eventlog 資源的引用(正如在 cworker 類中所做的一樣),并能夠使用 writeentry 方法記錄日志項:
private eventlog clog;
string ssource = servicecontrol.servicecontrolname;
string slog = "application";
// 查看源是否存在,如果不存在,則創建源
if (!eventlog.sourceexists(ssource))
eventlog.createeventsource(ssource, slog);
// 創建日志對象,并引用現在定義的源
clog = new eventlog();
clog.source = ssource;
// 在日志中寫入條目,表明創建成功
clog.writeentry("已成功創建", eventlogentrytype.information);
.net 框架大大簡化了性能計數器。對于每一個處理線程、線程導出的用戶和整個應用程序,這一消息應用程序都能提供計數器,用于跟蹤消息數量和每秒鐘處理消息的數量。要提供此功能,您需要定義性能計數器的類別,然后增加相應的計數器實例。
性能計數器的類別在服務 onstart 方法中定義。這些類別代表兩種計數器——消息總數和每秒鐘處理的消息數:
countercreationdata[] cdmessage = new countercreationdata[2];
cdmessage[0] = new countercreationdata("messages/total", "total messages processed",
performancecountertype.numberofitems64);
cdmessage[1] = new countercreationdata("messages/second", "messages processed a second",
performancecountertype.rateofchangepersecond32);
performancecountercategory.create("msdn message service", "msdn message service counters", cdmessage);
一旦定義了性能計數器類別,將創建 performancecounter 對象以訪問計數器實例功能。performancecounter 對象需要類別、計數器名稱和一個可選的實例名稱。對于輔助進程,將使用來自 xml 文件的進程名稱,代碼如下:
pcmsgtotworker = new performancecounter("msdn message service", "messages/total", sprocessname);
pcmsgsecworker = new performancecounter("msdn message service", "messages/second", sprocessname);
pcmsgtotworker.rawvalue = 0;
pcmsgsecworker.rawvalue = 0;
要增加計數器的值,僅僅需要調用適當的方法:
pcmsgtotworker.incrementby(1);
pcmsgsecworker.incrementby(1);
最后說明一點,服務終止時,安裝的性能計數器類別應該從系統中刪除:
performancecountercategory.delete("msdn message service");
由于性能計數器在 .net 框架中工作,因此需要運行一項特殊的服務。此服務 (perfcounterservice) 提供了共享內存。計數器信息將寫入共享內存,并被性能計數器系統讀取。
安裝
在結束以前,我們來簡要介紹一下安裝以及稱為 installutil.exe 的安裝工具。由于此應用程序是 windows 服務,它必須使用 installutil.exe 來安裝。因此,需要使用一個從 system.configuration.install 程序集中繼承的 installer 類:
public class serviceregister: installer
{
private serviceinstaller serviceinstaller;
private serviceprocessinstaller processinstaller;
public serviceregister()
{
// 創建服務安裝程序
serviceinstaller = new serviceinstaller();
serviceinstaller.starttype = servicestart.manual;
serviceinstaller.servicename = servicecontrol.servicecontrolname;
serviceinstaller.displayname = servicecontrol.servicecontroldesc;
installers.add(serviceinstaller);
// 創建進程安裝程序
processinstaller = new serviceprocessinstaller();
processinstaller.runundersystemaccount = true;
installers.add(processinstaller);
}
}
如此示例類所示,對于一個 windows 服務,服務和服務進程各需要一個安裝程序,以定義運行服務的帳戶。其他安裝程序允許注冊事件日志和性能計數器等資源。
總結
從這個 .net 框架應用程序示例中可以看出,以前只有 visual c++ 程序員能夠編寫的應用程序,現在使用簡單的面向對象程序即可實現。盡管我們的重點是 c#,但本文所述的內容也同樣適用于 visual basic 和 managed c++。新的 .net 框架使開發人員能夠使用任何編程語言來創建功能強大、可伸縮的 windows 應用程序和服務。
新的 .net 框架不僅簡化和擴展了編程的種種可能,還能夠輕松地將人們經常遺忘的應用程序檢測設備(例如性能監測計數器和事件日志通知)合并到應用程序中。盡管這里的應用程序沒有使用 windows 管理檢測設備 (wmi),但 .net 框架同樣也可以應用它。
參考資料
可伸縮的高可用性業務對象結構(英文)
msmq:可伸縮的高可用性負載平衡解決方案(英文)
c# 簡介和概述(英文)
c# 參考(英文)
msdn online .net 信息(英文)
關于作者
carl nolan 在北加利福尼亞的 microsoft 電子商務解決方案小組的西區工作。該小組的工作重點是使用 microsoft windows .net 平臺開發基于 internet 的解決方案。他的電子郵件地址是 [email protected]。
--------------------------------------------------------------------------------
請以 ie4.0 以上版本 800 * 600 瀏覽本站
©2001 microsoft corporation 版權所有。保留所有權利。使用規定。