如何使MFC编写的程序作为服务运行,并正常显示界面
2017-12-08 11:38
489 查看
Win7以及之后的windows版本下启动一个服务,在服务中创建一个带UI的进程
服务启动代码:
#pragma once
#pragma execution_character_set("utf-8")
#include "stdafx.h"
#include <winsvc.h>
#include "ManagerService.h"
#include "log.hpp"
#include <tlhelp32.h >
#include <Userenv.h>
#pragma comment(lib, "Userenv.lib")
#define FRONT_START 0 // 服务启动
#define FRONT_STOP 1 // 服务停止
#define FRONT_PAUSE 2 // 服务暂停
static SERVICE_STATUS g_ServiceStatus;
//服务的状态
static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
//
static PROCESS_INFORMATION g_ProcessInfo;
//要启动的进程信息
static HANDLE hEvents[4] = { 0 };
TCHAR g_strServiceName[MAX_PATH] = {0}; //服务的名称
VOID WINAPI ServiceMain(DWORD dwNumServicesArgs, LPWSTR *lpServiceArgVectors)
{
Log("ServiceMain begin\n");
// Register the control request handler
hEvents[0] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTSTART"));
hEvents[1] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTSTOP"));
hEvents[2] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTPAUSE"));
hEvents[3] = (HANDLE)0;
//注册控制Control Handler
g_ServiceStatusHandle = RegisterServiceCtrlHandler(g_strServiceName, ServiceCtrlHandler);
if (g_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
Log("Handler not installed\n");
return;
}
CoInitialize(0);
STARTUPINFO si;
// 设置服务状态为服务已经启动
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
DWORD dwRet = WAIT_FAILED;
int iFlag = FRONT_START;
while (1)
{
if ((iFlag == FRONT_START || iFlag == FRONT_PAUSE) && (dwRet == WAIT_OBJECT_0 + 1)) // 要求停止服务的事件发生
{
TerminateProcess(g_ProcessInfo.hProcess, 0);
CloseHandle(g_ProcessInfo.hProcess);
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
iFlag = FRONT_STOP;
Log("Front exit \n");
return;
}
if ((iFlag == FRONT_STOP || iFlag == FRONT_PAUSE) && (dwRet == WAIT_OBJECT_0)) // 要求启动服务的事件发生
{
iFlag = FRONT_START;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
Log("FRONT_START \n");
}
if ((iFlag == FRONT_START) && (dwRet == WAIT_OBJECT_0 + 2)) // 要求暂停服务的事件发生
{
TerminateProcess(g_ProcessInfo.hProcess, 0);
CloseHandle(g_ProcessInfo.hProcess);
g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
iFlag = FRONT_PAUSE;
Log("ServiceMain FRONT_PAUSE\n");
}
if (iFlag == FRONT_START) // 只在允许启动的情况下才去启动
{
if (dwRet == WAIT_OBJECT_0 + 3 || dwRet == WAIT_FAILED) // 进程意外退出或者还未启动
{
if (dwRet == WAIT_OBJECT_0 + 3)
{
Log(string("Front exit!!!").c_str());
}
// 记录进程退出日志
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpDesktop = _T("WinSta0\\Default");
si.lpTitle = NULL;
si.dwFlags = STARTF_USESHOWWINDOW;
// si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_SHOW;
si.cbReserved2 = NULL;
si.lpReserved2 = NULL;
TCHAR szPath[MAX_PATH];
//获取服务当前的路径
if (!GetModuleFileName(NULL, szPath, MAX_PATH))//if (!GetModuleFileName(_T(""), szPath, MAX_PATH))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "GetModuleFileName failed(%d)\n", err);
Log(data);
}
else
{
HANDLE hToken = NULL;
HANDLE hTokenDup = NULL;
bool bSuccess = TRUE;
do
{
//为了防止进程在后台启动,session隔离(在Vista 和 win7及之后的windows版本中有了session隔离)问题导致不能显示界面。被称为winsta0的Session才被允许与用户交互。因此,下文将切换Session,以便正常显示界面
//首先,获取进程访问令牌的句柄
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
{
//创建一个新的访问令牌来复制一个已经存在的标记
if (DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hTokenDup))
{
//获取当前活动的SessionId
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
//把服务hToken的SessionId替换成当前活动的Session(即替换到可与用户交互的winsta0下)
if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)))
{
char data[100] = { 0 };
sprintf(data, "SetTokenInformation error !error code:%d\n", GetLastError());
Log(data);
break;
}
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&g_ProcessInfo, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = _T("WinSta0\\Default");
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW /*|STARTF_USESTDHANDLES*/;
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
//创建进程环境块
if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE))
{
char data[100] = { 0 };
sprintf(data, "CreateEnvironmentBlock error !error code:%d\n", GetLastError());
Log(data);
break;
}
//在活动的Session下创建进程
if (!CreateProcessAsUser(hTokenDup, NULL, szPath, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &g_ProcessInfo))
{
char data[100] = { 0 };
sprintf(data, "CreateProcessAsUser error !error code:%d\n", GetLastError());
Log(data);
break;
}
DWORD dwExitCode;
hEvents[3] = g_ProcessInfo.hProcess;
CloseHandle(g_ProcessInfo.hThread);
if (hTokenDup != NULL && hTokenDup != INVALID_HANDLE_VALUE)
CloseHandle(hTokenDup);
//等待进程事件
dwRet = WaitForMultipleObjects(4, hEvents, FALSE, INFINITE);
char strDebug[100] = { 0 };
sprintf(strDebug, "WaitForMultipleObjects %d \n", dwRet);
Log(strDebug);
if ((WAIT_OBJECT_0 + 3) == dwRet) //停止
{
GetExitCodeProcess(g_ProcessInfo.hProcess, &dwExitCode);
CloseHandle(g_ProcessInfo.hProcess);
}
if (pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
}
else
{
char data[100] = { 0 };
sprintf(data, "DuplicateTokenEx error !error code:%d\n", GetLastError());
Log(data);
break;
}
if (hToken != NULL && hToken != INVALID_HANDLE_VALUE)
CloseHandle(hToken);
}
else
{
char data[100] = { 0 };
sprintf(data, "cannot get administror!error code:\n", GetLastError());
Log(data);
break;
}
} while (0);
Log("ServiceMain while (0)\n");
}
}
else if (dwRet == WAIT_TIMEOUT)
{
}
}
Sleep(100);
}
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
Log("ServiceMain End\n");
return;
}
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
char data[100] = { 0 };
sprintf(data, "ServiceCtrlHandler val(%d)\n", Opcode);
Log(data);
switch (Opcode)
{
case SERVICE_CONTROL_PAUSE:
g_ServiceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[2]);
break;
case SERVICE_CONTROL_CONTINUE:
g_ServiceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[0]);
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[1]);
break;
}
return;
}
BOOL InstallService()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if (!GetModuleFileName(NULL, szPath, MAX_PATH))//if (!GetModuleFileName(_T(""), szPath, MAX_PATH))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "GetModuleFileName failed(%d)\n", err);
Log(data);
return FALSE;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "OpenSCManager failed(%d)\n", err);
Log(data);
return FALSE;
}
// Create the service
Uninstall();
schService = CreateService(
schSCManager, // SCM database
g_strServiceName, // name of service
g_strServiceName, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "CreateService failed(%d)\n", err);
Log(data);
CloseServiceHandle(schSCManager);
return FALSE;
}
else
{
Log("Service installed successfully\n");
SERVICE_DELAYED_AUTO_START_INFO info = { TRUE };
if (!ChangeServiceConfig2(
schService, // handle to service
SERVICE_CONFIG_DELAYED_AUTO_START_INFO, // change: description
&info)) // new description
{
printf("ChangeServiceConfig2 failed\n");
}
if (StartService(schService, 0, NULL) == 0)
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
{
Log("already Running!\n");
}
else
{
char data[100] = { 0 };
sprintf(data, "StartService failed(%d)\n", err);
Log(data);
return FALSE;
}
}
else
{
Log("Pending ...\n");
}
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return true;
}
BOOL startService()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
SERVICE_STATUS ss;
if ((QueryServiceStatus(hService, &ss)) == 0)
{
Log("ServiceStatus unknow!\n");
}
if (StartService(hService, 0, NULL) == 0)
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
{
Log("already Running!\n");
}
else
{
char data[100] = { 0 };
sprintf(data, "StartService failed(%d)\n", err);
Log(data);
return bResult;
}
}
else
{
Log("Pending ...\n");
}
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
DWORD stopService()
{
// Variables
DWORD dwResult = 0;
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
do
{
// Connect to the SCM
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
dwResult = GetLastError();
break;
}
// Open the service
hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService == NULL)
{
dwResult = GetLastError();
break;
}
// Ask the service to stop
SERVICE_STATUS ss;
QueryServiceStatus(hService, &ss);
if (ss.dwCurrentState == SERVICE_RUNNING || ss.dwCurrentState == SERVICE_PAUSED)
{
::ControlService(hService, SERVICE_CONTROL_STOP, &ss);
}
Sleep(1000);
} while (0);
// Cleanup
if (hService != NULL)
::CloseServiceHandle(hService);
if (hSCM != NULL)
::CloseServiceHandle(hSCM);
// Return
return dwResult;
}
BOOL Uninstall()
{
if (!IsInstalled())
return TRUE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
Log("Couldn't open service manager\n");
return FALSE;
}
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService == NULL)
{
::CloseServiceHandle(hSCM);
Log("Couldn't open service\n");
return FALSE;
}
SERVICE_STATUS status;
BOOL bStop = ::ControlService(hService, SERVICE_CONTROL_STOP, &status);
//删除服务
BOOL bDelete = ::DeleteService(hService);
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
if (bDelete)
return TRUE;
return FALSE;
}
TCHAR* GetServiceName()
{
return g_strServiceName;
}
void Init()
{
// hEvents[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
g_ServiceStatusHandle = NULL;
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
char strService[100] = "XXX"; //服务名称为XXX
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, strService, -1, g_strServiceName, 100);
#else
strcpy(g_strServiceName, strService);
#endif
}
BOOL IsInstalled()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
BOOL isRunning()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
SERVICE_STATUS ss;
if (QueryServiceStatus(hService, &ss))
{
if (ss.dwCurrentState == SERVICE_RUNNING)
{
bResult = TRUE;
}
}
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
对话框启动代码:
// Test.cpp : 定义应用程序的类行为。
//
#include "stdafx.h"
#include "Test.h"
#include "TestDlg.h"
#include "InforDlg.h"
#include "ManagerService.h"
#include <winsvc.h>
#include <afxwin.h>
#include "log.hpp"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define PROC_ID _T("0xa9a66d98, 0x18c7, 0x447b, 0x80, 0xc, 0xa3, 0x20, 0xea, 0x4f, 0xb6, 0xe8")
HANDLE g_handle = (HANDLE)0;
// CTestApp
BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CTestApp构造
CTestApp::CTestApp()
{
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CTestApp对象
CTestApp theApp;
// CTestApp初始化
BOOL CTestApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
// CShellManager *pShellManager = new CShellManager;
// 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
// CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
//初始化服务
Init();
//服务未安装时,安装服务
if (!IsInstalled())
{
InstallService();
}
else
{
//用于判断窗口的唯一性
g_handle = ::CreateMutex(NULL, FALSE, PROC_ID);//handle为声明的HANDLE类型的全局变量
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
InforDlg dlg2;
m_pMainWnd = &dlg2;
dlg2.setLabelText(_T("实例已经在运行"));
dlg2.DoModal();
return FALSE;
}
CTestDlg dlg;
m_pMainWnd = &dlg;
TCHAR name[MAX_PATH] = { 0 };
_tcscpy(name, GetServiceName());
SERVICE_TABLE_ENTRY DispatchTable[] = { { name, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } };
//程序主线程连接到服务控制管理程序
if (!StartServiceCtrlDispatcher(DispatchTable))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "StartServiceCtrlDispatcher err(%d)\n", err);
Log(data);
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
Log("确定\n");
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
Log("取消\n");
}
else if (nResponse == -1)
{
int iRet = GetLastError();
char strErr[100] = { 0 };
sprintf(strErr, "Err:%d\n", iRet);
Log(strErr);
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
}
}
else
{
Log("StartServiceCtrlDispatcher sucess\n");
}
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
int CTestApp::ExitInstance()
{
if ((HANDLE)0 != g_handle)
{
CloseHandle(g_handle);
}
return CWinApp::ExitInstance();
}
服务启动代码:
#pragma once
#pragma execution_character_set("utf-8")
#include "stdafx.h"
#include <winsvc.h>
#include "ManagerService.h"
#include "log.hpp"
#include <tlhelp32.h >
#include <Userenv.h>
#pragma comment(lib, "Userenv.lib")
#define FRONT_START 0 // 服务启动
#define FRONT_STOP 1 // 服务停止
#define FRONT_PAUSE 2 // 服务暂停
static SERVICE_STATUS g_ServiceStatus;
//服务的状态
static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
//
static PROCESS_INFORMATION g_ProcessInfo;
//要启动的进程信息
static HANDLE hEvents[4] = { 0 };
TCHAR g_strServiceName[MAX_PATH] = {0}; //服务的名称
VOID WINAPI ServiceMain(DWORD dwNumServicesArgs, LPWSTR *lpServiceArgVectors)
{
Log("ServiceMain begin\n");
// Register the control request handler
hEvents[0] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTSTART"));
hEvents[1] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTSTOP"));
hEvents[2] = CreateEvent(NULL, FALSE, FALSE, _T("FRONTPAUSE"));
hEvents[3] = (HANDLE)0;
//注册控制Control Handler
g_ServiceStatusHandle = RegisterServiceCtrlHandler(g_strServiceName, ServiceCtrlHandler);
if (g_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
Log("Handler not installed\n");
return;
}
CoInitialize(0);
STARTUPINFO si;
// 设置服务状态为服务已经启动
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
DWORD dwRet = WAIT_FAILED;
int iFlag = FRONT_START;
while (1)
{
if ((iFlag == FRONT_START || iFlag == FRONT_PAUSE) && (dwRet == WAIT_OBJECT_0 + 1)) // 要求停止服务的事件发生
{
TerminateProcess(g_ProcessInfo.hProcess, 0);
CloseHandle(g_ProcessInfo.hProcess);
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
iFlag = FRONT_STOP;
Log("Front exit \n");
return;
}
if ((iFlag == FRONT_STOP || iFlag == FRONT_PAUSE) && (dwRet == WAIT_OBJECT_0)) // 要求启动服务的事件发生
{
iFlag = FRONT_START;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
Log("FRONT_START \n");
}
if ((iFlag == FRONT_START) && (dwRet == WAIT_OBJECT_0 + 2)) // 要求暂停服务的事件发生
{
TerminateProcess(g_ProcessInfo.hProcess, 0);
CloseHandle(g_ProcessInfo.hProcess);
g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
iFlag = FRONT_PAUSE;
Log("ServiceMain FRONT_PAUSE\n");
}
if (iFlag == FRONT_START) // 只在允许启动的情况下才去启动
{
if (dwRet == WAIT_OBJECT_0 + 3 || dwRet == WAIT_FAILED) // 进程意外退出或者还未启动
{
if (dwRet == WAIT_OBJECT_0 + 3)
{
Log(string("Front exit!!!").c_str());
}
// 记录进程退出日志
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpDesktop = _T("WinSta0\\Default");
si.lpTitle = NULL;
si.dwFlags = STARTF_USESHOWWINDOW;
// si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_SHOW;
si.cbReserved2 = NULL;
si.lpReserved2 = NULL;
TCHAR szPath[MAX_PATH];
//获取服务当前的路径
if (!GetModuleFileName(NULL, szPath, MAX_PATH))//if (!GetModuleFileName(_T(""), szPath, MAX_PATH))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "GetModuleFileName failed(%d)\n", err);
Log(data);
}
else
{
HANDLE hToken = NULL;
HANDLE hTokenDup = NULL;
bool bSuccess = TRUE;
do
{
//为了防止进程在后台启动,session隔离(在Vista 和 win7及之后的windows版本中有了session隔离)问题导致不能显示界面。被称为winsta0的Session才被允许与用户交互。因此,下文将切换Session,以便正常显示界面
//首先,获取进程访问令牌的句柄
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
{
//创建一个新的访问令牌来复制一个已经存在的标记
if (DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hTokenDup))
{
//获取当前活动的SessionId
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
//把服务hToken的SessionId替换成当前活动的Session(即替换到可与用户交互的winsta0下)
if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)))
{
char data[100] = { 0 };
sprintf(data, "SetTokenInformation error !error code:%d\n", GetLastError());
Log(data);
break;
}
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&g_ProcessInfo, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = _T("WinSta0\\Default");
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW /*|STARTF_USESTDHANDLES*/;
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
//创建进程环境块
if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE))
{
char data[100] = { 0 };
sprintf(data, "CreateEnvironmentBlock error !error code:%d\n", GetLastError());
Log(data);
break;
}
//在活动的Session下创建进程
if (!CreateProcessAsUser(hTokenDup, NULL, szPath, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &g_ProcessInfo))
{
char data[100] = { 0 };
sprintf(data, "CreateProcessAsUser error !error code:%d\n", GetLastError());
Log(data);
break;
}
DWORD dwExitCode;
hEvents[3] = g_ProcessInfo.hProcess;
CloseHandle(g_ProcessInfo.hThread);
if (hTokenDup != NULL && hTokenDup != INVALID_HANDLE_VALUE)
CloseHandle(hTokenDup);
//等待进程事件
dwRet = WaitForMultipleObjects(4, hEvents, FALSE, INFINITE);
char strDebug[100] = { 0 };
sprintf(strDebug, "WaitForMultipleObjects %d \n", dwRet);
Log(strDebug);
if ((WAIT_OBJECT_0 + 3) == dwRet) //停止
{
GetExitCodeProcess(g_ProcessInfo.hProcess, &dwExitCode);
CloseHandle(g_ProcessInfo.hProcess);
}
if (pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
}
else
{
char data[100] = { 0 };
sprintf(data, "DuplicateTokenEx error !error code:%d\n", GetLastError());
Log(data);
break;
}
if (hToken != NULL && hToken != INVALID_HANDLE_VALUE)
CloseHandle(hToken);
}
else
{
char data[100] = { 0 };
sprintf(data, "cannot get administror!error code:\n", GetLastError());
Log(data);
break;
}
} while (0);
Log("ServiceMain while (0)\n");
}
}
else if (dwRet == WAIT_TIMEOUT)
{
}
}
Sleep(100);
}
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
Log("ServiceMain End\n");
return;
}
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
char data[100] = { 0 };
sprintf(data, "ServiceCtrlHandler val(%d)\n", Opcode);
Log(data);
switch (Opcode)
{
case SERVICE_CONTROL_PAUSE:
g_ServiceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[2]);
break;
case SERVICE_CONTROL_CONTINUE:
g_ServiceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[0]);
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
SetEvent(hEvents[1]);
break;
}
return;
}
BOOL InstallService()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if (!GetModuleFileName(NULL, szPath, MAX_PATH))//if (!GetModuleFileName(_T(""), szPath, MAX_PATH))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "GetModuleFileName failed(%d)\n", err);
Log(data);
return FALSE;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "OpenSCManager failed(%d)\n", err);
Log(data);
return FALSE;
}
// Create the service
Uninstall();
schService = CreateService(
schSCManager, // SCM database
g_strServiceName, // name of service
g_strServiceName, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "CreateService failed(%d)\n", err);
Log(data);
CloseServiceHandle(schSCManager);
return FALSE;
}
else
{
Log("Service installed successfully\n");
SERVICE_DELAYED_AUTO_START_INFO info = { TRUE };
if (!ChangeServiceConfig2(
schService, // handle to service
SERVICE_CONFIG_DELAYED_AUTO_START_INFO, // change: description
&info)) // new description
{
printf("ChangeServiceConfig2 failed\n");
}
if (StartService(schService, 0, NULL) == 0)
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
{
Log("already Running!\n");
}
else
{
char data[100] = { 0 };
sprintf(data, "StartService failed(%d)\n", err);
Log(data);
return FALSE;
}
}
else
{
Log("Pending ...\n");
}
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return true;
}
BOOL startService()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
SERVICE_STATUS ss;
if ((QueryServiceStatus(hService, &ss)) == 0)
{
Log("ServiceStatus unknow!\n");
}
if (StartService(hService, 0, NULL) == 0)
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
{
Log("already Running!\n");
}
else
{
char data[100] = { 0 };
sprintf(data, "StartService failed(%d)\n", err);
Log(data);
return bResult;
}
}
else
{
Log("Pending ...\n");
}
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
DWORD stopService()
{
// Variables
DWORD dwResult = 0;
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
do
{
// Connect to the SCM
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
dwResult = GetLastError();
break;
}
// Open the service
hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService == NULL)
{
dwResult = GetLastError();
break;
}
// Ask the service to stop
SERVICE_STATUS ss;
QueryServiceStatus(hService, &ss);
if (ss.dwCurrentState == SERVICE_RUNNING || ss.dwCurrentState == SERVICE_PAUSED)
{
::ControlService(hService, SERVICE_CONTROL_STOP, &ss);
}
Sleep(1000);
} while (0);
// Cleanup
if (hService != NULL)
::CloseServiceHandle(hService);
if (hSCM != NULL)
::CloseServiceHandle(hSCM);
// Return
return dwResult;
}
BOOL Uninstall()
{
if (!IsInstalled())
return TRUE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
Log("Couldn't open service manager\n");
return FALSE;
}
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService == NULL)
{
::CloseServiceHandle(hSCM);
Log("Couldn't open service\n");
return FALSE;
}
SERVICE_STATUS status;
BOOL bStop = ::ControlService(hService, SERVICE_CONTROL_STOP, &status);
//删除服务
BOOL bDelete = ::DeleteService(hService);
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
if (bDelete)
return TRUE;
return FALSE;
}
TCHAR* GetServiceName()
{
return g_strServiceName;
}
void Init()
{
// hEvents[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
g_ServiceStatusHandle = NULL;
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
char strService[100] = "XXX"; //服务名称为XXX
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, strService, -1, g_strServiceName, 100);
#else
strcpy(g_strServiceName, strService);
#endif
}
BOOL IsInstalled()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
BOOL isRunning()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = ::OpenService(hSCM, g_strServiceName, SERVICE_ALL_ACCESS | DELETE);
if (hService != NULL)
{
SERVICE_STATUS ss;
if (QueryServiceStatus(hService, &ss))
{
if (ss.dwCurrentState == SERVICE_RUNNING)
{
bResult = TRUE;
}
}
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
对话框启动代码:
// Test.cpp : 定义应用程序的类行为。
//
#include "stdafx.h"
#include "Test.h"
#include "TestDlg.h"
#include "InforDlg.h"
#include "ManagerService.h"
#include <winsvc.h>
#include <afxwin.h>
#include "log.hpp"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define PROC_ID _T("0xa9a66d98, 0x18c7, 0x447b, 0x80, 0xc, 0xa3, 0x20, 0xea, 0x4f, 0xb6, 0xe8")
HANDLE g_handle = (HANDLE)0;
// CTestApp
BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CTestApp构造
CTestApp::CTestApp()
{
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CTestApp对象
CTestApp theApp;
// CTestApp初始化
BOOL CTestApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
// CShellManager *pShellManager = new CShellManager;
// 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
// CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
//初始化服务
Init();
//服务未安装时,安装服务
if (!IsInstalled())
{
InstallService();
}
else
{
//用于判断窗口的唯一性
g_handle = ::CreateMutex(NULL, FALSE, PROC_ID);//handle为声明的HANDLE类型的全局变量
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
InforDlg dlg2;
m_pMainWnd = &dlg2;
dlg2.setLabelText(_T("实例已经在运行"));
dlg2.DoModal();
return FALSE;
}
CTestDlg dlg;
m_pMainWnd = &dlg;
TCHAR name[MAX_PATH] = { 0 };
_tcscpy(name, GetServiceName());
SERVICE_TABLE_ENTRY DispatchTable[] = { { name, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } };
//程序主线程连接到服务控制管理程序
if (!StartServiceCtrlDispatcher(DispatchTable))
{
int err = GetLastError();
char data[100] = { 0 };
sprintf(data, "StartServiceCtrlDispatcher err(%d)\n", err);
Log(data);
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
Log("确定\n");
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
Log("取消\n");
}
else if (nResponse == -1)
{
int iRet = GetLastError();
char strErr[100] = { 0 };
sprintf(strErr, "Err:%d\n", iRet);
Log(strErr);
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
}
}
else
{
Log("StartServiceCtrlDispatcher sucess\n");
}
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
int CTestApp::ExitInstance()
{
if ((HANDLE)0 != g_handle)
{
CloseHandle(g_handle);
}
return CWinApp::ExitInstance();
}
相关文章推荐
- MFC程序中将软件打包后,setup.exe文件自己电脑运行正常,别人电脑显示找不到文件路径
- 如何让一个程序作为服务运行
- 如何让一个程序作为服务运行
- 如何让一个程序作为服务运行
- 用MFC编写的程序在别人电脑上显示时窗口界面出现问题
- Delphi编写系统服务四:如何限制系统服务和桌面程序只运行一个
- MFC窗体程序作为服务运行注销登录后不能关机的问题解决
- exe程序作为WINDOWS服务显示界面启动方法
- Delphi编写系统服务四:如何限制系统服务和桌面程序只运行一个
- mfc编写的程序如何在别人机器上运行?
- linux/Ubuntu下Qt creater 界面程序运行时无法正常显示中文的解决
- mfc编写的程序如何在别人机器上运行?
- Ubuntu下Qt creater 界面程序运行时控件中无法正常显示中文
- 如何使用MFC编写自定义UI界面【附高仿QQ 2014登陆界面范例程序】
- 1, 编写程序,当用户在文本框中输入内容之后,单机不同的按钮,能够把文半框中的内容粘贴到文本区中。“重置”按钮实现将文本框和文本区中的内容清空。界面上的文本区只能显示内容,不能让用户输入文本。运行结果
- 杂谈_如何让VS2013开发的MFC程序运行在XP系统下
- 让Java程序作为linux的Daemon后台运行 和 使用Java Service Wrapper将java程序作为linux服务并且开机自动启动
- 如何让windows控制台程序运行时不显示黑色框,直接运行
- 如何编写windows服务程序
- 编写一个学生和教师数据输入和显示程序,学生数据有编号,姓名,班号,和成绩,教师数据有编号,姓名,职称和部门。要求将编号,姓名输入显示设计成一个类person,并作为学生数据类t和教师数据操作类的基类