win32服務 mfc 結合 編寫有圖形介面的 Windows 服務程式

來源:互聯網
上載者:User

編寫有圖形介面的 Windows 服務程式

作者:feitian2007

下載原始碼

環境:Windows
2003,VC 6.0

摘要:從建立一個COM服務程式入手,然後將一個MFC項目改造成服務程式,最後讓這一程式在啟動時可以顯示圖形介面。

關鍵字:windows服務程式 COM服務程式 開機前啟動 NT服務 與案頭互動

一、什麼是windows的服務程式?

可以使用下面的幾種方法看到它。

  1. 開啟控制台,然後是管理工具,裡面有一個“服務”,雙擊後開啟;
  2. 或者是通過輸入命令的方式,開啟開始菜單,點擊運行,輸入mmc
    services.msc(mmc可省略),也可開啟;

  我們會在開啟的頁面中看到一個大的列表,標題列上包含有名稱、描述、狀態、啟動類型、登入身份等項。其中在狀態一欄中顯示為“已啟動”的是系統中已經啟動了的服務。我們先看一下服務的屬性。舉個例子,找到Print Spooler這一名稱,然後用右鍵在上面點擊,選擇“屬性”,可以看到它所執行的命令列是C:/WINDOWS/system32/spoolsv.exe,按下停止後,工作管理員中spoolsv.exe進程退出。我們所見到的這個列表就是服務程式的集中地,每一項就是一個服務程式。

上面這些標為自啟動的服務程式隨系統一起啟動。它與一些修改註冊表:

HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run 

項,及類似登錄機碼的程式不同的是,即使使用者沒有登入到系統中,它們也是會啟動並執行,或者說它們在系統登入前運行。

二、怎麼建立自己的服務程式?

  每一個服務程式對應登錄機碼HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services下的一個子項。因此我們可以通過增加註冊表項的方式增加服務程式。比如,我現在要增加一個test1服務程式,對應的可執行檔是c:/test1.exe。那麼我要增加如下登錄機碼:

  1. [HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/]下增加子項test1;
  2. test1下增加:
    字串型:"Description"="測試服務1"
    字串型:"DisplayName"="test1-displayname"
    DWORD型:"ErrorControl"=dword:00000001
    可擴充字串值(即檔案所在路徑):
    "ImagePath"=hex(2):43,00,3a,00,5c,00,74,00,65,00,73,00,74,00,31,00,2e,00,65,00,/
    78,00,65,00,00,00
    字串型:"ObjectName"="LocalSystem"
    DWORD型,值為3表示是手動:"Start"=dword:00000003
    DWORD型:"Type"=dword:00000020
  3. test1下增加子項:
    [HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/test1/Security]
    裡面的索引值從其他的服務程式註冊表值中複製。

  如果test1這一程式只是一個普通的win32程式,那麼這樣做了之後還是不行,服務程式有它自己的一些結構特點。那麼怎麼編寫這些服務程式?
  建立一個服務程式的最簡單的方法是用VC中的ATL COM嚮導。主菜單中選擇建立,然後選Projects中的ATL COM AppWizard,輸入一個項目名,選擇了所在目錄後,點OK按鈕,在出現的對話方塊中選擇Service(EXE),點Finish即可。然後編譯產生test1.exe。
  運行test1.exe
/regserver可以註冊程式為服務,test1.exe /unregserver是取消註冊。test1.exe運行時的參數是在:
Project->Settings->Debug->Program arguments中設定。

三、怎麼在建立的服務程式中加入自己的代碼?

我們看一下剛才產生的test1項目的結構。

  我們看到test1有一個類CServiceModule和一些Globals的內容。Globals包括一個_tWinMain函數,也就是程式的入口,其中使用了FindOneOf這一與分析命令列有關的函數,還剩下一個全域變數_Module。
  _tWinMain函數中,_Module初始化並設定m_bService為TRUE,在一些分析命令列和判斷是否為服務的代碼之後,使用_Module.Start()進入主要的執行部分。CServiceModule::Start()中,結構體SERVICE_TABLE_ENTRY建立了服務名與相應處理函數的映射。在這裡,如果m_bService為TRUE,則調用StartServiceCtrlDispatcher進入一種類似win32程式的訊息處理的過程,用SERVICE_TABLE_ENTRY中的處理函數讓程式執行下去。如果m_bService不為TRUE,則直接執行Run()函數。
  在SERVICE_TABLE_ENTRY中,我們看到服務處理函數為_ServiceMain,繼續跟蹤下去,發現是ServiceMain函數。在ServiceMain中又調用RegisterServiceCtrlHandler為服務增加了一個_Handler函數。對服務程式來說,我們可以在前面開啟的服務列表中對它們進行“啟動”,“停止”,“暫停”,“恢複”等操作。這實際上是由_Handler來處理不同的訊號。_Handler內部調用Handler,在Handler中,對傳入的dwOpcode參數作出處理。比如如果是SERVICE_CONTROL_STOP,也就是我們“停止”服務時,將使用PostThreadMessage對主線程發出一個退出的訊號。回到ServiceMain函數,在裡面同樣是在調用Run()函數。也就是說程式以服務身份和非服務身份運行時,區別在於以服務身份運行時多了一個Handler函數,處理使用者對服務程式發出的一些訊號。
  需要注意的是,這個程式註冊為服務時並不是直接寫註冊表,而是在Install中使用了OpenSCManager,CreateService等函數來完成的任務。顯然,這比直接寫註冊表要好一些,因為有時候我們並不太清楚要怎麼去修改登錄機碼的值來適應不同的服務程式配置,而這些函數有參數可以做到。

說到這裡,就涉及到我們自己編寫的代碼了。
比如現在我們已經建立了一個MFC的程式,想讓它成為一個服務程式,那要怎麼做呢?
我現在建立一個MFC EXE的項目mfc1,基於對話方塊。那麼把它變為一個服務程式的最簡單的方法就是把CServiceModule給拿過來使用。因為我們已經看到CServiceModule類已經把安裝服務,卸載服務,運行服務這些操作封裝得很好。
開啟test1的stdafx.h檔案,複製CServiceModule的聲明及相關標頭檔和變數到mfc1的stdafx.h中。 注意標頭檔的順序。
然後是把test1的test1.cpp中對CServiceModule類的實現,複製到mfc1中的mfc1.cpp中。靠近標頭檔放
在stdafx.h中CServiceModule類聲明前加上#include
<winsvc.h>,它裡面是對結構體SERVICE_STATUS_HANDLE的聲明。
編譯後出現以下類似錯誤:

D:/vc6_test/mfc1/mfc1.cpp(52) :
error C2065: ''IDR_Test1'' : undeclared identifier

D:/vc6_test/mfc1/mfc1.cpp(336) :
error C2065: ''CoInitializeSecurity'' : undeclared identifier

D:/vc6_test/mfc1/mfc1.cpp(337) :
error C2065: ''EOAC_NONE'' : undeclared identifier

D:/vc6_test/mfc1/mfc1.cpp(362) :
error C2065: ''IDS_SERVICENAME'' : undeclared identifier

D:/vc6_test/mfc1/mfc1.cpp(362) :
error C2065: ''LIBID_TEST1Lib'' : undeclared identifier

  我們可以在test1中找到IDR_Test1的聲明,放到mfc1中,解決第一條錯誤。但我們也可以去掉CServiceModule中與COM有關的一些代碼。這裡我們刪除RegisterServer,UnregisterServer兩個函數,並讓Run函數成為

void
CServiceModule::Run()

{

    _Module.dwThreadID =
GetCurrentThreadId();

    LogEvent(_T("Service
started"));

    if (m_bService)

       
SetServiceStatus(SERVICE_RUNNING);

 

    MSG msg;

    while (GetMessage(&msg, 0,
0, 0))

        DispatchMessage(&msg);

}            

增加資源IDS_SERVICENAME為“mfc1”。、點資源添加字串資源
注釋掉CServiceModule::Init中“CComModule::Init(p,
h, plibid);”一行。
注釋_tWinMain函數(技巧:用#if 0和#endif注釋)。找到_tWinMain 如下注釋

 

#if 0

_tWinMain

#enfif

現在編譯器,應該沒有錯誤了,但加入的CServiceModule還沒有起到作用。

在mfc1中的IDD_MFC1_DIALOG上加入兩個按鈕,分別是“安裝服務”,“卸載服務”。增加的單擊事件代碼為:
“安裝服務”按鈕:void
CMfc1Dlg::OnButton1() { _Module.Install(); }
“卸載服務”按鈕:void
CMfc1Dlg::OnButton2() { _Module.Uninstall(); }

下面在CMfc1App::InitInstance()中加入一些代碼:在函數裡面靠下放

_Module.Init(ObjectMap,
this->m_hInstance, IDS_SERVICENAME, NULL);

_Module.m_bService = TRUE;

_Module.Start();


  地點是在原來產生對話方塊的代碼的地方。而原有的產生對話方塊的代碼轉移到Run()中,位置是在使用了SetServiceStatus函數設定服務狀態之後,並注釋掉其後的訊息處理代碼,因對話方塊自身有訊息處理機制。

編譯時間若出現如下錯誤,將Install()和Uninstall()前的inline參數去掉即可:

mfc1Dlg.obj : error LNK2001: unresolved external symbol "public: int
__thiscall CServiceModule::Install(void)"
(?Install@CServiceModule@@QAEHXZ)

mfc1Dlg.obj : error LNK2001: unresolved external symbol "public: int
__thiscall CServiceModule::Uninstall(void)"
(?Uninstall@CServiceModule@@QAEHXZ)

現在可以編譯運行了。然後點擊“安裝服務”,就可以在服務列表中看到mfc1了。

四、這一服務程式運行時沒有圖形介面?

  不錯,剛才直接運行mfc1.exe時我們看到了圖形介面,但在服務列表中用右鍵菜單中的“啟動”時卻看不到任何介面。這該怎麼辦?
我們還需要在使用CreateService函數時(Install()中),加上一個參數,這樣才能允許程式與案頭互動,也就是可以顯示介面。這個參數是SERVICE_INTERACTIVE_PROCESS。
填加後的CreateService:

   
SC_HANDLE hService = ::CreateService(

        hSCM, m_szServiceName, m_szServiceName,

        SERVICE_ALL_ACCESS,

SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,

        SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,

        szFilePath, NULL, NULL,
_T("RPCSS/0"), NULL, NULL);       

再次編譯mfc1,卸載服務後,安裝服務。我們可以看到,通過服務列表啟動mfc1,原有的對話方塊出現了。

如需將服務設為自動啟動,則將 SERVICE_DEMAND_START 改為 SERVICE_AUTO_START。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.