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的资料,可以参考这里:

session 0 和其它session 区别

2. 权限问题。

改为服务启动后,在任务管理器的进程列表看到的用户是system,而在服务打开其它的进程或者创建管道(大家的常用写法是把权限控制的参数填NULL)时会失败,需要做对应的降权处理。

具体的问题描述和解决方法,可以参考我另一篇文章:

给管道设置安全描述符,实现与chrome跨进程通信

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。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
启动一个有界面交互功能的程序,可以使用ShellExecute函数或CreateProcess函数来实现。 使用ShellExecute函数可以调用已经注册在系统中的默认程序打开文件或者URL。示例代码如下: ```c++ ShellExecute(NULL,"open","example.exe",NULL,NULL,SW_SHOW); ``` 其中,第一个参数是父窗口的句柄,如果没有可以设置为NULL;第二个参数是操作类型,一般为“open”;第三个参数是要执行的程序的路径和名称;第四个参数是要传递给程序的参数,一般为NULL;第五个参数是工作目录,一般为NULL;第六个参数是窗口显示方式,一般为SW_SHOW。 使用CreateProcess函数可以启动一个新的进程,示例代码如下: ```c++ STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si,sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi,sizeof(pi)); BOOL bCreate = CreateProcess("example.exe",NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); if(bCreate) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } ``` 其中,第一个参数是要执行的程序的路径和名称;第二个参数是要传递给程序的参数,一般为NULL;第三个参数是进程的安全描述符,一般为NULL;第四个参数是线程的安全描述符,一般为NULL;第五个参数是指定新进程是否从调用进程继承其句柄,一般为FALSE;第六个参数是指定新进程的优先级,一般为0;第七个参数是指定新进程的环境变量,一般为NULL;第八个参数是指定新进程的工作目录,一般为NULL;第九个参数是指向STARTUPINFO结构的指针,一般为&si;第十个参数是指向PROCESS_INFORMATION结构的指针,一般为&pi。 以上是两种启动有界面交互功能的程序的方法,可以根据具体需求选择使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值