Author Topic: Contrib: windows service  (Read 9546 times)

DonSixto

  • Guest
Contrib: windows service
« on: October 31, 2007, 01:56:09 pm »
I'm sorry if I put this message in the wrong place.

Here is a little contribution.
Writing a daemon application in Linux is an easy and amusing task. There is a huge amount of information to help the programmer.
It is easy to port the daemon to windows too, using C::B and GNU compiler collection.
But it is not easy to get the daemon up and running in windows without logging in, because of the lack of information, and sometimes the misinformation.
The following is the skeleton of the main.cpp file for a project template. The user template is attached.
It is written in my coding style, feel free to beautify it.
Tested in Windows XP Service Pack 2 and Windows Server 2003.
Built with C::B snv 4335 under WinXP SP2.

Then, the daemon is a standalone application, which would be normally a console application.
The service is another console application created with this skeleton, which in turn starts and stops the daemon.
The daemon must include a way to start and stop through a connection to it or through a better way you may find.
This application skeleton will run even if the daemon executable is not present, so feel free to build and test.

Code
// Windows Service main application
// Add your code where necessary to create your own windows service application.

#include <windows.h>
#include <stdio.h>

// Replace with your own
#define NAME_IN_SERVICES TEXT("MyService")
#define MY_SERVICE_DESC TEXT("Replace with a description of your service.")
#define DAEMON_EXE_NAME "C:\\The\\Full\\Path\\to\\MyDaemon.exe"

// Some global vars
SERVICE_STATUS          gStatus;
SERVICE_STATUS_HANDLE   gStatusHandle;
HANDLE                  ghStopEvent = NULL;

void InstallService() {
  SC_HANDLE hSCMgr;
  SC_HANDLE hService;
  TCHAR ExeAddr[MAX_PATH];
  SERVICE_DESCRIPTION sd;
  LPTSTR Desc = MY_SERVICE_DESC;

  if (! GetModuleFileName(NULL, ExeAddr, MAX_PATH)) {
    printf("Cannot install service.\n");
    return;
  }

  hSCMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if (hSCMgr == NULL) {
    printf("OpenSCManager failed\n");
    return;
  }

  hService = CreateService(hSCMgr, NAME_IN_SERVICES, NAME_IN_SERVICES, SERVICE_ALL_ACCESS,
                           SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
                           ExeAddr, NULL, NULL, NULL, NULL, NULL);

  if (hService == NULL) {
    printf("CreateService failed (%s)\n", ExeAddr);
    CloseServiceHandle(hSCMgr);
    return;
  }
  else
    printf("Service successfully installed.\nExecutable: %s\n", ExeAddr);

  // Now, to change its description
  sd.lpDescription = Desc;
  if (! ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd))
    printf("ChangeServiceConfig2 failed.\n");

  CloseServiceHandle(hService);
  CloseServiceHandle(hSCMgr);
}

void UninstallService() {
  SC_HANDLE hSCMgr;
  SC_HANDLE hService;

  hSCMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if (NULL == hSCMgr) {
    printf("OpenSCManager failed\n");
    return;
  }

  hService = OpenService(hSCMgr, NAME_IN_SERVICES, DELETE);
  if (hService == NULL) {
    printf("OpenService failed\n");
    CloseServiceHandle(hSCMgr);
    return;
  }

  if (! DeleteService(hService))
    printf("Fallo DeleteService (%d)\n", (int) GetLastError());
  else
    printf("Service uninstalled successfully.\nYou must restart windows for changes to take effect.\n");

  CloseServiceHandle(hService);
  CloseServiceHandle(hSCMgr);
}

void MySetServiceStatus( DWORD State, DWORD ExitCode, DWORD Wait ) {
  gStatus.dwCurrentState = State;
  gStatus.dwWin32ExitCode = ExitCode;
  gStatus.dwWaitHint = Wait;

  if (State == SERVICE_START_PENDING)
    gStatus.dwControlsAccepted = 0;
  else
    gStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

  SetServiceStatus(gStatusHandle, &gStatus);
}

void WINAPI CtrlHandler( DWORD CtrlCmd ) {
  switch (CtrlCmd) {
    case SERVICE_CONTROL_STOP:
      MySetServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
      // Add here the necessary code to stop the daemon
      SetEvent(ghStopEvent);
      break;

    case SERVICE_CONTROL_INTERROGATE:
      // Add here the necessary code to query the daemon
      break;
  }

  MySetServiceStatus(gStatus.dwCurrentState, NO_ERROR, 0);
}

int Run( char * Command ) {
  STARTUPINFO si;
  PROCESS_INFORMATION pi;

  si.cb = sizeof(STARTUPINFO);
  si.lpReserved = NULL;
  si.lpDesktop = NULL;
  si.lpTitle = 0;
  si.dwX = si.dwY = si.dwXSize = si.dwYSize =
  si.dwXCountChars = si.dwYCountChars = si.dwFlags = 0;
  si.wShowWindow = SW_NORMAL;
  si.lpReserved2 = NULL;
  si.cbReserved2 = 0;
  si.hStdInput = si.hStdOutput = si.hStdError = 0;

  return CreateProcess(0, Command, 0, 0, 1, 0, 0, 0, &si, & pi);
}

void InitService() {
  // printf in this function will not work when running from SCM

  gStatusHandle = RegisterServiceCtrlHandler(NAME_IN_SERVICES, CtrlHandler);
  if (! gStatusHandle) {
    int e;

    e = GetLastError();
    if (e == ERROR_INVALID_NAME)
      printf("RegisterServiceCtrlHandler failed (ERROR_INVALID_NAME)\n");
    else if (e == ERROR_SERVICE_DOES_NOT_EXIST)
      printf("RegisterServiceCtrlHandler failed (ERROR_SERVICE_DOES_NOT_EXIST)\n");
    else
      printf("RegisterServiceCtrlHandler failed (%d)\n", (int) e);

    return;
  }

  gStatus.dwServiceType = SERVICE_WIN32;
  gStatus.dwServiceSpecificExitCode = 0;
  MySetServiceStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );

  // HERE put your daemon initialization code
  // if it takes too long to start, call MySetServiceStatus() with
  // SERVICE_START_PENDING periodically.
  // If initialization fails, call MySetServiceStatus with SERVICE_STOPPED.

  // The following is a generic code. Replace with your own.
  Run(DAEMON_EXE_NAME);
  // end of daemon initialization

  MySetServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
  ghStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (ghStopEvent == NULL) {
    MySetServiceStatus( SERVICE_STOPPED, NO_ERROR, 0 );
    printf("CreateEvent failed.\n");
    return;
  }

  WaitForSingleObject(ghStopEvent, INFINITE);
  MySetServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
}

SERVICE_TABLE_ENTRY DispatchTable[] = {
  { NAME_IN_SERVICES, (LPSERVICE_MAIN_FUNCTION) InitService },
  { NULL, NULL }
};

int main( int c, char * arg[] ) {
  if (c > 2) {
    printf("Usage: servicio [ install | uninstall ]\n");
    return 1;
  }

  if (c == 2 && ! stricmp(arg[1], "install")) {
    InstallService();
    return 0;
  }

  if (c == 2 && ! stricmp(arg[1], "uninstall")) {
    UninstallService();
    return 0;
  }

  if (! StartServiceCtrlDispatcher(DispatchTable))
    printf("StartServiceCtrlDispatcher failed\n");

  return 0;
}


Enjoy.

Kind regards

DonSixto


[attachment deleted by admin]

Offline orel

  • Multiple posting newcomer
  • *
  • Posts: 96
Re: Contrib: windows service
« Reply #1 on: November 01, 2007, 06:19:18 pm »
Nice contribution, can be very useful.

Maybe you could create a user-template and post it here, if you have the time ! :)
windows XP SP2
mingw gcc 3.4.5
svn Code::Blocks and M$ Visual Studio 2005 and .NET to eat!! SVNInside plugin :http://forums.codeblocks.org/index.php/topic,7063.0.html

DonSixto

  • Guest
Re: Contrib: windows service
« Reply #2 on: November 01, 2007, 11:05:53 pm »
Maybe you could create a user-template and post it here, if you have the time ! :)

I did it.
But it seems that when I pressed Preview it deleted the attached file.
I'll attach it once more.
In case it fails, the Console Application wizard generates the skeleton, and in the main.cpp file, replace the generated code with the given here.
My idea is that a C::B developer takes this code and creates a 'Windows Service' wizard (if you find it useful, of course).

regards

[attachment deleted by admin]

Offline orel

  • Multiple posting newcomer
  • *
  • Posts: 96
Re: Contrib: windows service
« Reply #3 on: November 02, 2007, 10:22:49 am »
Quote
I did it.
But it seems that when I pressed Preview it deleted the attached file.

You did the first time and it worked. My fault, i was thinking of a wizard ans wrote 'user-template' !!  I could if i find the time, that should'n be too much work, the only keywords to replace in the CPP file seem to be the path_to_daemon, the service name and its description.
windows XP SP2
mingw gcc 3.4.5
svn Code::Blocks and M$ Visual Studio 2005 and .NET to eat!! SVNInside plugin :http://forums.codeblocks.org/index.php/topic,7063.0.html