Detect screen lock - FMX in Win32

2020-03-31 06:07发布

问题:

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

回答1:

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