Whatever reason you have for using processEvents in place of exec is wrong. The two are not equivalent. exec() will, for example, process the deferred deletion events, while processEvents will not. As you've just found out, the lastWindowClosed signal is not emitted either. This should be telling you right there that you're doing it wrong.
The idiomatic Qt way of doing something each time the event loop goes for another iteration, is to use a zero-timeout timer. Those are virtual timers that do not use operating system resources, they are an internal Qt construct.
The example below illustrates the following:
Use of a zero-timeout timer within a QObject.
 
Use of the State Machine Framework to manage the state of the application. We have three states:
sWindows is the state when the application windows are still shown. The application is set not to quit on the last window being closed.
 
sSetup is the state reached when the last of the windows was closed. In this state we ask our Object to send its notification signal with the number of times it executed the zero-timeout timer. This will set the proper count in the message label (C++11 code) or in the count label (legacy code). The state machine automatically transitions to the following state.
 
sMessage is the state when the message labels are shown, and the application is set to quit upon the last window being closed.
 
 
The use of a state machine leads to declarative code: you tell the state machine how to behave, without implementing all of the behavior. You only have to implement the behaviors that are specific to your application and not already provided by Qt. The objects that the state machine manages can be very much decoupled, and the code that declares the behavior of the machine is cohesive - it can be all in one function, instead of being spread around. This is considered to be good software design.
Do note that the zero-timeout timer is very diligent: it will force your handler code to execute constantly whenever the event loop is empty. This will force 100% CPU consumption on the core where the GUI thread happens to be executing. If you have nothing to do, you should stop() the timer.
Qt 5 C++11 Code
// https://github.com/KubaO/stackoverflown/tree/master/questions/close-process-19343325
#include <QtWidgets>
int main(int argc, char** argv)
{
    QApplication app{argc, argv};
    QLabel widget{"Close me :)"};
    QLabel message{"Last window was closed"};
    int counter = 0;
    auto worker = [&]{
       counter++;
    };
    QTimer workerTimer;
    QObject::connect(&workerTimer, &QTimer::timeout, worker);
    workerTimer.start(0);
    QStateMachine machine;
    QState sWindows{&machine};
    QState sSetup  {&machine};
    QState sMessage{&machine};
    sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false);
    sWindows.addTransition(qApp, &QGuiApplication::lastWindowClosed, &sSetup);
    QObject::connect(&sSetup, &QState::entered, [&]{
       workerTimer.stop();
       message.setText(QString("Last window was closed. Count was %1.").arg(counter));
    });
    sSetup.addTransition(&sMessage);
    sMessage.assignProperty(&message, "visible", true);
    sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true);
    machine.setInitialState(&sWindows);
    machine.start();
    widget.show();
    return app.exec();
}
Qt 4/5 C++11 Code
#include <QApplication>
#include <QLabel>
#include <QStateMachine>
#include <QBasicTimer>
class Object : public QObject {
    Q_OBJECT
    QBasicTimer m_timer;
    int m_counter = 0;
protected:
    void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() == m_timer.timerId())
            m_counter ++;
    }
public:
    Object(QObject * parent = 0) : QObject{parent} {
        m_timer.start(0, this);
    }
    Q_SLOT void stop() const {
      m_timer.stop();
      emit countedTo(m_counter);
    }
    Q_SIGNAL void countedTo(int) const;
};
int main(int argc, char** argv)
{
    QApplication app{argc, argv};
    Object object;
    QLabel widget{"Close me :)"};
    QLabel message{"Last window was closed"};
    QLabel count;
    QStateMachine machine;
    QState sWindows{&machine};
    QState sSetup{&machine};
    QState sMessage{&machine};
    sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false);
    sWindows.addTransition(qApp, "lastWindowClosed()", &sSetup);
    object.connect(&sSetup, SIGNAL(entered()), SLOT(stop()));
    count.connect(&object, SIGNAL(countedTo(int)), SLOT(setNum(int)));
    sSetup.addTransition(&sMessage);
    sMessage.assignProperty(&message, "visible", true);
    sMessage.assignProperty(&count, "visible", true);
    sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true);
    machine.setInitialState(&sWindows);
    machine.start();
    widget.show();
    return app.exec();
}
#include "main.moc"