diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ef30b..7bde394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Changelog ========= +__3.0.9__ +--------- + +* Added SingleApplicationPrivate::primaryPid() as a solution to allow + bringing the primary window of an application to the foreground on + Windows. + + _Eelco van Dam from Peacs BV_ + __3.0.8__ --------- diff --git a/README.md b/README.md index e003ab4..82e7a5b 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,11 @@ Using `SingleApplication::instance()` is a neat way to get the `SingleApplication` instance for binding to it's signals anywhere in your program. +__Note:__ On Windows the ability to bring the application windows to the +foreground is restricted. See [Windows specific implementations](Windows.md) +for a workaround and an example implementation. + + Secondary Instances ------------------- @@ -177,7 +182,15 @@ Returns if the instance is a secondary instance. quint32 SingleApplication::instanceId() ``` -Returns a unique identifier for the current instance +Returns a unique identifier for the current instance. + +--- + +```cpp +qint64 SingleApplication::primaryPid() +``` + +Returns the process ID (PID) of the primary instance. ### Signals diff --git a/Windows.md b/Windows.md new file mode 100644 index 0000000..48b0748 --- /dev/null +++ b/Windows.md @@ -0,0 +1,46 @@ +Windows Specific Implementations +================================ + +Setting the foreground window +----------------------------- + +In the `instanceStarted()` example in the `README` we demonstrated how an +application can bring it's primary instance window whenever a second copy +of the application is started. + +On Windows the ability to bring the application windows to the foreground is +restricted, see [`AllowSetForegroundWindow()`][AllowSetForegroundWindow] for more +details. + +The background process (the primary instance) can bring its windows to the +foreground if it is allowed by the current foreground process (the secondary +instance). To bypass this `SingleApplication` must be initialized with the +`allowSecondary` parameter set to `true` and the `options` parameter must +include `Mode::SecondaryNotification`, See `SingleApplication::Mode` for more +details. + +Here is an example: + +```cpp +if( app.isSecondary() ) { + // This API requires LIBS += User32.lib to be added to the project + AllowSetForegroundWindow( DWORD( app.getPrimaryPid() ) ); +} + +if( app.isPrimary() ) { + QObject::connect( + &app, + &SingleApplication::instanceStarted, + this, + &App::instanceStarted + ); +} +``` + +```cpp +void App::instanceStarted() { + QApplication::setActiveWindow( [window/widget to set to the foreground] ); +} +``` + +[AllowSetForegroundWindow]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632668.aspx diff --git a/singleapplication.cpp b/singleapplication.cpp index 1ab69fa..8711cb1 100644 --- a/singleapplication.cpp +++ b/singleapplication.cpp @@ -45,6 +45,7 @@ #include "singleapplication.h" #include "singleapplication_p.h" + static const char NewInstance = 'N'; static const char SecondaryInstance = 'S'; static const char Reconnect = 'R'; @@ -61,14 +62,17 @@ SingleApplicationPrivate::~SingleApplicationPrivate() socket->close(); delete socket; } + memory->lock(); InstancesInfo* inst = (InstancesInfo*)memory->data(); if( server != nullptr ) { server->close(); delete server; inst->primary = false; + inst->primaryPid = -1; } memory->unlock(); + delete memory; } @@ -128,6 +132,8 @@ void SingleApplicationPrivate::genBlockServerName( int timeout ) void SingleApplicationPrivate::startPrimary( bool resetMemory ) { + Q_Q(SingleApplication); + #ifdef Q_OS_UNIX // Handle any further termination signals to ensure the // QSharedMemory block is deleted even if the process crashes @@ -158,13 +164,13 @@ void SingleApplicationPrivate::startPrimary( bool resetMemory ) memory->lock(); InstancesInfo* inst = (InstancesInfo*)memory->data(); - if( resetMemory ){ - inst->primary = true; + if( resetMemory ) { inst->secondary = 0; - } else { - inst->primary = true; } + inst->primary = true; + inst->primaryPid = q->applicationPid(); + memory->unlock(); instanceNumber = 0; @@ -217,6 +223,18 @@ void SingleApplicationPrivate::connectToPrimary( int msecs, char connectionType } } +qint64 SingleApplicationPrivate::primaryPid() +{ + qint64 pid; + + memory->lock(); + InstancesInfo* inst = (InstancesInfo*)memory->data(); + pid = inst->primaryPid; + memory->unlock(); + + return pid; +} + #ifdef Q_OS_UNIX void SingleApplicationPrivate::crashHandler() { @@ -433,6 +451,12 @@ quint32 SingleApplication::instanceId() return d->instanceNumber; } +qint64 SingleApplication::primaryPid() +{ + Q_D(SingleApplication); + return d->primaryPid(); +} + bool SingleApplication::sendMessage( QByteArray message, int timeout ) { Q_D(SingleApplication); diff --git a/singleapplication.h b/singleapplication.h index 93447f4..33a9898 100644 --- a/singleapplication.h +++ b/singleapplication.h @@ -102,10 +102,16 @@ public: /** * @brief Returns a unique identifier for the current instance - * @returns {int} + * @returns {qint32} */ quint32 instanceId(); + /** + * @brief Returns the process ID (PID) of the primary instance + * @returns {qint64} + */ + qint64 primaryPid(); + /** * @brief Sends a message to the primary instance. Returns true on success. * @param {int} timeout - Timeout for connecting diff --git a/singleapplication.pri b/singleapplication.pri index cd3dd35..a82ff28 100644 --- a/singleapplication.pri +++ b/singleapplication.pri @@ -11,3 +11,8 @@ win32 { msvc:LIBS += Advapi32.lib gcc:LIBS += -lAdvapi32 } + +DISTFILES += \ + $$PWD/README.md \ + $$PWD/CHANGELOG.md \ + $$PWD/Windows.md diff --git a/singleapplication_p.h b/singleapplication_p.h index 833e731..856b33d 100644 --- a/singleapplication_p.h +++ b/singleapplication_p.h @@ -40,6 +40,7 @@ struct InstancesInfo { bool primary; quint32 secondary; + qint64 primaryPid; }; class SingleApplicationPrivate : public QObject { @@ -54,6 +55,7 @@ public: void startPrimary( bool resetMemory ); void startSecondary(); void connectToPrimary( int msecs, char connectionType ); + qint64 primaryPid(); #ifdef Q_OS_UNIX void crashHandler();