$100 bounty for help with Windows Service code

Tyler Jensen via Digitalmars-d digitalmars-d at puremagic.com
Sun Aug 17 14:24:22 PDT 2014


$100 bounty offered!

I've coded up a Windows Service that ALMOST works but I'm missing 
something. I need your help.

I obtained some code from another forum user who had obtained it 
from another forum. I've modified and simplified to the best of 
my limited ability. Install and uninstall works. The service 
starts and produces the following error:

[[ Error 1053: The service did not respond to the start or 
control request in a timely fashion. ]]

Despite the error, the process runs and the ServiceMain is called 
and the worker thread executes as you can see from the following 
logging output:

[[
main ["C:\\Code\\DPlay\\edge\\edge\\Debug DMD x64\\edge.exe"], 
tid: 9980
initialize, tid: 9980
RunService serviceTable.ptr 81359A3F00, tid: 9980
ServiceMain pid: 11092813551BE58, tid: 5852
RegisterServiceCtrlHandler, serviceStatusHandle 894776416, tid: 
5852
pendStatus 0, tid: 5852
runningStatus 0, tid: 5852
worker pid: 11092, tid: 9964
worker thread, tid: 9964
]]

I've been struggling with this for a while now and reading 
everything I can find but to no avail. I'm using the latest DMD 
compiler with the following command line:

"$(VisualDInstallDir)pipedmd.exe" dmd -m64 -g -debug -X 
-Xf"$(IntDir)\$(TargetName).json" -IC:\D\dsource 
-deps="$(OutDir)\$(ProjectName).dep" 
-of"$(OutDir)\$(ProjectName).exe" -map 
"$(INTDIR)\$(SAFEPROJECTNAME).map"

I'm using Visual Studio 2013 on Windows 8.1 x64. The dsource 
win32 lib comes from dsource.org.

The first person who can help me solve this wins a $100 bounty 
and an honorable mention in the blog post I'll write up about it, 
along with the working code.

Here's the code in its entirety.

import core.thread;
import std.conv : to;
import std.process : system;
import std.stdio;
import std.string;
import win32.w32api;
import win32.winbase;
import win32.winerror;
import win32.winnt;
import win32.windef;
import win32.winsvc;
pragma(lib, "advapi32.lib");

enum SERVICE_NAME = "MyTestService";
enum DISPLAY_NAME = "My Test Service";
enum SERVICE_START_NAME = "NT AUTHORITY\\NetworkService";
enum CONTROL_PORT = 8080;

enum _MAX_PATH = 4096;

__gshared
{
     char* serviceName;
     char* displayName;
     char* serviceStartName;
     SERVICE_TABLE_ENTRY[] serviceTable;
     SERVICE_STATUS serviceStatus;
     SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
     HANDLE stopServiceEvent = null;
     Thread web;
     DWORD checkPoint = 1;
     bool stopping = false;
}

void initialize()
{
     serviceName = cast(char*) toStringz(SERVICE_NAME);
     displayName = cast(char*) toStringz(DISPLAY_NAME);
     serviceStartName = cast(char*) toStringz(SERVICE_START_NAME);
     debug logIt("initialize");
}

void logIt(T...)(T args)
{
     File f = File(r"c:\temp\inc.log", "a");
     auto tid = GetCurrentThreadId();
     if (tid)
         f.writeln(args, ", tid: ", tid);
     else
         f.writeln(args);
     f.close();
}

extern (Windows)
void ServiceControlHandler(DWORD controlCode)
{
     debug logIt("ServiceControlHandler, controlCode ", 
controlCode);

     switch (controlCode)
     {
         case SERVICE_CONTROL_SHUTDOWN:
         case SERVICE_CONTROL_STOP:
             StopService();
             break;

         case SERVICE_CONTROL_SESSIONCHANGE:
         case SERVICE_CONTROL_PAUSE: // 2
         case SERVICE_CONTROL_CONTINUE: // 3
         case SERVICE_CONTROL_INTERROGATE: // 4
         default:
             SetStatus(serviceStatus.dwCurrentState);
             break;
     }
}

extern(Windows)
void ServiceMain(DWORD argc, TCHAR** argv)
{
     //auto mythread = thread_attachThis();
     debug logIt("ServiceMain pid: ", getpid(), argv);

     // initialise service status
     with (serviceStatus)
     {
         dwServiceType		= SERVICE_WIN32_OWN_PROCESS;
         dwCurrentState		= SERVICE_STOPPED;
         dwControlsAccepted |= SERVICE_ACCEPT_STOP | 
SERVICE_ACCEPT_SHUTDOWN;
         dwWin32ExitCode		= NO_ERROR;
         dwServiceSpecificExitCode = 0;
         dwCheckPoint		      = 0;
         dwWaitHint		          = 0;
     }

     serviceStatusHandle = RegisterServiceCtrlHandler(serviceName,
                                                        
&ServiceControlHandler);

     debug logIt("RegisterServiceCtrlHandler, serviceStatusHandle 
", serviceStatusHandle);
     if (!serviceStatusHandle)
     {
         return;
     }

     // service is starting
     auto pendStatus = SetStatus(SERVICE_START_PENDING);
     debug logIt("pendStatus ", pendStatus);

     // do initialisation here
     stopServiceEvent = CreateEvent(null, FALSE, FALSE, null);
     if (!stopServiceEvent)
     {
         debug logIt("!stopServiceEvent = CreateEvent");
     }

     //worker thread
     web = new Thread(
                      {
                          Sleep(3000);
                          debug logIt("worker pid: ", getpid());
                          //serve(CONTROL_PORT, logFile);
                          while (!stopping)
                          {
                              Sleep(5000);
                              logIt("worker thread");
                          }
                          SetEvent(stopServiceEvent);
                      });
     web.isDaemon = true;
     web.start();

     // running
     auto runningStatus = SetStatus(SERVICE_RUNNING);
     debug logIt("runningStatus ", runningStatus);
}

void StopService()
{
     debug logIt("StopService called");
     SetStatus(SERVICE_STOP_PENDING);
     stopping = true; //tell worker thread to stop

     //wait for signal
     if (WaitForSingleObject(stopServiceEvent, INFINITE) != 
WAIT_OBJECT_0)
     {
         auto err = GetLastError();
         throw new Exception("Error: %s", to!string(err));
     }

     // service was stopped signaled and SERVICE_STOP_PENDING set 
already - so clean up
     CloseHandle(stopServiceEvent);
     stopServiceEvent = null;

     // service is now stopped
     auto stoppedStatus = SetStatus(SERVICE_STOPPED);
     debug logIt("stoppedStatus ", stoppedStatus);
}


// Set the service status and report the status to the SCM.
DWORD SetStatus(DWORD state,
                 DWORD exitCode = NO_ERROR,
                 DWORD waitHint = 0)
{
     serviceStatus.dwCheckPoint = ((state == SERVICE_RUNNING) || 
(state == SERVICE_STOPPED))
         ? 0
         : checkPoint++;
     with (serviceStatus)
     {
         dwCurrentState = state;
         dwWin32ExitCode = exitCode;
         dwWaitHint = waitHint;
     }
     return SetServiceStatus(serviceStatusHandle, &serviceStatus);
}


// 
---------------------------------------------------------------------------

void RunService()
{
     serviceTable =
     [
         SERVICE_TABLE_ENTRY(serviceName, &ServiceMain),
         SERVICE_TABLE_ENTRY(null, null)
     ];

     debug logIt("RunService serviceTable.ptr ", serviceTable.ptr);
     StartServiceCtrlDispatcher(serviceTable.ptr);
}

void InstallService()
{
     SC_HANDLE serviceControlManager = OpenSCManager(null, null,
                                                     
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
     if (serviceControlManager)
     {
         TCHAR path[_MAX_PATH + 1];
         if (GetModuleFileName(null, path.ptr, path.sizeof) > 0)
         {
             SC_HANDLE service = CreateService(
                                               
serviceControlManager,
                                               cast (const) 
serviceName,
                                               cast (const) 
displayName,
                                               
SERVICE_QUERY_STATUS,
                                               
SERVICE_WIN32_OWN_PROCESS,
                                               SERVICE_AUTO_START,
                                               
SERVICE_ERROR_NORMAL,
                                               path.ptr,
                                               null, null, null,
                                               cast (const) 
serviceStartName,
                                               null);
             if (service)
                 CloseServiceHandle(service);
         }

         CloseServiceHandle(serviceControlManager);
     }
}

void UninstallService()
{
     SC_HANDLE serviceControlManager = OpenSCManager(null, null,
                                                     
SC_MANAGER_CONNECT);

     if (serviceControlManager)
     {
         SC_HANDLE service = OpenService(serviceControlManager,
                                         serviceName,
                                         SERVICE_QUERY_STATUS | 
DELETE);
         if (service)
         {
             SERVICE_STATUS serviceStatus;
             if (QueryServiceStatus(service, &serviceStatus))
             {
                 if (serviceStatus.dwCurrentState == 
SERVICE_STOPPED)
                     DeleteService(service);
             }

             CloseServiceHandle(service);
         }

         CloseServiceHandle(serviceControlManager);
     }
}

void StartStop(bool toStart)
{
     debug logIt("StartStop");
     SC_HANDLE serviceControlManager = OpenSCManager(null, null,
                                                     
SC_MANAGER_CONNECT);

     if (serviceControlManager)
     {
         SC_HANDLE service = OpenService(
                                         serviceControlManager,
                                         serviceName,
                                         SERVICE_QUERY_STATUS
                                         | SERVICE_START
                                         | SERVICE_STOP);

         if (service)
         {
             SERVICE_STATUS ss;
             uint result;

             if (toStart)
                 result = StartService(service, 0, null);
             else
                 result = ControlService(service, 
SERVICE_CONTROL_STOP, &ss);

             if (result == 0) {
                 uint err = GetLastError();
                 if (err == 1062)
                     writeln("Already stopped!");
                 else if (err == 1056)
                     writeln("Already started!");
                 else
                     writeln("Error: ", err);
             }

             CloseServiceHandle(service);
         }

         CloseServiceHandle(serviceControlManager);
     }
}

void main(string[] args)
{
     debug logIt("main ", args);
     initialize();
     if (args.length < 2)
     {
         writeln("running...");
         RunService();
     }
     else
     {
         switch (args[1])
         {
             case "install":
                 writeln("installing...");
                 InstallService();
                 break;
             case "uninstall":
                 writeln("uninstalling...");
                 UninstallService();
                 break;
             case "start":
                 writeln("starting...");
                 StartStop(true);
                 break;
             case "stop":
                 writeln("stopping...");
                 StartStop(false);
                 break;
             default:
                 writefln("%s: unknown command: %s",
                          to!string(serviceName), args[1]);
                 break;
         }
     }
}


More information about the Digitalmars-d mailing list