一.Service
借助MSDN上Win7 Service的Demo和《用VC++建立Service服务应用程序》,在Win8上经历各种磨难,终于跑起来自己改装的服务程序了。
原来API基本没变,我所困惑的是Win7上直接运行都没有问题,在Win8上不可以。
报错:
OpenSCManager failed w/err 0x00000005
原来是Win8上权限的问题,也许我自己的Win7一启动就拥有了Admin权限吧。
下面直接进入正题,我整合了一下代码,共三个文件:main.c,Service.h, Service.cpp。(项目是控制台程序。)
main.c只是程序的入口,运行时接受参数。
#pragma region "Includes" #include <stdio.h> #include <windows.h> #include "Service.h" #pragma endregion int wmain(int argc, wchar_t* argv[]) { if ((argc > 1) && ((*argv[1] == L‘-‘ || (*argv[1] == L‘/‘)))) { if (_wcsicmp(L"install", argv[1] + 1) == 0) { SvcInstall(); } else if (_wcsicmp(L"remove", argv[1] + 1) == 0) { SvcUninstall(); } else if (_wcsicmp(L"query", argv[1] + 1) == 0) { SvcQueryConfig(); } else if(_wcsicmp(L"start",argv[1] + 1) == 0) { SvcStart(); } else if(_wcsicmp(L"stop",argv[1] + 1) == 0) { SvcStopNow(); } } else { _putws(L"Parameters:"); _putws(L" -install to install the service (require admin permission)"); _putws(L" -remove to remove the service (require admin permission)"); _putws(L" -query to query the configuration of the service"); _putws(L" -start to start the service"); _putws(L" -stop to stop the service"); RunService(); } return 0; }
代码中已经写的很清楚了,我的项目名称为Win8Service,只要运行Win8Service.exe -install,服务就会被安装。
注意:cmd必须要用admin启动。win8下做法:WIN+Q键,打开Search panel,输入cmd,右击Command Prompt,选择Run as administrator。
下面看看这几个函数的实现:
Service.h
#pragma once // Internal name of the service #define SERVICE_NAME L"CppWin8Service" // Displayed name of the service #define SERVICE_DISPLAY_NAME L"CppWin8Service Demo" // List of service dependencies - "dep1\0dep2\0\0" #define SERVICE_DEPENDENCIES L"" // The name of the account under which the service should run #define SERVICE_ACCOUNT L"NT AUTHORITY\\LocalService" // The password to the service account name #define SERVICE_PASSWORD NULL VOID RunService(); VOID SvcInstall(); VOID SvcUninstall(); VOID SvcQueryConfig(); BOOL SvcStart(); VOID SvcStopNow();
Service.cpp
#pragma region "Includes" #include <stdio.h> #include <windows.h> #include "Service.h" #pragma endregion SERVICE_STATUS g_ssSvcStatus; // Current service status SERVICE_STATUS_HANDLE g_sshSvcStatusHandle; // Current service status handle HANDLE g_hSvcStopEvent; VOID WINAPI SvcMain(DWORD dwArgc, LPWSTR* lpszArgv); VOID WINAPI SvcCtrlHandler(DWORD dwCtrl); VOID SvcInit(DWORD dwArgc, LPWSTR* lpszArgv); VOID SvcStop(); VOID SvcReportStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); VOID SvcReportEvent(LPWSTR lpszFunction, DWORD dwErr = 0); VOID RunService() { // You can add any additional services for the process to this table. SERVICE_TABLE_ENTRY dispatchTable[] = { { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)SvcMain }, { NULL, NULL } }; // This call returns when the service has stopped. // The process should simply terminate when the call returns. if (!StartServiceCtrlDispatcher(dispatchTable)) { SvcReportEvent(L"StartServiceCtrlDispatcher", GetLastError()); } } VOID WINAPI SvcMain(DWORD dwArgc, LPWSTR* lpszArgv) { SvcReportEvent(L"Enter SvcMain"); // Register the handler function for the service g_sshSvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, SvcCtrlHandler); if (!g_sshSvcStatusHandle) { SvcReportEvent(L"RegisterServiceCtrlHandler", GetLastError()); return; } // These SERVICE_STATUS members remain as set here g_ssSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_ssSvcStatus.dwServiceSpecificExitCode = 0; // Report initial status to the SCM SvcReportStatus(SERVICE_START_PENDING, NO_ERROR, 3000); // Perform service-specific initialization and work. SvcInit(dwArgc, lpszArgv); } VOID WINAPI SvcCtrlHandler(DWORD dwCtrl) { // Handle the requested control code. switch(dwCtrl) { case SERVICE_CONTROL_STOP: // Stop the service // SERVICE_STOP_PENDING should be reported before setting the Stop // Event - g_hSvcStopEvent - in SvcStop(). This avoids a race // condition which may result in a 1053 - The Service did not // respond... error. SvcReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); SvcStop(); SvcReportStatus(g_ssSvcStatus.dwCurrentState, NO_ERROR, 0); return; case SERVICE_CONTROL_INTERROGATE: break; default: break; } } VOID SvcInit(DWORD dwArgc, LPWSTR* lpszArgv) { SvcReportEvent(L"Enter SvcInit"); ///////////////////////////////////////////////////////////////////////// // Service initialization. // // Declare and set any required variables. Be sure to periodically call // ReportSvcStatus() with SERVICE_START_PENDING. If initialization fails, // call ReportSvcStatus with SERVICE_STOPPED. // Create a manual-reset event that is not signaled at first. The control // handler function, SvcCtrlHandler, signals this event when it receives // the stop control code. g_hSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_hSvcStopEvent == NULL) { SvcReportStatus(SERVICE_STOPPED, NO_ERROR, 0); return; } // Report running status when initialization is complete. SvcReportStatus(SERVICE_RUNNING, NO_ERROR, 0); ///////////////////////////////////////////////////////////////////////// // Perform work until service stops. // while(TRUE) { // Perform work ... // Check whether to stop the service. WaitForSingleObject(g_hSvcStopEvent, INFINITE); SvcReportStatus(SERVICE_STOPPED, NO_ERROR, 0); return; } } VOID SvcStop() { SvcReportEvent(L"Enter SvcStop"); // Signal the service to stop. if (g_hSvcStopEvent) { SetEvent(g_hSvcStopEvent); } } VOID SvcReportStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { static DWORD dwCheckPoint = 1; // Fill in the SERVICE_STATUS structure. g_ssSvcStatus.dwCurrentState = dwCurrentState; g_ssSvcStatus.dwWin32ExitCode = dwWin32ExitCode; g_ssSvcStatus.dwWaitHint = dwWaitHint; g_ssSvcStatus.dwControlsAccepted = (dwCurrentState == SERVICE_START_PENDING) ? 0 : SERVICE_ACCEPT_STOP; g_ssSvcStatus.dwCheckPoint = ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ? 0 : dwCheckPoint++; // Report the status of the service to the SCM. SetServiceStatus(g_sshSvcStatusHandle, &g_ssSvcStatus); } VOID SvcReportEvent(LPWSTR lpszFunction, DWORD dwErr) { HANDLE hEventSource; LPCWSTR lpszStrings[2]; wchar_t szBuffer[80]; hEventSource = RegisterEventSource(NULL, SERVICE_NAME); if (NULL != hEventSource) { WORD wType; if (dwErr == 0) { swprintf_s(szBuffer, ARRAYSIZE(szBuffer), lpszFunction); wType = EVENTLOG_INFORMATION_TYPE; } else { swprintf_s(szBuffer, ARRAYSIZE(szBuffer), L"%s failed w/err 0x%08lx", lpszFunction, dwErr); wType = EVENTLOG_ERROR_TYPE; } lpszStrings[0] = SERVICE_NAME; lpszStrings[1] = szBuffer; ReportEvent(hEventSource, // Event log handle wType, // Event type 0, // Event category 0, // Event identifier NULL, // No security identifier 2, // Size of lpszStrings array 0, // No binary data lpszStrings, // Array of strings NULL); // No binary data DeregisterEventSource(hEventSource); } } VOID SvcInstall() { wchar_t szPath[MAX_PATH]; if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0) { wprintf(L"GetModuleFileName failed w/err 0x%08lx\n", GetLastError()); return; } // Open the local default service control manager database SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); if (!schSCManager) { wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); return; } // Install the service into SCM by calling CreateService SC_HANDLE schService = CreateService( schSCManager, // SCManager database SERVICE_NAME, // Name of service SERVICE_DISPLAY_NAME, // Name to display SERVICE_CHANGE_CONFIG, // Desired access SERVICE_WIN32_OWN_PROCESS, // Service type SERVICE_DEMAND_START, // Start type SERVICE_ERROR_NORMAL, // Error control type szPath, // Service‘s binary NULL, // No load ordering group NULL, // No tag identifier SERVICE_DEPENDENCIES, // Dependencies SERVICE_ACCOUNT, // Service running account SERVICE_PASSWORD); // Password of the account if (NULL != schService) { wprintf(L"%s installed.\n", SERVICE_DISPLAY_NAME); CloseServiceHandle(schService); } else { wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError()); } CloseServiceHandle(schSCManager); } VOID SvcUninstall() { // Open the local default service control manager database SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!schSCManager) { wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); return; } // Open the service with delete, stop and query status permissions SC_HANDLE schService = OpenService(schSCManager, SERVICE_NAME, DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); if (NULL != schService) { // Try to stop the service SERVICE_STATUS ssSvcStatus; if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { wprintf(L"Stopping %s.", SERVICE_DISPLAY_NAME); Sleep(1000); while (QueryServiceStatus(schService, &ssSvcStatus)) { if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { wprintf(L"."); Sleep(1000); } else break; } if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { wprintf(L"\n%s stopped.\n", SERVICE_DISPLAY_NAME); } else { wprintf(L"\n%s failed to stop.\n", SERVICE_DISPLAY_NAME); } } // Now remove the service by calling DeleteService if (DeleteService(schService)) { wprintf(L"%s removed.\n", SERVICE_DISPLAY_NAME); } else { wprintf(L"DeleteService failed w/err 0x%08lx\n", GetLastError()); } CloseServiceHandle(schService); } else { wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError()); } CloseServiceHandle(schSCManager); } VOID SvcQueryConfig() { // Open the local default service control manager database SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!schSCManager) { wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); return; } // Try to open the service to query its status and config SC_HANDLE schService = OpenService(schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); if (NULL != schService) { wprintf(L"%s was installed.\n", SERVICE_DISPLAY_NAME); DWORD cbBytesNeeded; // // Query the status of the service // SERVICE_STATUS_PROCESS ssp; if (QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &cbBytesNeeded)) { wprintf(L"Service status: "); switch (ssp.dwCurrentState) { case SERVICE_STOPPED: _putws(L"Stopped"); break; case SERVICE_RUNNING: _putws(L"Running"); break; case SERVICE_PAUSED: _putws(L"Paused"); break; case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: case SERVICE_CONTINUE_PENDING: case SERVICE_PAUSE_PENDING: _putws(L"Pending"); break; } } else { wprintf(L"QueryServiceStatusEx failed w/err 0x%08lx\n", GetLastError()); } CloseServiceHandle(schService); } else { DWORD dwErr = GetLastError(); if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST) { wprintf(L"%s was not installed.\n", SERVICE_DISPLAY_NAME); } else { wprintf(L"OpenService failed w/err 0x%08lx\n", dwErr); } } CloseServiceHandle(schSCManager); } BOOL SvcStart() { // run service with given name SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schSCManager==0) { long nError = GetLastError(); wprintf(L"OpenSCManager failed, error code = %d\n", nError); } else { // open the service SC_HANDLE schService = OpenService( schSCManager, SERVICE_NAME, SERVICE_ALL_ACCESS); if (schService==0) { long nError = GetLastError(); wprintf(L"OpenService failed, error code = %d\n", nError); } else { // call StartService to run the service if(StartService(schService, 0, (LPCWSTR*)NULL)) { wprintf(L"%s started.\n", SERVICE_DISPLAY_NAME); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); return TRUE; } else { long nError = GetLastError(); wprintf(L"StartService failed, error code = %d\n", nError); } CloseServiceHandle(schService); } CloseServiceHandle(schSCManager); } return FALSE; } VOID SvcStopNow() { // Open the local default service control manager database SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!schSCManager) { wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); return; } // Open the service with delete, stop and query status permissions SC_HANDLE schService = OpenService(schSCManager, SERVICE_NAME, DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); if (NULL != schService) { // Try to stop the service SERVICE_STATUS ssSvcStatus; if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { wprintf(L"Stopping %s.", SERVICE_DISPLAY_NAME); Sleep(1000); while (QueryServiceStatus(schService, &ssSvcStatus)) { if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { wprintf(L"."); Sleep(1000); } else break; } if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { wprintf(L"\n%s stopped.\n", SERVICE_DISPLAY_NAME); } else { wprintf(L"\n%s failed to stop.\n", SERVICE_DISPLAY_NAME); } } CloseServiceHandle(schService); } else { wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError()); } CloseServiceHandle(schSCManager); }
下面看看运行结果如何。
WIN+R,调出RUN,输入services.msc调出Service管理。在这里你就会看见CppWin8Service Demo,点击一下,会发现其没有运行。
我们在刚刚install的控制台继续运行:
D:\honeywell\workspace\Win8Service\Debug>Win8Service.exe -install CppWin8Service Demo installed. D:\honeywell\workspace\Win8Service\Debug>Win8Service.exe -start CppWin8Service Demo started.
刷新一下Service,会看到此服务以及启动。
打开事件查看器(运行eventvwr.msc),Windows logs---》Application中可以看到CppWin8Service的infomation,比如:
CppWin8Service Enter SvcInit
二、外部程序启动和停止此服务
服务跑起来了,那么我们如何在外部程序中控制它呢?其实很简单,就是用上面的SvcStart和SvcStopNow方法来做就可以了。
我们新建一个MFC对话框程序,加两个button,一个启动一个停止。将两个函数拷进去,然后包含一下此头文件就可以了。
#include <winsvc.h>
三、批处理安装服务程序
说白了,就是用sc命令来安装启动服务程序,用批处理来包装一下,注意运行批处理时也要用admin。
sc命令见sc命令创建启动服务。
Service安装和启动的bat:
@echo. start service! @echo off @sc create LincTestServer binPath= "D:\XXX\Debug\NewService.exe" @sc start LincTestServer @sc config LincTestServer start= AUTO @sc config LincTestServer displayname="linc service" @echo off @echo. start ok! @pause
停止的bat:
@echo.stop service @echo off @sc stop LincTestServer @echo off @echo.service stoped @pause
卸载的bat:
@echo.delete service @echo off @sc delete LincTestServer @echo off @echo.service deleted @pause