最近需要实现一个功能,在启动第二个程序实例时将主界面窗口置顶显示。界面程序是基于Qt开发的,在Windows操作系统上实现窗口置顶需要一些特殊的处理。

使用Qt库的方案

可以使用Qt库的 QWidget::raise() 这个接口来置顶窗口。但是在Windows系统上,将窗口置顶显示这个操作是受限制的,在后台运行的程序并不能随意将自己的窗口置于顶层。需要借助 AllowSetForegroundWindow() 接口将置顶窗口权限移交至后台程序,后台程序才允许将自己的窗口置于顶层。

下面的例子采用了第三方库SingleApplication来实现实例之间的交互。

#include <QWidget>

#include "singleapplication.h"

#ifdef Q_OS_WINDOWS
#include <Windows.h>
#endif

void raiseWidget(QWidget* widget);

int main(int argc, char *argv[]) {

#ifdef Q_OS_WINDOWS
    SingleApplication app(argc, argv, true);

    if (app.isSecondary()) {

	AllowSetForegroundWindow( DWORD( app.primaryPid() ) );

	app.sendMessage("RAISE_WIDGET");

	return 0;
    }
#else
    SingleApplication app(argc, argv);
#endif

    QWidget* widget = new QWidget;

#ifdef Q_OS_WINDOWS
    QObject::connect(&app, &SingleApplication::receivedMessage,
		     widget, [widget] () { raiseWidget(widget); } );
#else
    QObject::connect(&app, &SingleApplication::instanceStarted,
		     widget, [widget] () { raiseWidget(widget); } );
#endif

    widget->show();

    return app.exec();
}

void raiseWidget(QWidget* widget) {
#ifdef Q_OS_WINDOWS
    HWND hwnd = (HWND)widget->winId();

    // check if widget is minimized to Windows task bar
    if (::IsIconic(hwnd)) {
	::ShowWindow(hwnd, SW_RESTORE);
    }

    ::SetForegroundWindow(hwnd);
#else
    widget->show();
    widget->raise();
    widget->activateWindow();
#endif
}

使用Win32接口的方案

可以使用Win32接口 FindWindow 找到窗口对应的句柄,通过窗口句柄将界面带到前面。这个窗口句柄允许是非本进程的。

tips: Qt编写的程序并不适合用 FindWindow 接口,因为Qt界面的 windows class name 都是一样的,所以通过 FindWindow 接口不容易匹配到目标窗口。