有時(shí)候,我們需要自己寫(xiě)的程序在沒(méi)有用戶(hù)登陸的情況下,只要Windows系統啟動(dòng)就運行,那我們可以把我們的程序寫(xiě)成一個(gè)Windows服務(wù)。
服務(wù)是能夠為各種用戶(hù)(包括本地用戶(hù)和遠程用戶(hù))所用的,擁有用戶(hù)授權級進(jìn)行管理的能力,并且不論用戶(hù)是否物理的與正在運行該應用程序的計算機相連都能正常執行。
下面,我將用一個(gè)簡(jiǎn)單的例子說(shuō)明如何用ATL來(lái)編寫(xiě)Windows服務(wù)程序。
首先,我們新建一個(gè)Project。如圖一所示,選擇 "ATL COM AppWizard",工程名為:ServiceDemo。
圖一
點(diǎn)擊 "OK ", 出現圖二,選擇Service [EXE]。點(diǎn)擊 Finish。
圖二
完成以上的步驟,一個(gè)"什么也不做"的服務(wù)就完成了!編譯… 打開(kāi)"控制面板"->"管理工具"
->"服務(wù)",嗯?我們寫(xiě)的服務(wù)怎么沒(méi)有在服務(wù)管理器(service control manager ,簡(jiǎn)稱(chēng)(SCM))里面列出來(lái)呢?呵呵,被我騙了?不要著(zhù)急,我們還需要做一些工作。
首先先大概介紹一下向導為我們生成的代碼:
程序的進(jìn)入點(diǎn)是全局函數_tWinMain, 仔細看一下這個(gè)函數,我們會(huì )發(fā)現當我們運行程序時(shí),可以加上參數,例如: ServiceDemo /RegServer 或者 ServiceDemo -RegServer,這個(gè)是用來(lái)本地服務(wù)器注冊(Register as Local S Register as Service erver); ServiceDemo / Service 或者 ServiceDemo -Service,這個(gè)是服務(wù)的注冊(Register as Service);ServiceDemo /UnRegServer 或者 ServiceDemo -UnRegServer ,這個(gè)是服務(wù)的刪除。所以,當我們寫(xiě)好了服務(wù)程序,只要運行的時(shí)候加上參數 Service ,這個(gè)時(shí)候在SCM中就會(huì )看到我們的服務(wù)了??梢栽囈幌略赟CM中對這個(gè)什么也不做的服務(wù)"啟動(dòng)","停止",改變一下它的啟動(dòng)方式。
每次編碼后測試都要在命令行中加參數運行服務(wù)才可以在SCM中列出來(lái)是不是很麻煩呢?我再介紹一個(gè)偷懶的方法,選擇VC IDE的菜單Project -> Setting, 再選擇Custom Build 面板,如圖三:
圖三
在"$(TargetPath)" /RegServer的下面加上:"$(TargetPath)" /Service,這樣當我們每次編碼后編譯程序,就不用再在命令行中去加參數執行我們的服務(wù)程序完成服務(wù)的注冊了。
繼續介紹向導生成的代碼:向導為我們建立了一個(gè)類(lèi):CServiceModule,全局變量_Module就是這個(gè)類(lèi)的實(shí)例。
Init():這個(gè)函數用于完成一些初始化工作;
Run():這個(gè)函數就是服務(wù)開(kāi)始運行后的內容,我們接下來(lái)要修改的內容也就是從這里入手。
Install():
看一下Install()的這一部分:
SC_HANDLE hService = ::CreateService(hSCM,m_szServiceName,m_szServiceName,SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,szFilePath,NULL,NULL,_T("RPCSS\0"),NULL,NULL);函數的原型如下:
SC_HANDLE CreateService(SC_HANDLE hSCManager, // handle to SCM databaseLPCTSTR lpServiceName, // name of service to startLPCTSTR lpDisplayName, // display nameDWORD dwDesiredAccess, // type of access to serviceDWORD dwServiceType, // type of serviceDWORD dwStartType, // when to start serviceDWORD dwErrorControl, // severity of service failureLPCTSTR lpBinaryPathName, // name of binary fileLPCTSTR lpLoadOrderGroup, // name of load ordering groupLPDWORD lpdwTagId, // tag identifierLPCTSTR lpDependencies, // array of dependency namesLPCTSTR lpServiceStartName, // account nameLPCTSTR lpPassword // account password);
具體的細節可以查一下MSDN,我只說(shuō)一下第六個(gè)和第十一個(gè)參數。第六個(gè)參數是服務(wù)的啟動(dòng)類(lèi)型。
SERVICE_DEMAND_START是手動(dòng)啟動(dòng),SERVICE_AUTO_START是自動(dòng)啟動(dòng)。第十一個(gè)參數是服務(wù)的依存關(guān)系,比如說(shuō)服務(wù)的啟動(dòng)想要依存SQL Server的啟動(dòng),那我們可以把這個(gè)參數寫(xiě)成:
_T("MSSQLSERVER\0");如果我們寫(xiě)的服務(wù)不依存于其他的任何服務(wù),那我們就將此參數設置為NULL就可以了。
接下來(lái),我們?yōu)樯厦娴?什么也不做"的服務(wù)添加一個(gè)簡(jiǎn)單的功能:做數字的累加,并且把結果寫(xiě)到系統的"應用程序日志"中去。
首先,我們在類(lèi)CServiceModule中添加一個(gè)成員變量:int n; 在Init()中對n進(jìn)行初始化:
n = 0;
然后在類(lèi)CServiceModule中添加一個(gè)成員函數Adder():
void CServiceModule::Adder(){n ++;CString str;str.Format("%i",n);LogEvent(str);}編譯…出錯了。??,提示 CString 沒(méi)有定義,難道在A(yíng)TL中無(wú)法用 MFC 嗎?讓我們看看設置:菜單Project->Setting ,General面板,默認的設置是:Use MFC in a Static Library。那為什么不可以用MFC中的類(lèi)呢?原來(lái)是頭文件沒(méi)有包含,這個(gè)不知道算不算 VC 的一個(gè) Bug : ,設置中默認是用MFC,可是卻沒(méi)有包含相應的頭文件。那我們就自己加上好了。在StdAfx.h中加上:#include
MSG msg;while (GetMessage(&msg, 0, 0, 0))DispatchMessage(&msg);
的前面加上代碼:
SetTimer(NULL,1,2000,(TIMERPROC)OnTimerProc);
注意一定要加在前面,因為要是加到while循環(huán)的下面,就沒(méi)有機會(huì )執行了。再添加一個(gè)全局的回調函數OnTimerProc 如下:
VOID CALLBACK OnTimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime){_Module.Adder();}好了,大功告成。編譯,然后在SCM中啟動(dòng)我們的服務(wù)。在控制面板中打開(kāi)"事件查看器",看一下運行的結果,如下圖四:
圖四
好了,就寫(xiě)到這里吧,其他的內容大家自己深究吧。祝各位編程愉快!
聯(lián)系客服