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,只有将程序绑定在服务中,通过服务开启来运行才可以正常启动或调用其他程序。

405

被折叠的 条评论
为什么被折叠?



