Compile a Windows service program with a graphical interface
Author: feitian2007
Download source code
Environment: Windows
2003, VC 6.0
Abstract: Starting from establishing a COM Service Program, transforming an MFC project into a service program, and finally enabling the program to display the graphic interface at startup.
Keywords: Windows Service Program COM Service Program start NT Service and desktop interaction before startup
1. What is a Windows service program?
You can see it in the following ways.
- Open the control panel, and then the management tool. There is a "service" in it. Double-click it to open it;
- Alternatively, open the Start Menu, click Run, and enter MMC.
Services. MSC (MMC can be omitted) or opened;
We will see a large list on the open page. The title bar contains the name, description, status, Startup type, logon identity, and other items. The Status column displays "started" as a service that has been started in the system. Let's take a look at the service attributes. For example, find the name print spooler, right-click it, and select "attribute". The command line it runs is C: /Windows/system32/spoolsv.exe. After the download stops, the spoolsv.exe process in the Task Manager exits. The list we see is the concentration of service programs, and each item is a service program.
The above marked as self-starting service programs started with the system. It works with some modifications to the registry:
HKEY_LOCAL_MACHINE/software/Microsoft/Windows/CurrentVersion/run
And a program similar to the registry key is different, even if the user does not log on to the system, they will run, or they will run before the system login.
2. How to establish your own service programs?
Each service program corresponds to a subitem under the Registry key HKEY_LOCAL_MACHINE/system/CurrentControlSet/services. Therefore, we can add a service program by adding a registry entry. For example, I want to add a test1 service program. The corresponding executable file is C:/test1.exe. Add the following registry key:
- Add the subitem test1 under [HKEY_LOCAL_MACHINE/system/CurrentControlSet/services;
- Added in test1:
String type: "Description" = "Test Service 1"
String type: "displayname" = "test1-displayname"
DWORD: "errorcontrol" = DWORD: 00000001
Extensible string value (that is, the path of the file ):
"ImagePath" = hex (2):, 3A, 00, 5C, 00 ,/
String type: "objectname" = "LocalSystem"
DWORD type, with a value of 3 indicating manual: "Start" = DWORD: 00000003
DWORD: "type" = DWORD: 00000020
- Add a subitem under test1:
[HKEY_LOCAL_MACHINE/system/CurrentControlSet/services/test1/security]
The key value is copied from the registry value of another service program.
If the test1 program is just a common Win32 program, it will not work after doing so. The service program has its own structural characteristics. So how to write these service programs?
The simplest way to create a service program is to use the atl com wizard in VC. Select new from the main menu, select atl com Appwizard from projects, enter a project name, select the directory, click OK, and select Service (exe) in the displayed dialog box ), click Finish. Test1.exe is compiled later.
Run test1.exe
/Regserveryou can use the registration program as a Service. test1.exe/unregserver cancels registration. When test1.exe is running, the parameters are:
Project-> Settings-> debug-> program arguments.
3. How to add your own code to the created service program?
Let's take a look at the structure of the created test1 project.
We can see that test1 has a class cservicemodule and some globals content. Globals includes a _ twinmain function, which is the entry of the program. The findoneof function is used to analyze the command line, and the global variable _ module is left.
In the _ twinmain function, _ module initializes and sets m_bservice to true. After some code that analyzes the command line and determines whether it is a service, _ module is used. start () enters the main execution part. In cservicemodule: Start (), the service_table_entry struct establishes a ing between the service name and the corresponding processing function. Here, if m_bservice is true, call startservicectrldispatcher to process messages similar to Win32 programs. Use the processing function in service_table_entry to run the program. If m_bservice is not true, run the run () function directly.
In service_table_entry, we can see that the service processing function is _ servicemain. Continue to trace and find that it is the servicemain function. In servicemain, registerservicectrlhandler is called to add a _ handler function to the service. For a service program, you can perform operations such as "start", "stop", "pause", and "Recover" on the service list that we opened earlier. This is actually done by _ handler to process different signals. _ Handler calls handler internally to process the passed dwopcode parameter in handler. For example, if it is service_control_stop, that is, when we "stop" the service, we will use postthreadmessage to send an exit signal to the main thread. Return to the servicemain function, in which the run () function is also called. That is to say, when a program runs as a Service identity and a non-service identity, the difference is that when the program runs as a Service Identity, there is an additional handler function to process the user's signal to the service program.
It should be noted that when registering this program as a service, it does not directly write the registry, but uses functions such as openscmanager and createservice in install to complete the task. Obviously, this is better than writing the Registry directly, because sometimes we don't know how to modify the registry key value to adapt to different service program configurations, and these functions have parameters.
Speaking of this, it involves our own code.
For example, if we have already set up an MFC program and want it to become a service program, what should we do?
Now I am creating a project mfc1 for MFC EXE, based on the dialog box. The simplest way to turn it into a service program is to take the cservicemodule for use. Because we have seen that the cservicemodule class has encapsulated the installation service, uninstalling the service, and running the service well.
Open the stdafx. h file of test1, copy the cservicemodule Declaration and related header files and variables to stdafx. h of mfc1. Pay attention to the sequence of header files.
Then, copy the implementation of the cservicemodule class in test1.cpp of test1 to mfc1.cpp in mfc1. Close to header file
Add # include before the cservicemodule class declaration in stdafx. h.
<Winsvc. h>, which declares the service_status_handle structure.
The following error occurs after compilation:
D:/vc6_test/mfc1/mfc1.cpp (52 ):
Error c2065: ''idr _ test1': Undeclared identifier
D:/vc6_test/mfc1/mfc1.cpp (336 ):
Error c2065: ''coinitializesecurity': Undeclared identifier
D:/vc6_test/mfc1/mfc1.cpp (337 ):
Error c2065: ''eoac _ none': Undeclared identifier
D:/vc6_test/mfc1/mfc1.cpp (362 ):
Error c2065: ''ids _ servicename '': Undeclared identifier
D:/vc6_test/mfc1/mfc1.cpp (362 ):
Error c2065: ''libid _ test1lib '': Undeclared identifier
We can find the idr_test1 declaration in test1 and put it in mfc1 to solve the first error. However, we can also remove some com-related code from the cservicemodule. Here we delete the registerserver and unregisterserver functions and make the run function become
Void
Cservicemodule: Run ()
{
_ Module. dwthreadid =
Getcurrentthreadid ();
Logevent (_ T ("Service
Started "));
If (m_bservice)
Setservicestatus (service_running );
MSG;
While (getmessage (& MSG, 0,
0, 0 ))
Dispatchmessage (& MSG );
}
Add the resource ids_servicename to "mfc1 ". , Point Resource add string resource
Comment out "ccommodule: Init (p,
H, plibid); "a row.
Annotation_twinmain function (Tip: Use # If 0 and # endif to annotate ). Find the following comments for _ twinmain:
# If 0
_ Twinmain
# Enfif
Now there should be no errors in compiling the program, but the added cservicemodule has not yet played a role.
Add two buttons on the idd_mfc1_dialog in mfc1: "Install service" and "uninstall service ". The added Click Event code is:
"Install service" button: void
Cmfc1dlg: onbutton1 () {_ module. Install ();}
"Uninstall service" button: void
Cmfc1dlg: onbutton2 () {_ module. Uninstall ();}
The following code is added to cmfc1app: initinstance (): delegate in the function
_ Module. INIT (objectmap,
This-> m_hinstance, ids_servicename, null );
_ Module. m_bservice = true;
_ Module. Start ();
The location is where the code of the original dialog box is generated. The code of the original generated dialog box is transferred to run (). The location is after the setservicestatus function is used to set the service status, and the subsequent Message Processing code is commented out, because the dialog box itself has a message processing mechanism.
If the following error occurs during compilation, remove the inline parameters before install () and uninstall:
Mfc1dlg. OBJ: Error lnk2001: unresolved external symbol "public: int
_ Thiscall cservicemodule: Install (void )"
(? Install @ cservicemodule @ qaehxz)
Mfc1dlg. OBJ: Error lnk2001: unresolved external symbol "public: int
_ Thiscall cservicemodule: uninstall (void )"
(? Uninstall @ cservicemodule @ qaehxz)
Now you can compile and run it. Click "Install service" to view mfc1 in the service list.
4. Is there a graphical interface when the service is running?
The graphic interface is displayed when mfc1.exe is directly run. However, no interface is displayed when you right-click the service list and choose "start" from the menu. What should I do?
When using the createservice function (install (), add a parameter to allow the program to interact with the desktop, that is, display the interface. This parameter is service_interactive_process.
The added createservice:
SC _handle hservice =: createservice (
Hscm, m_szservicename, m_szservicename,
Service_all_access,
Service_win32_own_process | service_interactive_process,
Service_demand_start,
Service_error_normal,
Szfilepath, null, null,
_ T ("RPCSS/0"), null, null );
Compile mfc1 again, uninstall the service, and install the service. We can see that the original dialog box appears when mfc1 is started through the service list.
To set the service to automatic start, change service_demand_start to service_auto_start.