您现在的位置是:首页 >其他 >C++以服务形式启动一个进程,需要关注的点网站首页其他
C++以服务形式启动一个进程,需要关注的点
简介C++以服务形式启动一个进程,需要关注的点
一、如何将进程以服务方式改写的代码,网上有很多教程,不再赘述。这里也附上一份代码可以参考:
二、改为服务启动后,可以用windows的sc命令去关闭、开启、删除、安装。
具体命令为:
sc create yourservicename binPath= D:sample.exe type= kernel // sample.exe是要启动的进程路径
sc start yourservicename //开启
sc stop yourservicename //停止
sc delete yourservicename //删除服务
也可以在任务管理器-服务界面 开启/停止服务。
三、下面来说说代码中因为改为服务启动需要修改的地方:
1. 代码中如果有SendMessage之类的API,在服务中不会生效,需要改成WTSSendMessageW。
原因是服务运行在session0 中,而所有包含界面的程序必须运行在用户会话中,而WTSSendMessageW这个API内部做了封装,突破了会话限制。
关于session 0 和 其他session的资料,可以参考这里:
2. 权限问题。
改为服务启动后,在任务管理器的进程列表看到的用户是system,而在服务打开其它的进程或者创建管道(大家的常用写法是把权限控制的参数填NULL)时会失败,需要做对应的降权处理。
具体的问题描述和解决方法,可以参考我另一篇文章:
3. 展示界面问题。
上面说到用WTSSendMessageW展示简单的提示信息,但如果是自己写的界面呢,如何在服务启动的方式下展示出来?
直接在服务的代码调用界面代码肯定是不行了,因为涉及到 session 隔离的问题,服务是无法展示界面的。这时候可以把界面代码封装成一个可以单独运行的exe,在服务里调用CreateProcessAsUser 函数,使用用户的session去创建进程,运行这个exe。
这种方法不仅支持在本机显示界面,还支持在远程PC显示界面。
相关代码如下:
#include <Iphlpapi.h>
#pragma comment(lib, "Iphlpapi.lib")
#include <wtsapi32.h>
#pragma comment(lib,"Wtsapi32.lib")
void policy::show_strategy_info(std::wstring message)
{
DWORD session_id = WTSGetActiveConsoleSessionId();
if (0xFFFFFFFF == session_id)
{
spdlog::warn("WTSGetActiveConsoleSessionId failed! sessionid:{}, {}", session_id, GetLastError());
return;
}
HANDLE user_token = nullptr;
if (!WTSQueryUserToken(session_id, &user_token))
{
spdlog::warn("WTSQueryUserToken(sessionid:{}) Failed! Error = {}", session_id, GetLastError());
DWORD dwSessionId = - 1;
DWORD dwCount = 0 ;
PWTS_SESSION_INFO pSessionInfo = NULL ;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
int dataSize = sizeof(WTS_SESSION_INFO);
for (DWORD i = 0; i < dwCount; ++i)
{
WTS_SESSION_INFO si = pSessionInfo[i];
if (WTSActive == si.State && session_id != si.SessionId)
{
// If the current session is active – store its ID
dwSessionId = si.SessionId;
spdlog::warn("WTSEnumerateSessions sessionId = {}", dwSessionId);
break;
}
}
if(!WTSQueryUserToken(dwSessionId, &user_token))
{
spdlog::warn("WTSQueryUserToken sessionId={}, ErrorCode = {}", dwSessionId, GetLastError());
CloseHandle(user_token);
user_token = nullptr;
return;
}
}
STARTUPINFOW startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(STARTUPINFOW));
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
startup_info.cb = sizeof(STARTUPINFOW);
//startup_info.lpDesktop = "WinSta0\Default";
startup_info.dwFlags = STARTF_USESHOWWINDOW;
startup_info.wShowWindow = SW_SHOWNORMAL;
DWORD creation_flags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
std::string temp_path = utils::current_path() + "xdrPop.exe";
std::wstring pop_exe_path = utils::ansi2unicode(temp_path);
BOOL result = CreateProcessAsUserW(user_token, pop_exe_path.c_str(), message.data(), NULL, NULL, FALSE, creation_flags, NULL, NULL, &startup_info, &process_info);
if (!result)
{
CloseHandle(user_token);
user_token = nullptr;
spdlog::warn("CreateProcessAsUser Failed! Error = {}", GetLastError());
return;
}
CloseHandle(user_token);
user_token = nullptr;
}
4. 注册表不能访问HKCU的问题。
HKCU是跟用户相关的,服务的session0 跟 用户的session是隔离的,自然无法访问HKCU。但是可以访问 HKLM。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。