VS2005 ATL WINDOWS服务感想

    技术2022-05-11  108

     

    最近写的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); }    

    最新回复(0)