制作可被svchost调用的服务(上)

一个被svchost调用的服务应该做成DLL,所以必须定义DLLMain函数,做为动态库的入口。

DLLMain的代码框架如下:

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   switch (ul_reason_for_call)
   {
      case DLL_PROCESS_DETACH:
         // 将服务状态设置为SERVICE_CONTROL_STOP
         notifyServiceManager(SERVICE_CONTROL_STOP, 0, 0);
         break;

      default:
         break;
   }

   return TRUE;
}

这里面最重要的是当卸载DLL时,应当通知OS服务已停止,所以需要将服务的状态设置为SERVICE_CONTROL_STOP。本例调用了一个自定义的函数notifyServiceManager来完成服务状态设定,代码如下:

DWORD __currentStatus;

int notifyServiceManager(DWORD status, DWORD exitCode, DWORD progress)
{
   __currentStatus = status;
   SERVICE_STATUS serviceStatus;
   serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS;
   serviceStatus.dwCurrentState = status;
   serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
   serviceStatus.dwWin32ExitCode = exitCode;
   serviceStatus.dwServiceSpecificExitCode = 0;
   serviceStatus.dwCheckPoint = progress;
   serviceStatus.dwWaitHint = 0;
   return SetServiceStatus(__serviceHandle, &serviceStatus);
}

这个函数的最后,调用了Win32 API SetServiceStatus来设置服务状态,关于SetServiceStatus的详细用法,可以参见MSDN文档。

svchost服务DLL应该导出ServiceMain函数,这个函数是服务的入口,代码如下:

SERVICE_STATUS_HANDLE __serviceHandle = nullptr;

extern "C" __declspec(dllexport) void ServiceMain(int argc, char *argv[])
{
   __serviceHandle = RegisterServiceCtrlHandler(argv[0], (LPHANDLER_FUNCTION)ServiceHandler);
   notifyServiceManager(SERVICE_START_PENDING, 0, 1);
   notifyServiceManager(SERVICE_RUNNING, 0, 0);

   HANDLE hThread = CreateThread(nullptr, 0, serviceThread, nullptr, 0, nullptr);
   if (hThread == nullptr)
   {
      // writeEventLog("error on create service thread.");
   }

   return;
}

注意:ServiceMain函数一定要用extern “C”方式声明,否则,在C++代码中,编译器会为ServiceMain生成一个古怪的名称,因而无法被svchost使用。

上面的代码中创建了一个服务线程,用于执行服务的具体动作。执行代码位域serviceThread函数中,其代码如下:

DWORD WINAPI serviceThread(void *params)
{
   // 执行具体的服务代码,一般会是循环,需要判断SERVICE_STOP_PENDING和SERVICE_STOPPED状态
   do {
      // 执行具体的服务代码
   } while ((__currentStatus != SERVICE_STOP_PENDING) && (__currentStatus != SERVICE_STOPPED));
   return 0;
}

ServiceMain注册了服务控制消息响应函数,名为ServiceHandler,代码如下:

void __stdcall ServiceHandler(DWORD dwControl)
{
    switch (dwControl)
    {
       case SERVICE_CONTROL_STOP:
          // do something ...
          notifyServiceManager(SERVICE_STOP_PENDING, 0, 0);
          notifyServiceManager(SERVICE_STOPPED, 0, 0);
          break;

       case SERVICE_CONTROL_PAUSE:
          // do something ...
          notifyServiceManager(SERVICE_PAUSE_PENDING, 0, 1);
          notifyServiceManager(SERVICE_PAUSED, 0, 0);
          break;

       case SERVICE_CONTROL_CONTINUE:
          // do something ...
          notifyServiceManager(SERVICE_CONTINUE_PENDING, 0, 1);
          notifyServiceManager(SERVICE_RUNNING, 0, 0);
          break;

       case SERVICE_CONTROL_INTERROGATE:
          // do something ...
          notifyServiceManager(__currentStatus, 0, 0);
          break;

       default:
          // do something ...
          notifyServiceManager(__currentStatus, 0, 0);
          break;
    }
}

注意:上面的代码中,对于每一个控制事件,都会对服务的状态做相应的更新。

至此为止,可被svchost调用的服务就制作完成了。

下一篇将说明如何将这个服务注册到svchost中。


版权声明:本文为kingfox原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。