Win32工程转为服务并显示程序界面

4 篇文章 0 订阅

1.使用NSSM工具转换

官网地址:NSSM下载地址
下载工具,根据系统位数选择64位或32位
在这里插入图片描述
管理员权限打开CMD命令行工具,切换到nssm.exe所在路径,输入指令 nssm install,即可自动打开NSSM配置界面,在界面中输入服务需要运行的程序。
在这里插入图片描述

配置好控制的程序,若需要显示程序界面,在Log on界面中选择Allow service to interact with desktop,最后在界面下边输入服务名称,点击Install service按钮。
在这里插入图片描述
在这里插入图片描述

配置项说明:

  • Path:运行应用程序的程序
  • Startup directory:应用程序所在的目录
  • Arguments:应用运行的参数
  • Service name:生成服务的名称

最后点击install service 完成windows服务安装,在windows服务列表就能看到创建的服务了。

常用命令

  • nssm install servername //创建servername服务,弹出配置界面
  • nssm start servername //启动服务
  • nssm stop servername //暂停服务
  • nssm restart servername //重新启动服务
  • nssm remove servername //删除创建的servername服务
  • nssm edit servername//更改servername服务,弹出修改界面
  • nssm set servername 参数名 参数值 //设置服务参数值
  • sc delete servername//windows删除服务命令

使用这些命令来启动、停止、删除服务等操作。

需要注意的是,虽然配置的时候添加了与桌面交互的选项,但实际测试时并没有正常出现程序窗口界面,这和我们开启程序的方式有关。

2 在服务中开启程序并显示界面

由于服务开启的进程都在服务会话中,属于后台进程,而我们直接打开程序的进程是在用户会话中。不同的账号登陆会有不同的用户会话ID。因此若直接在服务程序中启动另外的程序,则默认还是在服务会话中,打开的程序只能在任务管理器的后台进程中看到.exe但看不到程序窗口界面,不能和用户会话的程序进行交互。
为了解决该问题,需要改变开启程序的方式,让需要显示的程序的Session ID(会话ID)保持与当前用户的会话ID一致才能正常显示程序。调用CreateProcessAsUser函数来开启程序,使用的代码如下,传入要开启的软件绝对路径即可。
使用下面代码需要引入头文件 #include <UserEnv.h>
且需要用到对应的lib文件,可以在引入头文件之后找到文件所在目录,找到对应目录下的UserEnv.lib引入到工程中来使用,注意lib文件的版本要与工程实际编译环境对应。

bool StartProcess(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;
}

下面是我在项目中使用的头文件和库文件目录,我工程为64位编译环境。
在这里插入图片描述

另外在测试时发现,直接在工程上运行服务绑定的程序时打开另外的程序会失败,因为上面代码中有一个操作是要“把服务hToken的SessionId替换成当前活动的Session(即替换到可与用户交互的winsta0下)”,若直接运行工程是获取不到服务的SssionID,只有将程序绑定在服务中,通过服务开启来运行才可以正常启动或调用其他程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值