因为项目需要,做一个Tech Spike,看看用QT如何创建一个Windows Service,并实现触摸某硬件而弹出某应用程序的功能。
为实现“触摸某硬件而弹出某应用程序”,首先想到的是,这个触摸动作触发了一个特定的signal,而QObject的connect()函数就将这个signal与加载应用程序的动作连接起来,这样就实现了此功能。而这一切又是实现在Windows的service里面的。于是一切看起来就很顺畅自然了。现在,我们来看看如何用QT创建一个Windows的Service吧。
本来,创建一个Windows的Service是一件挺复杂的事情。如果QT这种跨平台的类库已经实现了这一功能,岂不是省去了开发者很多麻烦?!笔者也不能算QT专家,于是少不了Google一把。结论是:QT本身并不支持创建Windows的Service或Unix的daemon,但是有第三方的QT库支持!这个库就是:https://github.com/qtproject/qt-solutions/tree/master/qtservice
那么这个库是啥License呢?没有找到专门描述License的文件,但从所有源代码文件来看,应该是BSD License. 而在README.txt中又提到了LGPL. 笔者去了解个大概:BSD是一种比较宽松的License,而LGPL要求使用者只要不修改其源代码就可以用于商业产品。OK!可以用了!
怎么使用呢?有2种方式:一是编译成dll后使用,二是直接使用源码再配合自己的代码进行编译。好像是废话。。。
具体咋用呢?看其自带的examples:在examples这个目录下,提供了3个示例。第一个是controller,笔者没有细看,大致是写了一个类似于Windows的sc.exe的功能吧;第二个是interactive service,这个笔者简单编译运行了一下,运行时崩溃了,因为开始认为和笔者的目标不太吻合,就没有细究原因,但是到最后回头来看,发现却是和最后一个大坑一样的原因,这个暂且按下不表;最后一个示例就是一个一般的service,笔者略作了一下研究,发现其实它只有一个main.cpp文件,里面有一个service类和一个干活的类,而两个类基本都要实现start(或run)方法、pause、resume、terminate等方法。所以,开发者只要把这个main.cpp稍加修改,就是用QT创建自己的Windows Service了。
具体操作Service的方法如下:
1. 打开一个Administrator级别的Terminal窗口,注意,必须是Administrator权限的;
2. 假设编译出的可执行文件叫做XXX.exe, 在其目录下运行:XXX.exe -i 这是代表安装了XXX.exe作为Windows的服务了。此时你可以按Windows键+R键,再运行services.msc来查看这个新安装的服务。
注意:直接用-i选项安装的是一个"LocalSystem"帐号的服务,而用 -i account password 选项则是安装的一个当前用户帐号的服务。
3. 继续运行:XXX.exe -s 这是表示启动了这个Service. 此时,在系统的Service查看窗口按F5刷新,可以看到这个服务的运行状态已经变成Running了。
4. 此后,可以用-p、-r、-t、-u选项分别来暂停、继续、停止和卸载这个服务。而-e选项是如普通程序般运行此exe程序,即不以Service来运行,这是为了Debug方便。
以上就是用QT创建和运行Windows Service的介绍了。
#ifdef Q_OS_WIN
#include
#include
#include
#include
#include
BOOL launchGUIApplication(std::wstring app, std::vectorparams)
{
BOOL bResult = FALSE;
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
if (dwSessionId == 0xFFFFFFFF)
{
return FALSE;
}
HANDLE hUserToken = NULL;
if (WTSQueryUserToken(dwSessionId, &hUserToken) == FALSE)
{
return FALSE;
}
HANDLE hTheToken = NULL;
if (DuplicateTokenEx(hUserToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &hTheToken) == TRUE)
{
if (ImpersonateLoggedOnUser(hTheToken) == TRUE)
{
DWORD dwCreationFlags = HIGH_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES Security1 = { sizeof(Security1) };
SECURITY_ATTRIBUTES Security2 = { sizeof(Security2) };
LPVOID pEnv = NULL;
if (CreateEnvironmentBlock(&pEnv, hTheToken, TRUE) == TRUE)
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
TCHAR path[MAX_PATH];
_tcscpy_s(path, MAX_PATH, app.c_str());
TCHAR commandLine[MAX_PATH];
_tcscpy_s(commandLine, MAX_PATH, L" ");
for (auto item : params) {
_tcscat_s(commandLine, MAX_PATH, item.c_str());
}
// Launch the process in the client's logon session.
bResult = CreateProcessAsUser(
hTheToken,
(LPWSTR)(path),
(LPWSTR)(commandLine),
&Security1,
&Security2,
FALSE,
dwCreationFlags,
pEnv,
NULL,
&si,
&pi
);
RevertToSelf();
if (pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
}
CloseHandle(hTheToken);
}
CloseHandle(hUserToken);
return bResult;
}
#endif
#ifdef Q_OS_WIN
std::wstring app = L"notepad.exe";
std::vector params = {};
if (launchGUIApplication(app, params) == FALSE) {
qDebug() << "Failed to launch " << app.c_str();
}
#endif