VS2005 ATL WINDOWS服务感想
最近寫的WINDOWS 服務,以前用VC6寫過簡單的服務。VC6 帶的ATL 創建服務后,會生成一個繼承于CcomModule 的類,并覆蓋了START、STOP等(不記得了),只需在START、STOP里寫相應的代碼就行了。
現在已用VS2005,當然不用VC6了,VC8中的服務項目是基于.NET的,考慮.NET的性能對服務不合適,還是選擇ATL8。但創建完項目就發現生成的類除了安全檢測外什么也沒有。
生成的類繼承于CatlServiceModuleT的,在網上搜了一下CatlServiceModuleT,資料很少,例子較全的是一個英文網站上的,拿來一用出現一些問題,經過幾天的摸索(先把作業叫了,再摸索的),大體弄清CatlServiceModuleT。
CatlServiceModuleT 用T* pT = static_cast<T*>(this) 來實現對子類函數的調用(如果子類重寫了該函數)。我們可重寫的函數有ParseCommandLine、Start、Run、PreMessageLoop、RunMessageLoop、PostMessageLoop、InitializeSecurity、OnStop、OnPause、OnContinue、OnShutdown、ServiceMain、Handler、RegisterAppId等。
網上一般是重寫PreMessageLoop來做服務的開始工作,PostMessageLoop做結束工作。RegisterAppId 用來更改服務注冊信息。
主線程有一個消息循環,它不做什么,只等收到消息推出程序。PreMessageLoop里面可以啟動自己的線程,并進入服務處理循環。主線程和工作線程可用事件來交互,(如果多線程可能要用到互斥量、信號量),比如主線程OnStop的時候結束工作線程。
RegisterAppId 里面可以更改服務注冊信息,主要用函數ChangeServiceConfig2來更改服務的描述,也就是在服務管理里面每個服務的描述,不改就為空。
結束工作可用PostMessageLoop,但我是直接在OnStop里面寫的,也許主類中有要釋放的資源才用PostMessageLoop。我的資源全在工作類中。
現在是一些問題:
1,調試:怎樣才能在VS2005中調試。
看CatlServiceModuleT中的WinMain,檢查輸入參數有效就調用START。START先檢測服務有沒有注冊過,沒有注冊的就直接返回退出。所以一定要先注冊。檢測到有注冊就查看是否注冊為LocalService(也就是 -Service),如果為LocalService就把_ServiceMain連接到SCM(服務管理器),但在調試模式下不能連接成功,或者必須由SCM來啟動,要不它認為已啟動。所以不能注冊為服務,只能注冊為RegSever(-RegSever)。調試完后,再注冊為服務。如果只有在服務里面才出錯,可用CatlServiceModuleT帶的LogEvent 把調試信息寫到windows事件里面。MSDN中講可以服務進程連到VS2005我沒試過,因為全在RegSever模式下解決了。
2,更改服務的啟動模式。
沒找到更改服務啟動模式的方法。查看代碼發現在CatlServiceModuleT的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);
SERVICE_DEMAND_START表示手動在SCM啟動。有個方法,在重寫的RegisterAppId用ChangeServiceConfig修改,但看一下ChangeServiceConfig的參數,好多呀,太麻煩。加上我覺得寫一個服務就應該自動啟動,什么時候處理任務是服務的事,而且不想用了可以在SCM里禁用。所以我做了一個不道德的地方――把CatlServiceModuleT源程序改了。把SERVICE_DEMAND_START改為SERVICE_AUTO_START。
SC_HANDLE hService = ::CreateService(
hSCM, m_szServiceName, m_szServiceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, _T("RPCSS/0"), NULL, NULL);
誰叫他不道德沒流一個好的方法。當然你可以用ChangeServiceConfig,自己查一下。
3,暫停和繼續,
我想用暫停和繼續來實現配置的更新,因為不想改配置的時候,要停止服務,再啟動,那樣服務會斷。還有就是用命名管道或全局消息讓配置工具和服務通訊,但太麻煩,我只是想重新應有配置。
第一個問題,在SMC中暫停和繼續的菜單是灰的。找了很久,找到::SetServiceStatus(m_hServiceStatus, &m_status) ,還好m_status是Public ,用m_status. dwControlsAccepted=m_status.dwControlsAccepted| SERVICE_ACCEPT_PAUSE_CONTINUE;搞定。
另一個問題,重寫了OnPause但更本沒進去。用LogEvent調試,發現CatlServiceModuleT的_Handler都沒進去(LocalService就把_ServiceMain連接到SCM,_ServiceMain調用ServiceMain,ServiceMain中注冊Handler為SCM處理函數,處理STOP、PAUSE等),_Handler 中是這樣調用的((T*)_pAtlModule)->Handler(dwOpcode); 不知到_pAtlModule是指向誰的實例。我重寫了Handler,就可以進來了。在自己的Handler中調用PAUSE,OnContinue后,就調用父類的Handler。
終于完成了,代碼如下,工作線程沒寫出來。
class CNVSStoreServerModule : public CAtlServiceModuleT< CNVSStoreServerModule, IDS_SERVICENAME >
{
private:
CWorkThread * pWork; //工作類,沒寫出來。
public :
DECLARE_LIBID(LIBID_NVSStoreServerLib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_NVSSTORESERVER, "{CF40AF29-C742-4D52-906C-5915A611F2D6}")
HRESULT InitializeSecurity() throw()
{
return S_OK;
}
void OnPauze() throw()
{
SetEvent(pWork->mServerStopEvent);
SetServiceStatus(SERVICE_PAUSED);
__super::OnPause();
}
void OnStop() throw()
{
SetEvent(pWork->mServerStopEvent);
WaitForSingleObject(pWork->mCanStopEvent,INFINITE);
__super::OnStop();
}
void Handler(DWORD dwOpcode) throw()
{
switch (dwOpcode)
{
case SERVICE_CONTROL_PAUSE:
OnPauze();
break;
case SERVICE_CONTROL_CONTINUE:
OnContinue();
break;
}
__super::Handler(dwOpcode);
}
CServiceStatus GetServiceStatus() throw()
{
return this->m_ServiceStatus;
}
void OnContinue( ) throw( )
{
SetServiceStatus(SERVICE_RUNNING);
__super::OnContinue();
}
HRESULT PreMessageLoop(int nShowCmd) throw()
{
m_status.dwControlsAccepted =m_status.dwControlsAccepted | SERVICE_ACCEPT_PAUSE_CONTINUE;
HRESULT hr = __super::PreMessageLoop(nShowCmd);
if (hr == S_FALSE) hr = S_OK; //要這句才能走下去
pWork=new CWorkThread();
return hr;
}
HRESULT RegisterAppId(bool bService = false) throw()
{
HRESULT hr = S_OK;
BOOL res = __super::RegisterAppId(bService);
if (bService)
{
if (IsInstalled())
{
SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SERVICE_CHANGE_CONFIG);
SC_HANDLE hService = NULL;
if (hSCM == NULL)
hr = AtlHresultFromLastError();
else
{
hService = ::OpenServiceW(hSCM, m_szServiceName, SERVICE_CHANGE_CONFIG);
if (hService != NULL)
{
const int m_szServiceNameLen = 4096;
const int m_szServiceDescriptionLen = 2000;
WCHAR m_szServiceDescription[m_szServiceDescriptionLen]=L"你的服務描述";
SERVICE_DESCRIPTION sdBuf = {m_szServiceDescription};
res = ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &sdBuf);
::CloseServiceHandle(hService);
}
else
hr = AtlHresultFromLastError();
::CloseServiceHandle(hSCM);
}
}
}
return hr;
}
};
CNVSStoreServerModule _AtlModule;
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/,
LPTSTR /*lpCmdLine*/, int nShowCmd)
{
return _AtlModule.WinMain(nShowCmd);
}
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/udpcn/archive/2007/01/23/1491466.aspx
總結
以上是生活随笔為你收集整理的VS2005 ATL WINDOWS服务感想的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用 VC++ 2008 编写 Windo
- 下一篇: windows 批处理程序语法