0

I want my application to run some code when the screen gets locked (Win 7 and 10). My application may be in the background too when user locks screen.

Anyone point me in right direction?

Thank you, relayman

relayman357
  • 793
  • 1
  • 6
  • 30

1 Answers1

3

Use WTSRegisterSessionNotification() to register an HWND to receive WTS_SESSION_(UN)LOCK notifications via the WM_WTSSESSION_CHANGE window message.

You can use FMX's FormToHWND() function to obtain your Form's HWND. However, FireMonkey controls, including Forms, do not have an overridable WndProc() method to process window messages, like VCL does, so you will have to use the Win32 API's SetWindowLongPtr() or SetWindowSubclass() function (see Subclassing Controls on MSDN) to receive the WM_WTSSESSION_CHANGE window message:

class TMyForm : public TForm
{
    ...
#ifdef _Windows
private:
    bool MonitoringWTS;
    static LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
protected:
    virtual void __fastcall CreateHandle();
#endif
    ...
};

#ifdef _Windows

#include <FMX.Platform.Win.hpp>
#include <commctrl.h>
#include <wtsapi32.h>

void __fastcall TMyForm::CreateHandle()
{
    MonitoringWTS = false;

    TForm::CreateHandle();

    // depending on which version of C++Builder you are using...
    HWND hWnd = FormToHWND(this);
    //HWND hWnd = WindowHandleToPlatform(Handle)->Wnd;
    //HWND hWnd = FmxHandleToHWND(Handle);

    if (!SetWindowSubclass(hWnd, &SubclassWndProc, 1, reinterpret_cast<DWORD_PTR>(this)))
        throw Exception(_D("Could not subclass window"));

    MonitoringWTS = WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION);
    if (!MonitoringWTS)
        RaiseLastOSError();
}

LRESULT CALLBACK TMyForm::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
        case WM_NCDESTROY:
        {
            WTSUnRegisterSessionNotification(hWnd);
            reinterpret_cast<TMyForm*>(dwRefData)->MonitoringWTS = false;

            RemoveWindowSubclass(hWnd, &SubclassWndProc, uIdSubclass);
            break;
        }

        case WM_WTSSESSION_CHANGE:
        {
            TMyForm *pThis = reinterpret_cast<TMyForm*>(dwRefData);

            switch (wParam)
            {
                case WTS_SESSION_LOCK:
                    // do something ...
                    break;

                case WTS_SESSION_UNLOCK:
                    // do something ...
                    break;
            }

            return 0;
        }
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

#endif

Alternatively, you can use the RTL's AllocateHWnd() function to create a hidden HWND and provide it with a custom WndProc() method:

class TMyForm : public TForm
{
    ...
#ifdef _Windows
private:
    HWND WndForWTS;
    bool MonitoringWTS;
    void __fastcall WndProcForWTS(TMessage &Message);
public:
    __fastcall TMyForm(TComponent *Owner);
    __fastcall ~TMyForm();
#endif
    ...
};

#ifdef _Windows

#include <wtsapi32.h>

__fastcall TMyForm::TMyForm(TComponent *Owner)
    : TForm(Owner)
{
    WndForWTS = AllocateHWnd(&WndProcForWTS);

    MonitoringWTS = WTSRegisterSessionNotification(WndForWTS, NOTIFY_FOR_THIS_SESSION);
    if (!MonitoringWTS)
    {
        int err = GetLastError();
        DeallocateHWnd(WndForWTS);
        RaiseLastOSError(err);
    }
}

__fastcall ~TMyForm();
{
    DeallocateHWnd(WndForWTS);
}

void __fastcall TMyForm::WndProcForWTS(TMessage &Message)
{
    switch (Message.Msg)
    {
        case WM_NCDESTROY:
        {
            if (MonitoringWTS)
            {
                WTSUnRegisterSessionNotification(WndForWTS);
                MonitoringWTS = false;
            }
            WndForWTS = NULL;
            break;
        }

        case WM_WTSSESSION_CHANGE:
        {
            switch (Message.WParam)
            {
                case WTS_SESSION_LOCK:
                    // do something ...
                    break;

                case WTS_SESSION_UNLOCK:
                    // do something ...
                    break;
            }

            return;
        }
    }

    Message.Result = DefWindowProc(WndForWTS, Message.Msg, Message.WParam, Message.LParam);
}

#endif
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I found this [example](https://stackoverflow.com/questions/20113627/example-of-embarcadero-windowhandletoplatform-c) but i don't know how to implement it. I'm a toddler here. – relayman357 Dec 02 '18 at 20:45
  • Remy - are there examples you can point me to on how to do any of that? I found this link (http://bcbjournal.org/articles/vol2/9807/Message-handling_for_non-visual_components.htm) but i can't make it work in Tokyo 10.3.2. I'm guessing it is too old/outdated and i don't know how to adapt it. – relayman357 Dec 06 '18 at 02:18
  • Remy, i created a new FMX project and i tried your first approach. The first block i put in the Unit1.h and the second in the Unit1.cpp. I changed TmyForm to TForm1. When i run it i'm getting "E2268 Call to undefined function 'FormToHWND'. – relayman357 Dec 06 '18 at 15:35
  • 1
    @relayman357 which version of C++Builder are you using exactly? You never stated that. `FormToHWND()` requires XE5+, and is declared in `FMX.Platform.Win.hpp`. If your version does not have `FormToHWND() `, use `FmxHandleToHWND()` or `WindowHandleToPlatform()` instead. I updated my answer to show them – Remy Lebeau Dec 06 '18 at 17:22
  • I have "C++Builder 10.2 Version 25.0.31059.3231" with installed update "10.2 Update 3". What do you guys mean when you say XE5? Thanks Remy! – relayman357 Dec 06 '18 at 22:41
  • 1
    @relayman357 `FormToHWND()` definitely exists in 10.2 Tokyo. Try fully qualifying its namespace (in case `DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE` is in effect): `HWND hWnd = Fmx::Platform::Win::FormToHWND(this);` As for XE5, I am referring to C++Builder XE5, which is 6 versions before 10.2 Tokyo (XE5 was [released in 2013](http://delphi.wikia.com/wiki/Delphi_Release_Dates)). – Remy Lebeau Dec 06 '18 at 22:47
  • That was it (needed to fully qualify namespace). It works like a champ now! Yes!! Merry Christmas Remy! – relayman357 Dec 07 '18 at 02:13