Skip to content

Commit

Permalink
Primary PID support (#36)
Browse files Browse the repository at this point in the history
* 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 #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()
  • Loading branch information
itay-grudev committed Oct 2, 2017
1 parent 6fbf6bf commit 4f03651
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 6 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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__
---------

Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------------

Expand Down Expand Up @@ -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

Expand Down
46 changes: 46 additions & 0 deletions Windows.md
Original file line number Diff line number Diff line change
@@ -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
32 changes: 28 additions & 4 deletions singleapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion singleapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,16 @@ class SingleApplication : public QAPPLICATION_CLASS

/**
* @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
Expand Down
5 changes: 5 additions & 0 deletions singleapplication.pri
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ win32 {
msvc:LIBS += Advapi32.lib
gcc:LIBS += -lAdvapi32
}

DISTFILES += \
$$PWD/README.md \
$$PWD/CHANGELOG.md \
$$PWD/Windows.md
2 changes: 2 additions & 0 deletions singleapplication_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
};

class SingleApplicationPrivate : public QObject {
Expand All @@ -54,6 +55,7 @@ Q_OBJECT
void startPrimary( bool resetMemory );
void startSecondary();
void connectToPrimary( int msecs, char connectionType );
qint64 primaryPid();

#ifdef Q_OS_UNIX
void crashHandler();
Expand Down

0 comments on commit 4f03651

Please sign in to comment.