討論區快速選單
知識庫快速選單
軟體開發過程中有哪些資安漏洞? 掌握Salesforce雲端管理秘訣 政府補助!學嵌入式+物聯網
[ 回上頁 ] [ 討論區發言規則 ]
[教學] Service程式的寫法
更改我的閱讀文章字型大小
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/28 下午 02:13:34
其實在MSDN裡都有sample可以參考, 不過Service程式的基本寫法蠻固定的, 看一看便大概能夠了解如何去撰寫. 想要了解更多的, 可再去參考MSDN的相關資料, 尤其是關於Service程式的帳號權限與執行空間.

我的習慣是將Service程式寫成一個console mode程式, 而這個程式同時做為Service服務, 並可對Service進行安裝與控制等功能. 它的主程式就是大家熟悉的main函數:

int main(int argc, char* argv[])
{
  if (argc >= 2)
  {
    const char* param = argv[1];
    if (stricmp(param,"/install") == 0)
    {
     // 安裝Service
     return 0;
    }
    if (stricmp(param,"/uninstall") == 0)
    {
     // 反安裝Service
     return 0;
    }
    if (stricmp(param,"/status") == 0)
    {
     // 顯示Service狀態
     return 0;
    }
    if (stricmp(param,"/start") == 0)
    {
     // 啟動Service
     return 0;
    }
    if (stricmp(param,"/stop") == 0)
    {
     // 停止Service
     return 0;
    }
    if (strcmp(param,某種參數) == 0)
    {
     // 執行Service
     return 0;
    }
  }
  // 顯示help
  return 0;
}

因此一個程式就能控制Service的安裝/反安裝/查詢狀態/啟動/停止, 而自己本身也做為Service服務的程式. 注意程式裡的"某種參數"是由自己去定義的, 而在安裝時指定Service程式在啟動時, 應以該參數來呼叫, 這樣才能配合去執行Service服務的程式部份.

Service程式的安裝與控制, 都必須透過Service Control Manager才行, 因此首先要先開啟SCM:

SC_HANDLE scm_handle = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (scm_handle == NULL) 錯誤處理...
其他處理...
CloseServiceHandle(scm_handle);

後面用有到scm_handle這個變數的, 便不再多做說明了. 以下便是各種安裝與控制的處理方法:

1.安裝Service程式

SC_HANDLE sv_handle = CreateService(scm_handle,SERVICE_NAME,SERVICE_NAME,SC_MANAGER_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_auto_START,SERVICE_ERROR_IGNORE,prg_name,NULL,NULL,NULL,NULL,NULL);
if (sv_handle == NULL) 錯誤處理...
CloseServiceHandle(sv_handle);

其中SERVICE_NAME便是Serivce程式的名稱, prg_name則是Service程式應執行的command line, 其中必須包括main函數裡所謂的"某種參數". 為了避免Service程式所在路徑有空白字元, 最好是將程式檔名用引號"括起來. CreateService最後兩個參數是帳號/密碼, 不給的話是以LocalSystem帳號執行, 處理本機資料大都已足夠, 如果需要更高權限的話, 便要另行給定 (帳號為domain\username). 另外, 這裡的範例寫法是不支援Interactive功能的, 也就是你不能顯示視窗, 也不能要求user輸入資料, 需要的話, 請參考MSDN裡的資料, 改變一下參數的設定.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/28 下午 02:20:53
2.反安裝Service程式

SC_HANDLE sv_handle = OpenService(scm_handle,SERVICE_NAME,access);
if (sv_handle == NULL) 錯誤處理...
if (!DeleteService(sv_handle)) 錯誤處理...
CloseServiceHandle(sv_handle);

SCM其實只是先將Service程式標示為刪除, 必須等Service程式所有的process和thread都停止後, 才會做真正的移除動作 (重開機亦可).

3.查詢狀態

SC_HANDLE sv_handle = OpenService(scm_handle,SERVICE_NAME,access);
if (sv_handle == NULL) 錯誤處理...
SERVICE_STATUS status;
if (!QueryServiceStatus(sv_handle,&status)) 錯誤處理...
status.dwCurrentState便是目前Service程式的狀態值,
CloseServiceHandle(sv_handle);

關於status.dwCurrentState的值, 必須配合你的Service服務程式裡的寫法而定, 一般SERVICE_RUNNING表正在執行, SERVICE_STOPPED表已經停止.

4.啟動Service程式

SC_HANDLE sv_handle = OpenService(scm_handle,SERVICE_NAME,access);
if (sv_handle == NULL) 錯誤處理...
if (!StartService(sv_handle,0,NULL)) 錯誤處理...
CloseServiceHandle(sv_handle);

5.停止Service程式

SC_HANDLE sv_handle = OpenService(scm_handle,SERVICE_NAME,access);
if (sv_handle == NULL) 錯誤處理...
SERVICE_STATUS status;
if (!ControlService(sv_handle,SERVICE_CONTROL_STOP,&status)) 錯誤處理...
status.dwCurrentState便是目前Service程式的狀態值
CloseServiceHandle(sv_handle);

ControlService會將第2參數的值送到Service服務控制程式內, 而這個服務控制程式是由我們寫的. 因此如何停止, 其實還是我們的事. 這裡可以經由回傳的狀態值來決定是否成功.

6.執行Service程式

SERVICE_TABLE_ENtry dispatch_table[] =
{
  {SERVICE_NAME, ServiceMain},
  {NULL, NULL}
};
StartServiceCtrlDispatcher(dispatch_table);

當呼叫StartServiceCtrlDispatcher後, Service程式便會進入常駐狀態, 直到狀態值變成SERVICE_STOPPED後, 這個函數才會還回控制權. 而在dispatch裡定義的ServiceMain函數, 便是我們要寫的Service服務主程式. 以下便是一個範例:
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/28 下午 02:29:31
static SERVICE_STATUS Service_Status;
static SERVICE_STATUS_HANDLE Service_Status_Handle;

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
  // 登錄Service Control Handler
  Service_Status_Handle = RegisterServiceCtrlHandler(SERVICE_NAME,ServiceCtrlHandler);
  if (Service_Status_Handle == NULL) return;
  // 設定執行狀態
  Service_Status.dwServiceType = SERVICE_WIN32;
  Service_Status.dwCurrentState = SERVICE_RUNNING;
  Service_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  Service_Status.dwWin32ExitCode = 0;
  Service_Status.dwServiceSpecificExitCode = 0;
  Service_Status.dwCheckPoint = 0;
  Service_Status.dwWaitHint = 0;
  if (!SetServiceStatus(Service_Status_Handle,&Service_Status)) return;
  // 建一個新的執行緒, 開始正式處理資料
  AfxBeginThread(thread_start_service,NULL);
}

這邊有兩個議題, 一個是Service Control Handler, 一個是新的執行緒, 前者待會再提. 由於ServiceMain主要是做Service服務的初始化動作, 因此在一定時限內必須結束返回, 不然會被視同Timeout失敗, 因此我們可將實際要處理的部份, 經由另一個執行緒來處理 (有需要的話, 也可常駐在系統裡), 如此ServiceMain的主控制權便很快地便還回給SCM.

Service Control Handler的目的, 便是用來提供給外界控制Service之用, 它傳入的參數, 便是前面提到ControlService傳入的功能碼. 不過這些功能碼是固定的, 而且在SetServiceStatus時, 便必須指定能夠接收那幾種碼. 這邊我們只設定可接收SERVICE_CONTROL_STOP訊息. 以下是一個範例:

void WINAPI ServiceCtrlHandler(DWORD opcode)
{
  switch(opcode)
  {
    case SERVICE_CONTROL_STOP:
     停掉ServiceMain建立的執行緒, 以及所有其他該關掉的東西...
     if (失敗) break;
     // 設定狀態值
     Service_Status.dwServiceType = SERVICE_WIN32;
     Service_Status.dwCurrentState = SERVICE_STOPPED;
     Service_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
     Service_Status.dwWin32ExitCode = 0;
     Service_Status.dwServiceSpecificExitCode = 0;
     Service_Status.dwCheckPoint = 0;
     Service_Status.dwWaitHint = 0;
     break;
    default:
     return;
  }
  SetServiceStatus(Service_Status_Handle,&Service_Status);
}

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/28 下午 02:44:08
現在我們要做的事, 便是如何在thread_start_service處理我們所需的服務, 由於這部份隨個人而定, 因此便不再說明下去. 例如可能是開始socket listener, 不斷接收資料並做處理, 或是一個timer, 去監控某些事件等等...

當然我們也可以再去啟動另一個程式檔, 來實際處理所需的服務 (用CreateProcess). 如果涉及到網路資源的存取, 而目前的執行帳號是LocalSystem, 也可利用LogonUser來做轉換, 例如:

static PROCESS_INFORMATION pi; // process資訊

// 登入使用者
HANDLE token;
if (!LogonUser(username,domain_name,password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_default,&token)) return;
// 執行處理程式
STARTUPINFO si;
memset(&si,0,sizeof(si));
memset(&pi,0,sizeof(pi));
si.cb = sizeof(si);
if (!CreateProcessAsUser(token,NULL,程式檔名,NULL,NULL,false,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi))
{
  CloseHandle(token);
  return;
}
WaitForSingleObject(pi.hProcess,INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
memset(&pi,0,sizeof(pi));
CloseHandle(token);

這樣的寫法會永久等待所呼叫的程式處理結束. 這邊PROCESS_INFORMATION做成global變數的目的, 便是用來在處理SERVICE_CONTROL_STOP訊息時, 可利用TerminateProcess直接將此程序關掉, 而達到停止Service的作用. 不過強迫停止該程式是否會造成副作用, 則必須自己去考慮, 不然便要用其他方法來通知停止.
作者 : akira32(Akira) VC++優秀好手DirectX優秀好手貼文超過2000則人氣指數超過350000點
[ 貼文 2527 | 人氣 384134 | 評價 3430 | 評價/貼文 1.36 | 送出評價 243 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/30 上午 12:27:26
可以減少return 0;

int main(int argc, char* argv[])
{
  if (argc >= 2)
  {
    const char* param = argv[1];
    if (stricmp(param,"/install") == 0)
    {
     // 安裝Service
    }
    else if (stricmp(param,"/uninstall") == 0)
    {
     // 反安裝Service
    }
    else if (stricmp(param,"/status") == 0)
    {
     // 顯示Service狀態
    }
    else if (stricmp(param,"/start") == 0)
    {
     // 啟動Service
    }
    else if (stricmp(param,"/stop") == 0)
    {
     // 停止Service
    }
    else if (strcmp(param,某種參數) == 0)
    {
     // 執行Service
    }
    else
{
     //不合法的參數
     return -1;
     }
    return 0;
  }
  // 顯示help
  return 0;
}
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/30 上午 06:45:14
>可以減少return 0;

... 真的不知該說些什麼... (有點程度的應該知道我想說的是什麼)

算了, 當我沒說好了... it's nonsense...
作者 : whatthis(WhatThis) 貼文超過500則
[ 貼文 703 | 人氣 523 | 評價 710 | 評價/貼文 1.01 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/30 上午 08:50:19
>... 真的不知該說些什麼... (有點程度的應該知道我想說的是什麼)
>
>算了, 當我沒說好了... it''s nonsense...

青衫兄整理這篇很有用啊,以前看WROX一本專講Windows Service的書,薄薄一本就要價1400呢

可以去搜尋一下那個說減少return 0;仁兄以前的發言,就可以知道不需要太在意了 ...... XD
作者 : cog(Cog) VC++卓越專家C++優秀好手貼文超過200則
[ 貼文 458 | 人氣 12 | 評價 3390 | 評價/貼文 7.4 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/31 上午 01:35:12
其實使用VC的專案精靈開"ATL project",就可以很快速的建立一個service起來,
實作上跟前文說是大同小異。只除了ServiceMain那一段我有不一樣的看法。

>這邊有兩個議題, 一個是Service Control Handler, 一個是新的執行緒, 前者待會再提. 由於ServiceMain主要是做Service服務的初始化動作, 因此在一定時限內必須結束返回, 不然會被視同Timeout失敗, 因此我們可將實際要處理的部份, 經由另一個執行緒來處理 (有需要的話, 也可常駐在系統裡), 如此ServiceMain的主控制權便很快地便還回給SCM.

檢查VC產生出來的程式碼,會發現ServiceMain會呼叫Run()去跑while loop
loop裡面做的事就是GetMessage & DispatchMessage,一直至service被中止。
至於SCM等待Timeout的關鍵在SetServiceStatus(SERVICE_RUNNING)上面,
如果程式一直不設定SERVICE_RUNNING,一段時間後就Timeout了。
應該還不需要使用到另外一個執行緒來完成Service的動作。
 板主 : 青衫 , Raymond
 > Visual C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - Visual C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
Visual C++
1 青衫 11070 
2 Raymond 10090 
3 Clier 7630 
4 小約翰 2500 
5 Cog 2030 
6 coco 1870 
7 aming 1410 
8 牧童哥 1400 
9 r2109 1380 
10 Akira 1350 
Visual C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.1865234