天天看點

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服務與安裝