(二)對服務的深入討論之上
上一章其實只是概括性的介紹,下面開始才是真正的細節所在。在進入點函數裡面要完成ServiceMain的初始化,准確點說是初始化一個SERVICE_TABLE_ENTRY結構數組,這個結構記錄了這個服務程序裡面所包含的所有服務的名稱和服務的進入點函數,下面是一個SERVICE_TABLE_ENTRY的例子:
SERVICE_TABLE_ENTRY service_table_entry[] =
{
{ "MyFTPd" , FtpdMain },
{ "MyHttpd", Httpserv},
{ NULL, NULL },
};
第一個成員代表服務的名字,第二個成員是ServiceMain回調函數的地址,上面的服務程序因為擁有兩個服務,所以有三個SERVICE_TABLE_ENTRY元素,前兩個用於服務,最後的NULL指明數組的結束。
接下來這個數組的地址被傳遞到StartServiceCtrlDispatcher函數:
BOOL StartServiceCtrlDispatcher(
LPSERVICE_TABLE_ENTRY lpServiceStartTable
)
這個Win32函數表明可執行文件的進程怎樣通知SCM包含在這個進程中的服務。就像上一章中講的那樣,StartServiceCtrlDispatcher為每一個傳遞到它的數組中的非空元素產生一個新的線程,每一個進程開始執行由數組元素中的lpServiceStartTable指明的ServiceMain函數。
SCM啟動一個服務程序之後,它會等待該程序的主線程去調StartServiceCtrlDispatcher。如果那個函數在兩分鐘內沒有被調用,SCM將會認為這個服務有問題,並調用TerminateProcess去殺死這個進程。這就要求你的主線程要盡可能快的調用StartServiceCtrlDispatcher。
StartServiceCtrlDispatcher函數則並不立即返回,相反它會駐留在一個循環內。當在該循環內時,StartServiceCtrlDispatcher懸掛起自己,等待下面兩個事件中的一個發生。第一,如果SCM要去送一個控制通知給運行在這個進程內一個服務的時候,這個線程就會激活。當控制通知到達後,線程激活並調用相應服務的CtrlHandler函數。CtrlHandler函數處理這個服務控制通知,並返回到StartServiceCtrlDispatcher。StartServiceCtrlDispatcher循環回去後再一次懸掛自己。