您的位置:首页 > 其它

如何使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();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐