天天看点

Windows 编程中用Service开启一个外部进程的两种语言实现方式(C++,C#)。

Windows 编程中用Service开启一个外部进程的两种语言实现方式(C++,C#)。

C++实现

新建一个win32项目,主函数方法:

#pragma region Includes
#include "stdafx.h"
#include <string.h>  
#include <stdio.h>  
#include <Windows.h>  
#include <iostream>
#include<fstream>
#include "start_gui.h"
#pragma endregion

#define SERVICE_NAME TEXT("srv_demo")  
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hServiceStatusHandle;
void WINAPI service_main(int argc, char** argv);
void WINAPI ServiceHandler(DWORD fdwControl);

TCHAR szSvcName[80];
SC_HANDLE schSCManager;
SC_HANDLE schService;

DWORD WINAPI srv_core_thread(LPVOID para)
{
    std::wstring bin_path_=L"D:\\a.exe";    
    StartGUIProc obj(bin_path_);
    obj.Run();

    return NULL;
}


void WINAPI ServiceHandler(DWORD fdwControl)
{
    switch (fdwControl)
    {
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
        uaquit = 1;
        //add you quit code here

        break;
    default:
        return;
    };
    if (!SetServiceStatus(hServiceStatusHandle, &ServiceStatus))
    {
        DWORD nError = GetLastError();
    }
}


void WINAPI service_main(int argc, char** argv)
{
    ServiceStatus.dwServiceType = SERVICE_WIN32;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
    hServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceHandler);
    if (hServiceStatusHandle == 0)
    {
        DWORD nError = GetLastError();
    }
    //add your init code here

    //add your service thread here
    HANDLE task_handle = CreateThread(NULL, NULL, srv_core_thread, NULL, NULL, NULL);

    if (task_handle == NULL)
    {
        //fprintf(log,"create srv_core_thread failed\n");
    }   
    // Initialization complete - report running status 
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 9000;
    if (!SetServiceStatus(hServiceStatusHandle, &ServiceStatus))
    {
        DWORD nError = GetLastError();
    }

}
//do not change main function
int main(int argc, const char *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[2];

    ServiceTable[0].lpServiceName = SERVICE_NAME;
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)service_main;

    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;
    // 启动服务的控制分派机线程
    StartServiceCtrlDispatcher(ServiceTable);
    return 0;
}
           

开启进程的类:

#include "stdafx.h"
#include "start_gui.h"
#include <windows.h>
#include "WtsApi32.h"
#include "UserEnv.h"
#include <iostream>
#include <string>
#include <tchar.h>

#pragma comment(lib, "Wtsapi32.Lib")
#pragma comment(lib, "Userenv.Lib")
StartGUIProc::StartGUIProc(const std::wstring& processPath, const std::wstring& arguments)
: processPath_(processPath), arguments_(arguments)
{
}

HANDLE StartGUIProc::GetCurrentUserToken()
{
    PWTS_SESSION_INFO pSessionInfo = 0;
    DWORD dwCount = 0;
    ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
    int session_id = 0;
    for (DWORD i = 0; i < dwCount; ++i)
    {
        WTS_SESSION_INFO si = pSessionInfo[i];
        if (WTSActive == si.State)
        {
            session_id = si.SessionId;
            break;
        }
    }
    ::WTSFreeMemory(pSessionInfo);
    HANDLE current_token = 0;
    BOOL bRet = ::WTSQueryUserToken(session_id, &current_token);
    int errorcode = GetLastError();
    if (bRet == false)
    {
        return 0;
    }
    HANDLE primaryToken = 0;
    bRet = ::DuplicateTokenEx(current_token, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &primaryToken);
    errorcode = GetLastError();
    if (bRet == false)
    {
        return 0;
    }
    return primaryToken;
}

BOOL StartGUIProc::Run()
{
    HANDLE primaryToken = GetCurrentUserToken();
    if (primaryToken == 0)
    {
        return FALSE;
    }
    STARTUPINFO StartupInfo = { 0 };
    PROCESS_INFORMATION processInfo;
    StartupInfo.cb = sizeof(STARTUPINFO);

    std::wstring command = L"\"" + processPath_ + L"\"";
    if (arguments_.length() != 0)
    {
        command += L" " + arguments_;
    }
    void* lpEnvironment = NULL;
    BOOL resultEnv = ::CreateEnvironmentBlock(&lpEnvironment, primaryToken, FALSE);
    if (resultEnv == 0)
    {
        long nError = GetLastError();
    }
    BOOL result = ::CreateProcessAsUser(primaryToken, 0, (LPWSTR)(command.c_str()), NULL, NULL, FALSE, CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, NULL, 0, &StartupInfo, &processInfo);
    ::DestroyEnvironmentBlock(lpEnvironment);
    ::CloseHandle(primaryToken);
    return result;
}
           

C#实现:项目需要引用Cjwdev.WindowsApi.dll

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Win32;

namespace ServiceTest
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {             
            Tools.StartApp(@"C:\Users\Administrator\Desktop\a.exe");
        }

        protected override void OnStop()
        {

        }          
    }
}
           

开启进程的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Win32;
using Cjwdev;
using Cjwdev.WindowsApi;
using System.Runtime.InteropServices;

namespace Servicetest
{
    class Tools
    {
        public static void StartApp(string strAppPath)
        {
            try
            {
                string appStartPath = strAppPath;
                IntPtr userTokenHandle = IntPtr.Zero;
                ApiDefinitions.WTSQueryUserToken(ApiDefinitions.WTSGetActiveConsoleSessionId(), ref userTokenHandle);

                ApiDefinitions.PROCESS_INFORMATION procInfo = new ApiDefinitions.PROCESS_INFORMATION();
                ApiDefinitions.STARTUPINFO startInfo = new ApiDefinitions.STARTUPINFO();
                startInfo.cb = (uint)Marshal.SizeOf(startInfo);

                ApiDefinitions.CreateProcessAsUser(
                    userTokenHandle,
                    appStartPath,
                    "",
                    IntPtr.Zero,
                    IntPtr.Zero,
                    false,
                    0,
                    IntPtr.Zero,
                    null,
                    ref startInfo,
                    out procInfo);

                if (userTokenHandle != IntPtr.Zero)
                    ApiDefinitions.CloseHandle(userTokenHandle);

                int _currentAquariusProcessId = (int)procInfo.dwProcessId;
            }
            catch (Exception ex)
            {

            }  
        }   
    }
}
           

!!!特别注意:如果需要打开的外部程序引用了其他动态库,请将生成的服务.exe放在与需要打开的程序及动态库在同级目录下,然后安装服务,否则Service不能打开外部程序。

至于Windows如何创建一个Service,安装卸载服务以及打开外部程序的其他实现请参考以下链接,同时感谢以下文章的作者:

1. 【转】用C/C++创建windows服务程序

2. 【转】 解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离

3. CreateProcessAsUser,C#写的windows服务弹框提示消息或者启动子进程

4. C#创建Windows服务与安装