【写在前面】
在很多软件中,程序通常需要这样一个状态:只有一个实例运行中。
而另一些特殊的应用程序,它们又需要长期运行。
然而,在 Qt 提供的库中,似乎没有找到相关的。
实际上,Qt 其实提供了一些解决方法,如:QtSingleApplication 和 QtService。
不过要注意的是,它们并没有被包含在官方库中。
【正文开始】
QtSingleApplication 是 Qt 提供的,用于创建单应用程序的库。
首先,我们需要构建库本身:
-
打开 buildlib/buildlib.pro ,其中的:
DLLDESTDIR = $$[QT_INSTALL_BINS]
可以注释掉,它是 dll 目录,若生成 dll 则会将 dll 拷贝一份到 [ Qt 安装目录下 ]。
当然,不注释也没有任何问题。
-
输出目录由 common.pri 中的 QTSERICE_LIBDIR 控制,我将其改为了:
QTSINGLEAPPLICATION_LIBDIR = $$PWD/../lib
PS:可以根据需要自行修改。
-
最后直接构建就OK。
当然,由于是 QWidgets 版本的,所以在 Qml 中使用则需要我们进行一点小小的改动。
具体是:QApplication 改为 QGuiApplication,QWidget 改为 QWindow,activeWindow() 改为 requestActive()。
而使用则很简单:
#include <QtSingleApplication>
#include <QQmlApplicationEngine>
#include <QWindow>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QString appId = "SingleApp_Test";
QtSingleApplication singleApp(appId, argc, argv);
if (singleApp.isRunning()) {
singleApp.sendMessage("raise window.");
return EXIT_SUCCESS;
}
QWindow *mainWindow = nullptr;
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&singleApp, [&mainWindow, url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);;
//注意,这里因为main.qml为Window,所以直接转了。
mainWindow = qobject_cast<QWindow *>(obj);
}, Qt::QueuedConnection);
engine.load(url);
singleApp.setActivationWindow(mainWindow);
QObject::connect(&singleApp, &QtSingleApplication::messageReceived,
&singleApp, &QtSingleApplication::activateWindow);
return singleApp.exec();
}
1、类似 QApplicatioin / QGuiApplication ,首先创建一个 QtSingleApplication,第一个参数是程序标识字符串,可以没有。
2、判断是否已经运行,如果运行中则激活窗口,注意,在最后几行,messageReceived() 信号需要连接到 activateWindow(),因为其实质上是一种本地通信。
3、通过 setActiveWindow() 来指定需要激活的窗口。
QtService 是Qt 提供的,用于创建服务程序的库。
它在 Windows 上为服务,在 Linux 则为守护进程。
构建过程类似 QtSingelApplication,就不再赘述了。
这里就简单讲下使用方法:
class Service : public QtService<QCoreApplication>
{
public:
Service(int argc, char **argv);
protected:
void start();
void pause();
void resume();
void processCommand(int code);
}
1、继承 QtService,模板参数为 QCoreApplication / QApplication / QGuiApplication 都可。
2、自由实现自己的 start() / pause() / resume() 等等函数。
Service::Service(int argc, char **argv)
: QtService<QCoreApplication>(argc, argv, "My Service")
{
setServiceDescription(QString::fromLocal8Bit("Test Service"));
setServiceFlags(QtServiceBase::CanBeSuspended);
setStartupType(QtServiceController::AutoStartup);
}
3、自由设置服务的属性,包括:setServiceDescription() 设置描述文本, setServiceFlags() 设置标志( 这里是可以被暂停 ),setStartupType() 设置启动类型。
4、main 中只需 exec() 即可:
int main(int argc, char *argv[])
{
Service service(argc, argv);
return service.exec();
}
和普通程序不同,服务程序在 Windows 下可以通过 [sc 命令] 使用 ( 需管理员权限 ):
例如,使用 [sc create] 来创建我们服务,当然,更具体的用法使用 [sc -?] 查看帮助。
用起来似乎有些麻烦,不过好在 Qt 还提供了 QtServiceController,即:服务控制器。
如它的字面意思,QtServiceController 用于控制服务的「 安装、启动、暂停、继续、停止以及向其发送命令 」。
直接来看代码:
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QtServiceController controller("My Service");
if (controller.isInstalled()) qDebug() << "My service is installed";
else {
qDebug() << "My service isn't install";
controller.install(app.applicationDirPath() + "/MyService.exe");
}
if (controller.isRunning()) qDebug() << "My service is running";
else {
qDebug() << "My service isn't running";
controller.start();
}
return app.exec();
}
1、初始化一个 QtServiceController,参数为服务名称。
2、isInstalled() 判断服务是否已经安装,isRunning() 判断是否在运行。
3、ServiceController 提供 install() / start() / pause() / resume() / stop() / sendCommand() 等函数来完成服务的「 安装、启动、暂停、继续、停止以及向其发送命令 」。
4、其中 install() 为:
static bool install(const QString &serviceFilePath, const QString &account = QString(),
const QString &password = QString());
注意,需要管理员权限才能完成这些操作,即:以管理员身份运行程序。
最后,可以看到服务已经成功启动:
【结语】
呼~总算写完了,休息会_(:3 」∠)_ 。
实际上,Qt 不仅提供 QtSingleApplication 和 QtService,同样未加入官方库的还有:
[ qtbrowserplugin ] [ qtlockedfile ] [ qtpropertybrowser ] [ qtscriptclassic ] [ qtsoap ] [ qtwinmigrate ]。
然而因为自己只用过 QtSingleApplication 和 QtService,所以就没有写其它的。
另外,每个项目都提供了 example,因此,只要自己多试试就可以很快掌握。
最后,它们的源码可以从 https://code.qt.io/cgit/qt-solutions/qt-solutions.git/tree/ 得到。