Movatterモバイル変換


[0]ホーム

URL:


Wayback Machine
6 captures
19 May 2008 - 08 Mar 2013
FebOCTApr
Previous capture25Next capture
200920112013
success
fail
COLLECTED BY
Organization:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
Collection:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
TIMESTAMPS
loading
The Wayback Machine - https://web.archive.org/web/20111025063446/http://www.codeproject.com:80/KB/cpp/codesolutions.aspx
Click here to Skip to main content
8,194,390 members and growing! (53,183 online)
EmailPassword Lost password?
Home
Search within:




Licence 
First Posted 11 Mar 2006
Views 24,115
Downloads 238
Bookmarked 23 times

CREATING MULTIPLE WINDOW SERVICES USING INI CONFIG FILE

MULTIPLE WINDOW SERVICES
 
See Also
Print Article
add
Add to your CodeProject bookmarks
Discuss
Discuss this article
4
  4.09 (5 votes)

1

2
1 vote, 20.0%
3
2 votes, 40.0%
4
2 votes, 40.0%
5
4.09/5 - 5 votes
μ 4.09, σa 1.46 [?]
Sponsored Links

Sample Image - multiservices.jpg

Introduction

This project demonstrates how one can configure a windows service to run as multiple services. I discovered that very little code is needed to make the basic ATL Service project that is created using the Visual Studio Wizard into a project that could create and run multiple services using the same executable. For my example I�m only going to create 3 services.

I spent a week trying to come up with a solution to my problem by looking at other code examples to no avail with every example using hard coded multiple service names in the SERVICE_TABLE_ENTRY table which was not an option for me. My example makes it very simple to create and run as many window services that the Service Component Manager can handle using the same executable and an INI file for each service you want to create. I even demonstrate that each service is running its own INI file by writing out to the Event Viewer log the name of the service running and its INI file.

To get started the _tWinMain method was modified to take a second argument for installing and uninstalling a windows service which would be a fully qualified path to an INI file. This is the backbone of the whole project and is used to create the service name by reading the SERVICENAME setting from the INI file and appending it to the project name which in this case is �ThisIs�. The INI file path is also stored in the registry for the service created and is reloaded in the ServiceMain when the service is started . I�m sure someone will come up with an alternative solution of passing any old name instead of an INI file path for the second argument which is fine depending on your environment.

All of my code changes are tagged as "//DG" and be sure to uninstall a service before recompiling a new build or it will stay in a "Performing registration" mode until you kill the process in task manager.

First step is to include the windows.h header file and two global variables used during service registration and for the creation of a service name at the top of the ThisIs.cpp file. We need this so we can read the INI files and also to read and write to the registry

#include"stdafx.h"#include"resource.h"#include#include"ThisIs.h"#include<windows.h>//DG#include"ThisIs_i.c"#include<stdio.h>CServiceModule _Module;char configfile[100];//DGchar sServiceName[30];//DGBEGIN_OBJECT_MAP(ObjectMap)END_OBJECT_MAP()

Second we need to modify the default _tWinMain created by the ATL wizard to Accept our second argument which will be the full path to our INI config file.

extern"C"int WINAPI _tWinMain(HINSTANCE hInstance,     HINSTANCE/*hPrevInstance*/, LPTSTR lpCmdLine,int/*nShowCmd*/){/* USAGE SECTION   TO INSTALL SERVICEA  thisis -install C:\ThisIs\ServiceA.ini  (This will create a service called ThisIsServiceA"  TO INSTALL SERVICEB  thisis -install C:\ThisIs\ServiceB.ini  (This will create a service called ThisIsServiceB"  TO INSTALL SERVICEC  thisis -install C:\ThisIs\ServiceC.ini  (This will create a service called ThisIsServiceC"    TO UNINSTALL SERVICEA    thisis -uninstall C:\ThisIs\ServiceA.ini  Please note that the SERVICENAME in the ini file is appended to the project name  so when you open up the Services window in Administrative Tools you should see  a service called "ThisIsServiceA" if you run the "thisis -install C:\ThisIs\ServiceA.ini"  command from a DOS prompt.*/lpCmdLine = GetCommandLine();//this line necessary for _ATL_MIN_CRT    _Module.Init(ObjectMap, hInstance, IDS_SERVICENAME, &LIBID_THISISLib);    _Module.m_bService = TRUE;    TCHAR szTokens[] = _T("-/");//lpCmdLine = "-install C:\\ThisIs\\ServiceA.ini";    LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);while (lpszToken != NULL)    {if (memcmp(lpszToken,"uninstall",9)==0)//DG{            strcpy(configfile,lpszToken +10);//DGif(strlen(configfile)<=0)//DG{MessageBox(NULL, _T("You didn't specify a config file argument."),"ThisIs", MB_OK);return0;}_Module.LoadConfigSettings();//DGstrcat(_Module.m_szServiceName,sServiceName);//DGif (strlen(sServiceName) ==0)//DG{MessageBox(NULL, _T("Unable to read SERVICENAME key in config file."),"ThisIs", MB_OK);return0;}return _Module.UnregisterServer();}// Register as Local Serverif (memcmp(lpszToken,"regserver",9)==0)//DG{            strcpy(configfile,lpszToken +10);//DGif(strlen(configfile)<=0)//DG{MessageBox(NULL, _T("You didn't specify a config file argument."),"ThisIs", MB_OK);return0;}_Module.LoadConfigSettings();//DGstrcat(_Module.m_szServiceName,sServiceName);//DGif (strlen(sServiceName) ==0)//DG{MessageBox(NULL, _T("Unable to read SERVICENAME key in config file."),"ThisIs", MB_OK);return0;}return _Module.RegisterServer(TRUE, FALSE);}// Register as Serviceif (memcmp(lpszToken,"install",7)==0)//DG{            strcpy(configfile,lpszToken +8);//DGif(strlen(configfile)<=0)//DG{MessageBox(NULL, _T("You didn't specify a config file argument."),"ThisIs", MB_OK);return0;}_Module.LoadConfigSettings();//DGstrcat(_Module.m_szServiceName,sServiceName);//DGif (strlen(sServiceName) ==0)//DG{MessageBox(NULL, _T("Unable to read SERVICENAME key in config file."),"ThisIs", MB_OK);return0;}return _Module.RegisterServer(TRUE, TRUE);}                lpszToken = FindOneOf(lpszToken, szTokens);    }// Are we Service or Local Server    CRegKey keyAppID;    LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ);if (lRes != ERROR_SUCCESS)return lRes;    CRegKey key;    lRes = key.Open(keyAppID, _T("{0D327CD4-741C-4E5D-BD84-4DB303595E0A}"), KEY_READ);if (lRes != ERROR_SUCCESS)return lRes;    TCHAR szValue[_MAX_PATH];    DWORD dwLen = _MAX_PATH;    lRes = key.QueryValue(szValue, _T("LocalService"), &dwLen);    _Module.m_bService = FALSE;if (lRes == ERROR_SUCCESS)        _Module.m_bService = TRUE;_Module.Start();// When we get here, the service has been stoppedreturn _Module.m_status.dwWin32ExitCode;}

Next we need to add some code to the Install() function so we can create a new registry hive location called Parameters so we can save the path of our INI file for the service we're going to install.

inline BOOL CServiceModule::Install(){char regpath[200];//DGif (IsInstalled())return TRUE;    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);if (hSCM == NULL)    {        MessageBox(NULL, _T("Couldn't open service manager"), m_szServiceName, MB_OK);return FALSE;    }// Get the executable file path    TCHAR szFilePath[_MAX_PATH];    ::GetModuleFileName(NULL, szFilePath, _MAX_PATH);    SC_HANDLE hService = ::CreateService(        hSCM, m_szServiceName, m_szServiceName,        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,        SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,        szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);if (hService == NULL)    {        ::CloseServiceHandle(hSCM);        MessageBox(NULL, _T("Couldn't create service"), m_szServiceName, MB_OK);return FALSE;    }    ::CloseServiceHandle(hService);    ::CloseServiceHandle(hSCM);/*DG BEGIN save config file path to registry*/DWORD maxlen =100;strcpy(regpath,"SYSTEM\\CurrentControlSet\\Services\\");strcat(regpath,m_szServiceName);strcat(regpath,"\\Parameters");CRegKey keyAppID;keyAppID.Create(HKEY_LOCAL_MACHINE,_T(regpath));keyAppID.SetValue(configfile, "CONFIGFILE");/*DG END*/    return TRUE;}

Next we need to declare the LoadConfigSettings routine for reading values from our INI file in the CServiceModule class module in the StdAfx.h.

class CServiceModule :public CComModule{public:HRESULT RegisterServer(BOOL bRegTypeLib, BOOL bService);HRESULT UnregisterServer();void Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, UINT nServiceNameID,const GUID* plibid = NULL);void Start();void ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv);void Handler(DWORD dwOpcode);void Run();    BOOL IsInstalled();    BOOL Install();    BOOL Uninstall();LONG Unlock();void LogEvent(LPCTSTR pszFormat, ...);void SetServiceStatus(DWORD dwState);void SetupAsLocalServer();void LoadConfigSettings();//DG//Implementationprivate:staticvoid WINAPI _ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv);staticvoid WINAPI _Handler(DWORD dwOpcode);// data memberspublic:    TCHAR m_szServiceName[256];    SERVICE_STATUS_HANDLE m_hServiceStatus;    SERVICE_STATUS m_status;DWORD dwThreadID;BOOL m_bService;};extern CServiceModule _Module;#include//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif// !defined(AFX_STDAFX_H__D0A5ADBC_7B3A_4E70_9027_BD4E9D5562D5__INCLUDED)

Next step we add the LoadConfigSettings routine to the ThisIs.cpp file

void CServiceModule::LoadConfigSettings()//DG{char szDefault[255];GetPrivateProfileString("MAIN","SERVICENAME", szDefault, sServiceName,255, configfile);/* Continue to add more entries as needed to read INI file settings */}

Now here is where we come to the most important part and the key to the whole process of getting multiple services to run. As far as the service executable is concerned it thinks it is called "ThisIs" but we're going to change it's internal run time Service Name and give it a new alias within the ServiceMain routine upon startup by changing the internal public variable m_szServiceName to the name of the running service. All of the code generated by the ATL service wizard is dependent upon this variable so by modifying it you force the entire application to fall in line.

To understand what i'm saying you need to know that the first argument passed in the ServiceMain argument array is the name of the service that was started.

inlinevoid CServiceModule::ServiceMain(DWORD dwArgc , LPTSTR*  lpszArgv )//DG{/*DG BEGIN */char msg[200];char regpath[200];/*Set Internal m_szServiceName variable to whatever service was started*/strcpy(m_szServiceName,(char *)lpszArgv[0]);strcpy(sServiceName,m_szServiceName);DWORD maxlen =100;strcpy(regpath,"SYSTEM\\CurrentControlSet\\Services\\");strcat(regpath,m_szServiceName);strcat(regpath,"\\Parameters");CRegKey keyAppID;keyAppID.Create(HKEY_LOCAL_MACHINE,_T(regpath));keyAppID.QueryValue(configfile,"CONFIGFILE",&maxlen);//LoadConfigSettings();//DGstrcpy(msg,m_szServiceName);strcat(msg," is configured to run with INI file:");strcat(msg,configfile);LogEvent(_T(msg));/*DG END*/// Register the control request handler    m_status.dwCurrentState = SERVICE_START_PENDING;    m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);    if (m_hServiceStatus == NULL)    {        LogEvent(_T("Handler not installed"));        return;    }    SetServiceStatus(SERVICE_START_PENDING);    m_status.dwWin32ExitCode = S_OK;    m_status.dwCheckPoint = 0;    m_status.dwWaitHint = 0;    // When the Run function returns, the service has stopped.    Run();    SetServiceStatus(SERVICE_STOPPED);    LogEvent(_T("Service stopped"));}

And that's all folks!

In my production version I have a built in timer in the Run() function and a host of other routines to connect to databases which is where the INI file comes in or you can choose to save it all in the registry.

License

About the Author

Diego Gil

Web Developer

United States United States

Member


loading...
Sign Up to vote  PoorExcellent
Add a reason or comment to your vote:x
Votes of 3 or less require a comment

Comments and Discussions

 
 RefreshFirstPrevNext
Generalusing dll in window servicemembershaeron21:02 14 Dec '06  
QuestionIncomplete?memberHobbitCoder3:14 13 Mar '06  
AnswerRe: Incomplete?memberDiego Gil16:09 13 Mar '06  
GeneralRe: Incomplete?memberHobbitCoder0:53 14 Mar '06  
Last Visit: 19:00 31 Dec '99     Last Update: 21:34 24 Oct '111

General General   News News   Suggestion Suggestion   Question Question   Bug Bug   Answer Answer   Joke Joke   Rant Rant   Admin Admin   

Permalink |Advertise |Privacy |Mobile
|Web01 |2.5.111017.1
Article Copyright 2006 by Diego Gil
Everything elseCopyright ©CodeProject, 1999-2011
Terms of Use
Layout:fixed|fluid

The Daily Insider

[8]ページ先頭

©2009-2025 Movatter.jp