系统服务用CreateProcessAsUser实现创建界面进程

一、需求

       有一个带界面的GUI程序需要一直保持运行状态,但是操作系统有时会自动更新重启,虽然已经通过注册表的方式设置为自启动,但是必须登录操纵系统才能启动,所以需要一种不登录也能让该程序运行的实现方法。

       又因为这个程序是GUI程序,无法直接注册为系统服务(系统服务可以开机后不登录也在后台运行),所以考虑通过把一个控制台程序注册成系统服务,然后通过这个控制台程序调用GUI程序。下面代码是控制台程序调用GUI程序的关键逻辑。

       经测试,在Win10 和 windows server 2016 都正常启用GUI程序。但是在云服务器有点特殊,使用下面代码调用GUI程序后,分配到的Session ID (会话ID,每个用户登录操作系统后都会被分配一个会话ID) 是1,但是我们用云平台给的用户名密码远程登录操作系统后的Session ID是2,这就导致我们看不到GUI程序的界面。在本地的虚拟机和实体机则正常。

 

2、代码



bool CStartUIProcess::Start(const string& strAppPath)
{
	HANDLE hToken = NULL;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
	{
		return false;
	}

	HANDLE hTokenDup = NULL;
	bool bRet = DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
	if (!bRet || hTokenDup == NULL)
	{
		CloseHandle(hToken);
		return false;
	}

	DWORD dwSessionId = WTSGetActiveConsoleSessionId();
	//把服务hToken的SessionId替换成当前活动的Session(即替换到可与用户交互的winsta0下)
	if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)))
	{
		DWORD nErr = GetLastError();
		CloseHandle(hTokenDup);
		CloseHandle(hToken);
		return false;
	}

	STARTUPINFO si;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = _T("WinSta0\\Default");
	si.wShowWindow = SW_SHOW;
	si.dwFlags = STARTF_USESHOWWINDOW /*|STARTF_USESTDHANDLES*/;

	//创建进程环境块
	LPVOID pEnv = NULL;
	bRet = CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE);
	if (!bRet)
	{
		CloseHandle(hTokenDup);
		CloseHandle(hToken);
		return false;
	}

	if (pEnv == NULL)
	{
		CloseHandle(hTokenDup);
		CloseHandle(hToken);
		return false;
	}

	//在活动的Session下创建进程
	PROCESS_INFORMATION processInfo;
	ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
	DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;

	if (!CreateProcessAsUser(hTokenDup, NULL, (char*)strAppPath.c_str(), NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &processInfo))
	{
		DWORD nRet = GetLastError();
		CloseHandle(hTokenDup);
		CloseHandle(hToken);
		return false;
	}

	DestroyEnvironmentBlock(pEnv);
	CloseHandle(hTokenDup);
	CloseHandle(hToken);

	return true;
}

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现Windows后台服务可以使用Windows API中的Windows Service Control Manager(SCM),具体步骤如下: 1. 创建Windows服务: 在C++中,可以使用WinAPI中的CreateService函数来创建Windows服务。这个函数需要传递一些参数,例如服务名称、服务描述、服务类型等。在创建服务时,需要指定服务程序的路径和名称,以及服务程序启动的方式,例如自动、手动、禁用等。 2. 实现服务入口函数: 服务入口函数是Windows服务的核心,所有的服务操作都在服务入口函数中实现。在C++中,可以使用WinAPI中的ServiceMain函数作为服务入口函数,该函数会在服务被启动时自动调用。 3. 定义服务处理程序: 服务处理程序是服务入口函数的一部分,它用于处理服务请求。在C++中,可以使用WinAPI中的HandlerEx函数作为服务处理程序,该函数会在服务请求时自动调用。 4. 实现对多个进程的监控: 可以使用WinAPI中的EnumProcesses函数来获取系统中所有正在运行的进程的ID,然后使用OpenProcess函数打开进程句柄,使用GetExitCodeProcess函数获取进程退出码,如果发现某个进程已经退出,则可以使用CreateProcessAsUser函数来重启该进程。 5. 实现重启打开界面: 可以使用CreateProcessAsUser函数来启动需要打开的界面程序,该函数可以指定进程的启动路径、命令行参数、工作目录等信息,以及使用CreateProcessWithLogonW函数以指定用户的身份来启动进程。 需要注意的是,Windows服务是在系统后台运行的,无法直接和用户交互。如果需要在服务中打开界面,可以考虑创建一个用户界面程序,并在服务中调用该程序来实现界面的打开和关闭。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值