$100 bounty for help with Windows Service code
Tyler Jensen via Digitalmars-d
digitalmars-d at puremagic.com
Sun Aug 17 18:25:18 PDT 2014
On Monday, 18 August 2014 at 01:11:37 UTC, Vladimir Panteleev
wrote:
> On Monday, 18 August 2014 at 00:37:15 UTC, Tyler Jensen wrote:
>> On Monday, 18 August 2014 at 00:07:57 UTC, ketmar via
>> Digitalmars-d wrote:
>>> On Sun, 17 Aug 2014 23:56:54 +0000
>>> Tyler Jensen via Digitalmars-d <digitalmars-d at puremagic.com>
>>> wrote:
>>>
>>> try to catch and process exceptions where they may arose. any
>>> file
>>> operation can throw exception (yes, even innocent-looking
>>> writeln(),
>>> let alone 'auto fl = File("...")').
>>>
>>> also, you don't need to manually close the file, it will be
>>> automatically closed when file variable goes out of scope.
>>
>> Now have all file i/o ops in a try/catch and ignoring errors
>> just to see if that has anything to do with it.
>
> I would recommend wrapping the entire main() function in a
> try/catch block, and logging any caught exceptions.
Good idea. I'll work to make this more robust before putting it
to a blog post.
>
>> The answer is no. I'm still getting result of 0 and a
>> GetLastError return of ERROR_INVALID_HANDLE. Now I just need
>> to figure out why the serviceStatusHandle is invalid. Ideas?
>
> I think I found the problem.
>
> In winsvc.d, SERVICE_STATUS_HANDLE is incorrectly declared as a
> DWORD (4-byte integer), when it should be declared as a HANDLE
> (8 bytes on 64-bit platforms).
>
> Please try changing the definition of SERVICE_STATUS_HANDLE
> from DWORD to size_t. I'll commit a fix to the win32 bindings
> repository.
FANTASTIC! That little gem fixed it all up. With this change to
winsvc.d:
//original: alias DWORD SERVICE_STATUS_HANDLE;
alias size_t SERVICE_STATUS_HANDLE;
And with the code below, you'll have an x64 Windows Service. The
code is not sufficient robust for production but it serves as a
working example with the fix to winsvc.d.
Vladimir, you win the $100. Thanks for doggedly helping me solve
this. It's very much appreciated and comes as a great relief.
Shoot me a private email with an address or PayPal account I can
use to send you the bounty.
Here's the final service code:
import core.sync.mutex : Mutex;
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)
{
try
{
File f = File(r"c:\temp\inc.log", "a");
auto tid = GetCurrentThreadId();
if (tid)
f.writeln(args, ", tid: ", tid);
else
f.writeln(args);
}
catch
{
}
}
void logItWt(T...)(T args)
{
try
{
File f = File(r"c:\temp\incwt.log", "a");
auto tid = GetCurrentThreadId();
if (tid)
f.writeln(args, ", tid: ", tid);
else
f.writeln(args);
}
catch
{
}
}
//void ServiceControlHandler(DWORD controlCode)
extern (Windows)
DWORD ServiceControlHandler(DWORD controlCode, DWORD eventType,
void* eventData, void* context)
{
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;
}
return NO_ERROR;
}
extern(Windows)
void ServiceMain(DWORD argc, TCHAR** argv)
{
//auto mythread = thread_attachThis();
debug logIt("ServiceMain pid: ", getpid(), argv);
// initialise service status
//with (serviceStatus)
//{
//DWORD dwServiceType;
//DWORD dwCurrentState;
//DWORD dwControlsAccepted;
//DWORD dwWin32ExitCode;
//DWORD dwServiceSpecificExitCode;
//DWORD dwCheckPoint;
//DWORD dwWaitHint;
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0; //|=
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = 0;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 3000;
//}
Sleep(1000); //test delay before getting handler
serviceStatusHandle =
RegisterServiceCtrlHandlerEx(serviceName,
&ServiceControlHandler, null);
debug logIt("RegisterServiceCtrlHandler, serviceStatusHandle
", serviceStatusHandle);
if (!serviceStatusHandle)
{
return;
}
// service is starting
serviceStatus.dwControlsAccepted = 0; //accept no controls
while pending
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 logItWt("worker pid: ", getpid());
//serve(CONTROL_PORT, logFile);
while (!stopping)
{
Sleep(5000);
logItWt("worker thread");
}
SetEvent(stopServiceEvent);
});
web.isDaemon = true;
web.start();
// running
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
auto runningStatus = SetStatus(SERVICE_RUNNING);
debug logIt("runningStatus ", runningStatus);
}
void StopService()
{
debug logIt("StopService called");
serviceStatus.dwControlsAccepted = 0;
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)
//{
serviceStatus.dwCurrentState = state;
serviceStatus.dwWin32ExitCode = exitCode;
serviceStatus.dwWaitHint = waitHint;
//}
auto result = SetServiceStatus(serviceStatusHandle,
&serviceStatus);
if (result == 0)
{
auto errCode = GetLastError();
logIt("SetServiceStatus error ", errCode);
}
return result;
}
//
---------------------------------------------------------------------------
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