• Welcome to Valhalla Legends Archive.
 

Windows NT Services

Started by Mephisto, November 26, 2004, 06:18 PM

Previous topic - Next topic

Mephisto

I've written my service code for my bot, but there's just one major problem.  After the service is started and is running, it does nothing, at least from what I can tell.  Perhaps someone can explain why it's not working based on the following code:

Edit: Provided cleaner re-written code (same results occur though)...

#include "stdafx.h"
#include "extern.h"
#include "CModBot.h"

#define _WIN32_WINNT 0x0502
#define WINVER 0x0502


// user-defined API CALLBACK functions
VOID WINAPI theServiceMain(DWORD argc, LPSTR argv[]);
VOID CALLBACK theServiceCtrlHandler(DWORD controlCode);

// miscellaneous functions
DWORD InitializeService(DWORD argc, LPSTR argv[]);
BOOL InstallService();
BOOL UninstallService();
VOID SvcDebugOut(LPSTR String, DWORD Status);

// API structures
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle;

// API HANDLEs
SC_HANDLE serviceHandle = NULL;
SC_HANDLE SCMHandle     = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

// miscellaneous data
CModBot *pBot;

DWORD __cdecl main(DWORD argc, LPSTR argv[])
{
freopen("C:\Documents and Settings\Sargera\My Documents\Development\C\ModBot\Forcebot.log", "a", stdout);
SERVICE_TABLE_ENTRY serviceTable[2];
memset(&serviceTable, 0, sizeof(serviceTable));
serviceTable[0].lpServiceName = "Forcebot";
serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)theServiceMain;
StartServiceCtrlDispatcher(serviceTable);
return 0;
}

VOID WINAPI theServiceMain(DWORD argc, LPSTR argv[])
{
DWORD status = 0;
memset(&serviceStatus, 0, sizeof(serviceStatus));

serviceStatus.dwServiceType     = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;

serviceStatusHandle = RegisterServiceCtrlHandler("Forcebot", (LPHANDLER_FUNCTION)theServiceCtrlHandler);

if (serviceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
printf("Error registering service control handler!");
return;
}

status = InitializeService(argc, argv);

if (status != 0) {
serviceStatus.dwCurrentState     = SERVICE_STOPPED;
        serviceStatus.dwCheckPoint     = 0;
        serviceStatus.dwWaitHint     = 0;
        serviceStatus.dwWin32ExitCode     = status;
        serviceStatus.dwServiceSpecificExitCode = status;

        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        return;
}

serviceStatus.dwCurrentState = SERVICE_RUNNING;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;

if (!SetServiceStatus (serviceStatusHandle, &serviceStatus)) {
        status = GetLastError();
        SvcDebugOut("[MY_SERVICE] SetServiceStatus error %ld\n", status);
}

pBot->Main(NULL);
// ...

serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(serviceStatusHandle, &serviceStatus);

SvcDebugOut("[MY_SERVICE] Returning the Main Thread \n", 0);
return;
}

VOID CALLBACK theServiceCtrlHandler(DWORD controlCode)
{
switch (controlCode) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
serviceStatus.dwCurrentState  = SERVICE_STOP_PENDING;
serviceStatus.dwWaitHint      = 1000;
SetServiceStatus(serviceStatusHandle, &serviceStatus);

externData->shutdownBot = FALSE;

serviceStatus.dwWaitHint = 0;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(serviceStatusHandle, &serviceStatus);

SvcDebugOut("[MY_SERVICE] Leaving MyService \n", 0);
return;
}
return;
}


DWORD InitializeService(DWORD argc, LPSTR argv[])
{
argc;
argv;
pBot = new CModBot;
return 0;
}

BOOL InstallService()
{
LPSTR filePath = new char[MAX_PATH];

if (!GetModuleFileName(NULL, filePath, MAX_PATH)) {
printf("Error: Could not find exe file location! (%d)\n", GetLastError());
return FALSE;
}

serviceHandle = CreateService(
SCMHandle,                 // SCManager database
TEXT("Forcebot"),          // name of service
TEXT("Forcebot"),          // service name to display
SERVICE_ALL_ACCESS,        // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START,      // start type
SERVICE_ERROR_NORMAL,      // error control type
filePath,                  // path to service's binary
NULL,                      // no load ordering group
NULL,                      // no tag identifier
NULL,                      // no dependencies
NULL,                      // LocalSystem account
NULL                       // no password
);

if (serviceHandle == NULL) {
printf("Failed to install service! (%d)\n", GetLastError());
return FALSE;
} else {
printf("Successfully installed service Forcebot!\n");
CloseServiceHandle(serviceHandle);
return TRUE;
}
}

BOOL UninstallService()
{
    serviceHandle = OpenService(SCMHandle, TEXT("Forcebot"), DELETE);

    if (serviceHandle == NULL) {
        printf("Could not open service! (%d)\n", GetLastError());
        return FALSE;
    }

    if (!DeleteService(serviceHandle))
    {
        printf("DeleteService failed (%d)\n", GetLastError());
        return FALSE;
    } else
        printf("Deleted service Forcebot successfully!\n");

    CloseServiceHandle(serviceHandle);
    return TRUE;
}

VOID SvcDebugOut(LPSTR String, DWORD Status)
{
LPSTR buf = new CHAR[1024];

if (strlen(String) < 1000) {
sprintf(buf, String, Status);
OutputDebugStringA(buf);
}

delete buf;
}

Adron

I suggest you put a breakpoint on pBot->Main(NULL); and make sure you get there. Then step into it and find out what it's doing....

Perhaps you should allow it interactive access to the desktop so you can see if it pops up some messagebox or something waiting for input?

Mephisto

I've suspected it always failed on the call to StartServiceCtrlDispatcher(...), as when debugging when not a service after making that call main() just returns.  Unless that call only succeeeds when running as a service.  But interestingly enough, any form of output fails when running as a service (e.g. directing stdout to a file, opening a file stream for output, etc.).

Adron

Quote from: Mephisto on November 27, 2004, 12:51 PM
I've suspected it always failed on the call to StartServiceCtrlDispatcher(...), as when debugging when not a service after making that call main() just returns.  Unless that call only succeeeds when running as a service.  But interestingly enough, any form of output fails when running as a service (e.g. directing stdout to a file, opening a file stream for output, etc.).

That call only succeeds when running as a service yes. Try debugging it when running as a service. You can use gflags to set up image file execution options so you get a debugger running as soon as it starts.

Opening a file stream for output should work when running as a service, unless your system account lacks permissions to open the file.

Skywing

Remember to set the interactive desktop flag if you are going to use the image debugger setting or your debugger will not be reachable.

Mephisto

Alright, thanks.  So there's nothing particuarily wrong with my code as it is?  Just one of those minute bugs that causes everything to fail...

Mephisto

I've pinpointed the problem to the call to SetServiceStatus using __asm int 3 breakpoints.  However, the error is that the function is never returning (though interestingly enough the service starts successfully).  I did try to comment out the call to SetServiceStatus but that caused the service to not start properly (obviously).  Anyways, does anyone know why based on the above code why this would happen.

Arta

hmm, you're not always calling SetServiceStatus in your handler.

Quote
When a service receives a control request, the service's Handler function must call SetServiceStatus, even if the service's status did not change. A service can also use this function at any time and by any thread of the service to notify the service control manager of status changes. Examples of such unsolicited status updates include:

- Checkpoint updates that occur when the service is in transition from one state to another.
- Fatal error updates that occur when the service must stop due to a recoverable error.

Mephisto

Changed it after I posted my most recent reply; it always sets it now regardless of the control code.