一、nt服務程序
所謂nt服務,實際上就是一個可以在系統啟動時自動在一定身份下啟動的伴隨系統長時間存在的進程。象ftp server、http server、脫機打印等都是采用nt服務的形式提供的。這實際上類似unix的root daemon進程。nt服務歸納起來,nt服務又以下幾個特征:
1、可以自啟動,不需要交互啟動。這對于服務器來說是一個重要的特征。當然,你可以決定服務是否自啟動,甚至可以屏蔽某個服務。
2、nt服務沒有用戶界面,基本上類似一個dos 程序,因為nt服務必須長時間運行,所以不想普通win32進程一樣有自己的界面。但是nt服務可以同用戶有界面交互,這是一類特殊的服務進程。可以通過nt的任務管理器來看到服務進程。
3、nt服務通過scm(services control manager)接口來管理,安裝、啟動、停止、撤除等都需要scm的接口功能來進行。控制面板的服務控制器就是利用scm接口來管理系統中的所有服務的。實際上,還有一些可以控制服務的程序或者命令,有net.exe 、服務器管理器等 、scm.exe等。
4、這些進程都以一定的身份運行,以方便進行服務器資源的存取。一般情況下使用域中的localsystem賬號運行,此賬號對本機上的大多數資源(除非特別禁止)有完全的存取權限,這樣可以保證服務程序的“強大”。但是,也有些服務采用特別的賬號運行,你也可以特別設定一個服務的帳號。
5、由系統自動以線程方式運行,一般情況下不過多占用系統資源,這同普通的進程有所區別,如果不采用線程方式,一般進程往往消耗整個cpu資源。一般需要時時存在,又不能過多消耗資源的任務以服務來實現最合適。
二、服務控件
一般認為編寫nt服務需要使用c/c++來實現,vc6利用atl向導來提供一個基本的服務框架。具體實現步驟為:fileànew…àatl com appwizardàserviceàfinish.但是使用vc編寫nt服務需要編寫太多的代碼,這也意味著需要太多的調試、維護。實際上,nt服務不是必須由c/c++才可以編寫的,實際上可以由任何能夠實現上一節幾個特點的任何語言實現,包括vb。
vb編寫服務有那些好處呢,至少可以列出以下幾條:
1、編碼簡單,熟悉vb語法的任何人理解本文后都可編寫。
2、意味著修改服務實現的邏輯簡單,維護簡單。
3、可以利用幾乎大多數的vb中的組件功能,編寫一個強大的服務,譬如ado等,如果用vc來實現,相信任何人都會發怵。
4、(牽強一點)可以證明vb在bill的天空下是多么強大。
那么,vb如何實現nt服務編寫呢?據我所至,至少有兩種途徑:
1、 按照c/c++的思路利用winapi來實現。
2、 利用組件按照vb傳統方式實現。
如果利用方法1實際上是照搬c/c++的套路,如果有更好的路子可以實現,相信任何人都不會走這條“絕路”,因為相對于其他語言來說這種編程完全喪失了vb自身得特點同時也沒有獲得其他語言的任何優勢。在這里,想告訴大家的是利用ocx來實現一個服務。如果您在msdn中搜索samples/msdn/techart/4920/,您可以看到一個已經編寫好的vc5的工程文件。編譯這個工程實際上會得到一個ntsvc.ocx的。如果您對c/c++不熟悉,可以從http://www.mywebtech.net/download/ntsvc.zip 下載一個ntsvc.ocx,此ocx是我從backoffice碟中獲得的,將其拷貝到/winnt/system32/下,利用regsvr32 ntsvc.ocx命令注冊之。這樣,您的vb就可以從project/components…引出的對話框列表中看到名為“microsoft nt service control”項。此組件擁有我們創建一個服務的基本的功能,如果要編寫一個nt服務,我們將其拖進我們的窗體,然后設定其屬性,調用其與系統、注冊表、scm交互的功能就可以實現完成一個服務了。
我們首先了解這個組件的屬性,并向大家解釋這些屬性的用法:
account string ,賬號屬性,即本nt服務在哪一個nt域賬號下運行,缺省是localsystem賬號,實際上大多數的nt服務都可以在此賬號下安全圓滿的運行。
controlsaccepted long,此服務接受那些scm控制,為以下值:
0 允許start 以及 stop .
2 允許pause 以及 continue .
4 允許 shutdown 。
其他值,用戶自定義的某些事件.
利用這個屬性,您可以自己決定nt服務進程某個(譬如某個不可中斷操作)時刻是否允許scm停止、暫停、啟動等操作。
dependencies string ,如果您編寫的服務依賴于某個或者某些服務才能正常運行,您必須在注冊服務時指定依賴的服務列表。dependencies按照依賴順序以chr(0)來分隔多個服務,最后必須以兩個chr(0)結束。(大家可以看到這是一個c/c++的存在痕跡)
displayname string,顯示名,nt服務以何種名字顯示給察看者。
interactive boolean ,是否允許有同桌面用戶有交互的部分。
loadordergroup string,同dependencies相關,決定如果本服務啟動之前,那些服務必須啟動,格式也類似,也以chr(0)分割,連續的兩個chr(0)結尾。
password string,服務啟動的口令,如果使用缺省得賬號,就沒有必要設定服務啟動的帳號。
servicename string,服務名,如果使用net.exe來控制服務,net.exe的指定那一個服務的參數就是此屬性中的字符串。
startmode 枚舉型,具體為:
vcstartautomatic 2 服務可以自己啟動
svcstartmanual 3 服務手動啟動
svcstartdisabled 4 服務不能自啟動
另外有一個debug屬性,不做討論。
我們要將一個vb程序當作一個nt服務,必須向系統作一些“申請”,而相應的工作vb是無法很好的完成的。所以,ntsvc.ocx提供了相應的方法留作我們想系統傳遞相關信息。
install ,將當前vb程序安裝成nt服務,在此之前,您必須至少設置displayname, servicename, controlsaccepted以及startmode等屬性。除此之外您可能還要設置account、password、loadordergroup、dependencies等。這些信息的設置正確與否,決定您的服務程序能否正常啟動運行。
uninstall, 將當前ntsvc.ocx指定的服務從系統注冊表中刪除。nt服務取決于系統服務注冊表的設定,這是一個眾所周知的秘密。
startservice,將指定的服務啟動,如果該服務注冊了。
stopservice,停止服務,如果服務正在運行。
logevent ,記錄服務事件。服務運行中,可能發生錯誤以及意料不到的事件,這些可以通過此方法記錄下來,供管理員通過“事件察看器”察看相關的信息,以最優化服務。此方法有三個參數event, id, message. event指發生的事件類型,可以設為以下值:
svceventerror 1 錯誤事件
svceventwarning 2 警告事件.
svceventinformation 4 提供參考信息.
svceventauditsuccess 8 審計成功.
svceventauditfailure 10 審計失敗
除了以上方法,可能用戶還需要讀寫注冊表,此控件還提供了注冊表的訪問方法:
deletesetting (section[, key])
getallsettings(section)
getsetting(section, key[, default])
savesetting(section, key, setting).
三、編寫服務
了解以上內容,下面我們開始來設計一個服務,通過例子,讓大家理解如何在vb中編寫服務.
在此之前,我們決定寫一個什么樣的服務。我參考c++build中的一個例子,寫一個不斷報警的服務進程。該進程啟動后在后臺不斷間隔5秒發出beep叫,這可以讓大家更深切知道此服務的存在,雖然有些令人討厭。服務的名字為vbbeepsvc,在scm中顯示為the vb nt svc view。
跟著我一起來吧!
1、創建工程,設定相關使用到的控件。
所有的vb的控件必須有一個form作為載體,所以,首先我們創建一個標準工程,選擇菜單project—>components…,然后選取(microsoft nt service control),會在toolbar中出現nt服務控件。再拖一個timer控件到form上。然后保存一下。基本上,創建過程完成。
2、設定控件屬性。
選中ntsvc.ocx實例,在屬性欄中設定:displayname: the vb nt svc view,servicename: vbbeepsvc,startmode:3(手動啟動服務).其他的就缺省吧。
由于我們希望每個5秒就beep一次,所以我們必須依靠一種定時機制來實現,所以我們將timer的interval設定位5000毫秒。
以上屬性的設定視您的需要而定,我只是說在我的vbbeepsvc中如此設定足夠了。
3、編寫代碼,實現服務邏輯以及服務安裝、撤除。
因為服務程序實際上是一個exe文件,并且需要自己解決安裝、撤除問題,因此需要在此程序中加入利用nt服務控件來實現安裝、撤除問題。那么,應當在什么時候進行了。vb程序啟動時正時form裝載的時候,所以,我們需要在窗體的load事件中加入一些代碼:
on error goto err_load ‘如果出現錯誤就紀錄以供參考
dim strdisplayname as string
strdisplayname = ntservice1.displayname
if command = "-install" then ‘當啟動時帶上 –install的參數時
ntservice1.interactive = true
if ntservice1.install then
call ntservice1.savesetting("parameters", "timerinterval", "1000") ‘系統參數存儲
msgbox strdisplayname & " 安裝成功!"
else
msgbox strdisplayname & " 安裝失敗"
end if
end ‘終止安裝
else
if command = "-uninstall" then ‘如果啟動時帶上 撤除參數
if ntservice1.uninstall then
msgbox strdisplayname & " 撤除成功"
else
msgbox strdisplayname & " 撤除失敗"
end if
end ‘終止撤除
else
end if
end if
‘假若不是安裝或撤除操作,即為啟動服務
timer1.interval = cint(ntservice1.getsetting("parameters", "timerinterval", "2000"))
‘使用timer控件來模擬服務的線程特性
ntservice1.controlsaccepted = svcctrlpausecontinue ‘接受暫停、停止操作,意味著需要為此編碼
ntservice1.startservice ‘設置好參數后啟動服務
err_load:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description) ‘svcmessageerror為nt服務控件的錯誤值
4、添加控制服務的代碼。
盡管服務的連續線程等特性是依賴timer實現的,但是服務的控制卻是有scm接口向每一個服務發出的,表現在vb服務程序中為nt服務控件捕獲到相關的事件發生,我們就應當在這些事件中根據具體的情況響應,決定能不能、如何控制服務邏輯。當然,具體的邏輯在timer事件中表現,但是通過改變nt服務控件和timer控件均支持的全局變量,可以實現控制服務的邏輯實現。具體代碼演示:
private sub ntservice1_control(byval eventid as long)
on error goto err_control
‘在此加入一些自己的處理邏輯,當然也可以如本例一樣空缺
err_control:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description) ‘紀錄
end sub
private sub ntservice1_pause(success as boolean)
on error goto err_pause
timer1.enabled = false ‘禁止timer事件,因此也停止了服務的發生
call ntservice1.logevent(svceventerror, svcmessageerror, "service paused")
success = true ‘返回給scm命令發出者,表示服務成功停止
err_pause:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description)
end sub
private sub ntservice1_start(success as boolean)
on error goto err_start
success = true
timer1.enabled = true ‘允許服務邏輯進行
err_start:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description)
end sub
private sub ntservice1_stop()
on error goto err_stop
unload me ‘撤除form,自然timer也不存在,服務邏輯停止了
err_stop:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description)
end sub
5、編寫服務邏輯。
具體就是在timer事件中,根據約定寫一些服務細節。本例中就是發出been,但是考慮到對scm命令的響應,所以需要編碼為:
on error goto err_timer
beep ‘此處即具體的服務細節
err_timer:
call ntservice1.logevent(svcmessageerror, svceventerror, "[" & err.number & "] " & err.description)
end sub
6、編譯安裝、測試
如果以上沒有什么錯誤的話,現在可以編譯程序了。假設我們得到的服務程序的文件名為:vbbeepsvc.exe,我們需要通過以下命令進行安裝:
d:/vbprog/>vbbeepsvc –install
如果需要撤除已經安裝的服務,則:
d:/vbprog/>vbbeepsvc –uninstall
安裝完后,打開控制面板的“服務”(win2000中在“管理工具”),好了,看到其中的nt服務列表中包含我們加入的服務,顯示為:“the vb nt svc view”,我們可以類似啟動其他任何服務一樣啟動、停止、暫停此服務。啟動服務時,我們會聽到服務發出的討厭的beep聲音。我們的測試完成。
四、vb編寫服務的幾個說明:
1、首先聲明:vb編寫服務是一種嘗試,技術研究,并非提倡所有服務都要用vb寫才對頭。同理,也說明了服務非vc寫不可。
2、vb寫的服務僅適合win32服務,不適合nt底層服務。
3、vb的優勢在activex控件,nt服務中我們可以使用絕大多數控件來完成我們的服務邏輯,譬如涉及數據庫操作,我們可以使用ado組件,這方面,同vc相比,vb具有天然的優勢。
4、做好服務內部的錯誤事件記載,只有用好這一點,才能夠真正符合服務編寫規范,也方便我們的除錯。
5、最后一點,本文僅供參考,如有錯誤以及錯誤引起的后果,本人概不負責.