Primary PID support (#36)

* Added the ability to bring the primary application window to the foreground on Windows systems by adding an option flag. THis option can only be used in Windows development and in applications derived from QApplication with a QMainWindow object.
Because the primary application needs to be instructed to go to the foreground, the option SecondaryNotification must also be set to use this functionality

* Changed the ability to bring the primary application window to the front as discussed in itay-grudev/SingleApplication#31.

Now the process ID of the primary application get stored and is accessible for other instances of the application. It is to the developer to bring the applications windows to the front. For convenience the accompanying readme now contains a paragraph with example of how to do this on Windows systems.

* v3.0.9 Added SingleApplicationPrivate::primaryPid()
This commit is contained in:
Itay Grudev 2017-10-02 12:17:41 +01:00 committed by GitHub
parent 6fbf6bffc8
commit 4f03651072
7 changed files with 111 additions and 6 deletions

View File

@ -1,6 +1,15 @@
Changelog 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__ __3.0.8__
--------- ---------

View File

@ -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 `SingleApplication` instance for binding to it's signals anywhere in your
program. 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 Secondary Instances
------------------- -------------------
@ -177,7 +182,15 @@ Returns if the instance is a secondary instance.
quint32 SingleApplication::instanceId() 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 ### Signals

46
Windows.md Normal file
View File

@ -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

View File

@ -45,6 +45,7 @@
#include "singleapplication.h" #include "singleapplication.h"
#include "singleapplication_p.h" #include "singleapplication_p.h"
static const char NewInstance = 'N'; static const char NewInstance = 'N';
static const char SecondaryInstance = 'S'; static const char SecondaryInstance = 'S';
static const char Reconnect = 'R'; static const char Reconnect = 'R';
@ -61,14 +62,17 @@ SingleApplicationPrivate::~SingleApplicationPrivate()
socket->close(); socket->close();
delete socket; delete socket;
} }
memory->lock(); memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data(); InstancesInfo* inst = (InstancesInfo*)memory->data();
if( server != nullptr ) { if( server != nullptr ) {
server->close(); server->close();
delete server; delete server;
inst->primary = false; inst->primary = false;
inst->primaryPid = -1;
} }
memory->unlock(); memory->unlock();
delete memory; delete memory;
} }
@ -128,6 +132,8 @@ void SingleApplicationPrivate::genBlockServerName( int timeout )
void SingleApplicationPrivate::startPrimary( bool resetMemory ) void SingleApplicationPrivate::startPrimary( bool resetMemory )
{ {
Q_Q(SingleApplication);
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
// Handle any further termination signals to ensure the // Handle any further termination signals to ensure the
// QSharedMemory block is deleted even if the process crashes // QSharedMemory block is deleted even if the process crashes
@ -158,13 +164,13 @@ void SingleApplicationPrivate::startPrimary( bool resetMemory )
memory->lock(); memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data(); InstancesInfo* inst = (InstancesInfo*)memory->data();
if( resetMemory ){ if( resetMemory ) {
inst->primary = true;
inst->secondary = 0; inst->secondary = 0;
} else {
inst->primary = true;
} }
inst->primary = true;
inst->primaryPid = q->applicationPid();
memory->unlock(); memory->unlock();
instanceNumber = 0; 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 #ifdef Q_OS_UNIX
void SingleApplicationPrivate::crashHandler() void SingleApplicationPrivate::crashHandler()
{ {
@ -433,6 +451,12 @@ quint32 SingleApplication::instanceId()
return d->instanceNumber; return d->instanceNumber;
} }
qint64 SingleApplication::primaryPid()
{
Q_D(SingleApplication);
return d->primaryPid();
}
bool SingleApplication::sendMessage( QByteArray message, int timeout ) bool SingleApplication::sendMessage( QByteArray message, int timeout )
{ {
Q_D(SingleApplication); Q_D(SingleApplication);

View File

@ -102,10 +102,16 @@ public:
/** /**
* @brief Returns a unique identifier for the current instance * @brief Returns a unique identifier for the current instance
* @returns {int} * @returns {qint32}
*/ */
quint32 instanceId(); 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. * @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting * @param {int} timeout - Timeout for connecting

View File

@ -11,3 +11,8 @@ win32 {
msvc:LIBS += Advapi32.lib msvc:LIBS += Advapi32.lib
gcc:LIBS += -lAdvapi32 gcc:LIBS += -lAdvapi32
} }
DISTFILES += \
$$PWD/README.md \
$$PWD/CHANGELOG.md \
$$PWD/Windows.md

View File

@ -40,6 +40,7 @@
struct InstancesInfo { struct InstancesInfo {
bool primary; bool primary;
quint32 secondary; quint32 secondary;
qint64 primaryPid;
}; };
class SingleApplicationPrivate : public QObject { class SingleApplicationPrivate : public QObject {
@ -54,6 +55,7 @@ public:
void startPrimary( bool resetMemory ); void startPrimary( bool resetMemory );
void startSecondary(); void startSecondary();
void connectToPrimary( int msecs, char connectionType ); void connectToPrimary( int msecs, char connectionType );
qint64 primaryPid();
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
void crashHandler(); void crashHandler();