The previous day, due to system issues, we had to re-open Borland C ++ Builder 6, which had not been used for a long time, and compile a Windows service. The original idea is to write something that can set such as the service name, display name, description, and configuration file path based on the specified parameters, and use a service program as the shell of a variety of different service content, it can be separately controlled in the Windows Service Manager. Because BCB service templates are not customized, you need to pay a weekly discount.
The service objects implemented by users in BCB are inherited from TService, which is tentatively set to TMyService. When a Global Object Svrmgr: Application (TServiceApplication) is used to create a service object, the user's service object is automatically one of the Application components.
Generally, you can create a service object as follows:
- Application-> CreateForm (_ classid (TMyService), & MyService );
BCB SvrMgr. hpp is actually a Statement of Delphi implementation. View SvrMgr. pas. You can see the code for creating the service:
- Svc: = CreateService (SvcMgr,
- PChar (Name), // <TMyService-> Name is used as the service Name
- PChar (DisplayName), // <TMyService-> DisplayName is used as the display name
- SERVICE_ALL_ACCESS,
- GetNTServiceType,
- GetNTStartType,
- GetNTErrorSeverity,
- PChar (Path ),
- PChar (LoadGroup ),
- PTag,
- PChar (GetNTDependencies ),
- PSSN,
- PChar (Password ));
We can see that when the/install parameter is used to register a service, BCB uses the Name attribute of TMyService as the service Name and DisplayName as the service display Name. To customize these two names, we can add additional parameters:/name and/displayname, which are used to specify the service name and service display name respectively. Example:
MyService/install/name: "ThisIsAnotherService"/displayname: "display name"
/Name and/displayname are followed by a ":" to separate the subsequent strings. During Parameter Parsing, the system treats the strings in double quotation marks as a whole. If you do not know this and have different opinions on the quotation marks, please refer to the help provided by Windows: command prompt.
BCB only processes the/install and/uninstall parameters. The added parameters must be processed by yourself. Here, two BCB functions are used: ParamCount () and ParamStr (), for more information, see BCB Help. The following is the sample code:
- Bool isInstall = FindCmdLineSwitch ("install", true); // install the service flag
- Bool isUninstall = FindCmdLineSwitch ("uninstall", true); // uninstall the service flag
- AnsiString serviceName; // service name
- AnsiString serviceDispName; // service display name
- If (ParamCount ()> 1 & (isInstall | isUninstall) // specify the parameters and the installation and uninstall flag.
- {
- // Create it first
- Application-> CreateForm (_ classid (TMyService), & MyService );
- If (isInstall | isUninstall) // for installation or uninstall
- {
- For (int I = 1; I <= ParamCount (); I ++) // process all parameters cyclically
- {
- AnsiString param = ParamStr (I). LowerCase (); // converts the string to LowerCase for ease of comparison.
- Int pos; // parameter location
- If (param. Pos ("/name:") // match the service name: "/name: xxxxx"
- {
- Pos = param. Pos (":");
- ServiceName = param. SubString (pos + 1, param. Length ()-pos );
- If (pos = serviceName. Pos ("")> 0) // Delete spaces. This is not necessary. The system will point out that the name is invalid.
- {
- ServiceName. Delete (pos, 1 );
- }
- }
- Else
- If (param. Pos ("/dispname:") // match the service display name: "/serviceDispName: xxxxx"
- {
- Pos = param. Pos (":");
- Servicedispname = Param. substring (Pos + 1, Param. Length ()-Pos );
- }
- }
- If (servicename. Length ()> 0)
- Application-> components [0]-> name = servicename; // use the specified service name
- Else
- Servicename = Application-> components [0]-> name; // The default name of tmyservice: myservice is used if no service name is specified.
- If (serviceDispName. Length () = 0)
- ServiceDispName = serviceName; // if no display name is specified, use the service name instead.
- (TService *) Application-> Components [0])-> DisplayName = serviceDispName; // set the display name
- }
- }
- Application-> Run (); // <The service starts to Run
Now, compile and register our service. We can see that it is displayed in the service list in the specified way. Let's try to start it ...... And so on ..... Startup failed !!!! OOOOOOOH! SHIT !!!!!
Click here !?!?
Let's check SvrMgr. pas, BCB (or Delphi ?) To start the service:
- //.....
- Begin
- Forms. Application. OnException: = OnExceptionHandler; // exception handle
- ServiceCount: = 0; // number of service objects (TXXXService)
- For I: = 0 to componentcount-1 do // The number of components contained in the application is the number of service objects
- If components [I] Is tservice then Inc (servicecount );
- Setlength (servicestarttable, servicecount + 1); // you can specify the size of the Service Startup table.
- Fillchar (servicestarttable [0], sizeof (tservicetableentry) * (servicecount + 1), 0); // clear
- J: = 0;
- For I: = 0 to componentcount-1 do // fill in the service entry table
- If components [I] Is tservice then
- Begin
- ServiceStartTable [J]. lpServiceName: = PChar (Components [I]. Name); // The Name attribute is used here!
- ServiceStartTable [J]. lpServiceProc: = @ ServiceMain; // For the ServiceMain entry function, see Windows SDK help
- Inc (J );
- End;
- StartThread: = TServiceStartThread. Create (ServiceStartTable); // start the service
- //.....
- // TServiceStartThread thread function implementation
- Procedure TServiceStartThread. Execute;
- Begin
- If StartServiceCtrlDispatcher (FServiceStartTable [0]) then // use the service entry table to start the service
- ReturnValue: = 0
- Else
- Returnvalue: = getlasterror;
- End;
Windows API StartServiceCtrlDispatcher () uses the service entry table to start the service. This operation is name-related, and we have used the specified name to install the service. Therefore, when the system (TServiceApplication) when you use the original name (MyService here) to start the service, the service named MyService cannot be found (we have specified its name as ThisIsAnotherService), leading to startup failure. Because the system does not provide a context about the service name when starting the service, we need to create this context. A simple method is to modify the Service Startup path record and add the service name as a parameter when installing the service. When the service starts, parse this parameter and use this parameter to modify TMyService-> Name, so that the service can be started smoothly. The sample code is as follows:
Add additional parameters and save the service name:
- //.......
- Application-> Run ();
- // After running, the service has been successfully installed.
- If (isinstall)
- {
- // The simplest method is the hack registry.
- Tregistry * Reg = new Tregistry ();
- Ansistring key = "// system // CurrentControlSet // services //" + servicename; // registry path
- Reg-> rootkey = HKEY_LOCAL_MACHINE;
- If (reg-> openkey (Key, false) // open the referral
- {
- Ansistring ImagePath = reg-> readstring ("ImagePath"); // describes the path of the program image.
- Reg-> writestring ("ImagePath", ImagePath + "-" + servicename); // Add an additional parameter: Service name.
- }
- Reg-> closekey ();
- Delete reg;
- } // If isinstall then hack Registry
Then add the parameter processing:
- If (paramcount ()> 1 & (isinstall | isuninstall ))
- {
- //......
- }
- Else // start if it is not installed
- {
- // Check the number of parameters
- If (ParamCount ()> 0)
- {
- AnsiString extraParam = ParamStr (1). LowerCase (); // extra parameter, converted to lower case
- AnsiString specifiedServiceName;
- If (extraParam. Pos ("-") // locate "-"
- {
- Int pos = extraParam. Pos ("-");
- SpecifiedServiceName = extraParam. SubString (pos + 1, extraParam. Length ()-pos); // parse the service name
- }
- If (! SpecifiedServiceName. Length () // if the name is invalid, use the default name: MyService
- {
- Application-> CreateForm (_ classid (TMyService), & MyService); // use the default name
- }
- Else
- {
- Application-> CreateForm (_ classid (TMyService), & MyService );
- MyService-> Name = specifiedServiceName; // use the specified Name
- }
- }//
- } // If... else
- //...
- Application-> Run ();
- //...
Now, compile the program, reinstall the service, and try again to start ......
If there is no error, the Service should be able to start smoothly. (Nonsense)
BCB does not provide the description attribute of the service, which can be implemented by modifying the registry. The operation is very simple.
This is the final technique .....