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

时间:2022-08-29 08:02:11

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服务与安装