Results 1 to 11 of 11

Thread: C++/WinAPI Filtering Desktop Windows out of Window Event Callbacks

  1. #1
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default C++/WinAPI Filtering Desktop Windows out of Window Event Callbacks

    Invoking "EnumWindows" with the given callback function filters the windows on the desktop properly. However, the same tests do not work when used in a callback that handles desktop accessibility events (no messages are printed if you open and then close something but the function runs).

    Expected behavior is that right clicking on the desktop will not print anything, but opening e.g. an Explorer window will print one message. If you remove the tests you will see that very nearly everything on a form is a "window."

    Shell Code:
    g++ -std=c++1z -municode -Wall -Werror -pedantic -lgdi32
    C++ Code:
    #define _UNICODE
    #include <windows.h>
    #include <stdlib.h>

    #include <vector>
    #include <iostream>
    using namespace std;

    void ErrorExit(
        const wchar_t *lpszFunction
    );

    LRESULT CALLBACK WndProc(
        _In_ HWND   hWnd,
        _In_ UINT   uMsg,
        _In_ WPARAM wParam,
        _In_ LPARAM lParam
    );

    // Window functions: [url]https://msdn.microsoft.com/en-us/library/windows/desktop/ms632595(v=vs.85).aspx[/url]
    BOOL CALLBACK EnumWindowsCallback(
        _In_ HWND   hWnd,
        _In_ LPARAM lParam
    );

    void CALLBACK WinEventCallback(
        _In_ HWINEVENTHOOK hWinEventHook,
        _In_ DWORD         event,
        _In_ HWND          hWnd,
        _In_ LONG          idObject,
        _In_ LONG          idChild,
        _In_ DWORD         dwEventThread,
        _In_ DWORD         dwmsEventTime
    );

    std::vector<struct window> windows;

    HINSTANCE hInst;
    WCHAR szTitle[] = L"Tile";
    WCHAR szMenuName[] = L"Tile";
    WCHAR szWindowClass[] = L"Tile";

    int
    main(int argc, char *argv[])
    {
        LPWSTR *szArgList;
        int narg;
        WNDCLASSEXW wcex;
        HWND hWnd;
        LONG lStyle;
        HACCEL hAccelTable;
        MSG msg;
        FILE *fConsole;
       
        // Fake values for arguments passed to wWinMain.
        HINSTANCE hInstance = 0;
        int nCmdShow = 0;
       
        UNREFERENCED_PARAMETER(lStyle);
        UNREFERENCED_PARAMETER(hAccelTable);
        UNREFERENCED_PARAMETER(fConsole);

        szArgList = CommandLineToArgvW(GetCommandLineW(), &narg);
        if (!szArgList) {
            ErrorExit(L"CommandLineToArgvW");
        }

        // TODO: Add custom icon or use default Visual Studio icon.
        wcex.cbSize         = sizeof(WNDCLASSEX);
        wcex.style          = CS_HREDRAW | CS_VREDRAW;;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, IDI_APPLICATION);
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
       
        wcex.lpszMenuName   = szMenuName;
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, IDI_APPLICATION);
        if (!RegisterClassEx(&wcex)) {
            ErrorExit(L"RegisterClassExW");
        }

        hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
        if (!hWnd) {
            ErrorExit(L"CreateWindowExW");
        }
       
        lStyle = GetWindowLong(hWnd, GWL_STYLE);
        if (!SetWindowLongPtr(hWnd, GWL_STYLE,
                WS_BORDER | WS_THICKFRAME)) {
            ErrorExit(L"SetWindowLongPtr");
        }
       
        // Necessary per API reference for SetWindowLong. If left out,
        // there will be a persistent rendering artifact after the first
        // window move if the window has not been resized.
        if (!SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
                SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
                SWP_FRAMECHANGED)) {
            ErrorExit(L"SetWindowPos");
        }
       
        ShowWindow(hWnd, nCmdShow);
        if (!UpdateWindow(hWnd)) {
            ErrorExit(L"UpdateWindow");
        }
       
        //EnumWindows(EnumWindowsCallback, 0);
        HWINEVENTHOOK hWinEventHook = SetWinEventHook(
            EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, NULL,
            WinEventCallback, 0, 0, WINEVENT_OUTOFCONTEXT);
       
        hAccelTable = LoadAccelerators(hInstance, 0);
        while (GetMessage(&msg, nullptr, 0, 0)) {
            if (!TranslateAccelerator(hWnd, nullptr, &msg)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
       
        if (hWinEventHook)
            UnhookWinEvent(hWinEventHook);
       
        return (int)msg.wParam;
    }

    LRESULT CALLBACK
    WndProc(
        _In_ HWND   hWnd,
        _In_ UINT   message,
        _In_ WPARAM wParam,
        _In_ LPARAM lParam
    )
    {
        switch (message) {
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        default: {
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        }
        return 0;
    }

    BOOL CALLBACK
    EnumWindowsCallback(
        _In_ HWND   hWnd,
        _In_ LPARAM lParam
    )
    {
        // TODO: Enumerate only windows that have a presence on the desktop.
        //
        // [url]https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863[/url]
        //   Uses criteria similar to what the Alt+Tab menu uses. May not list allocate
        // windows in a process group.
       
        // Returns zero on error or no text.
        // TODO: Figure out if returned size being one less than expected is
        // normal.
        int nMax = GetWindowTextLengthW(hWnd) + 1;
        LPWSTR lpStr = new wchar_t[nMax];

        // Skip to next window on error.
        // TODO: Skip if and only if there is an error. Per the documentation, the
        // window text may not exist and have zero length.
        if (!(nMax - 1))
            return TRUE;
       
        if (!GetWindowTextW(hWnd, lpStr, nMax)) {
            // TODO: Call SetLastError.
            // TODO: Return FALSE to stop enumeration.
        }
       
        LONG lStyle;
        if (!(lStyle = GetWindowLong(hWnd, GWL_STYLE))) {
            // TODO: Call SetLastError.
            // TODO: Return FALSE to stop enumeration.
        }
       
        // These conditions are fulfilled by windows with a desktop presence.
        if ((lStyle & (WS_BORDER | WS_VISIBLE)) != (WS_BORDER | WS_VISIBLE))
            return TRUE;
       
        RECT rArea;
        if (!GetClientRect(hWnd, &rArea)) {
            // TODO: Check to see if this fails if application is cached.
            // TODO: Call SetLastError.
            // TODO: Return FALSE to stop enumeration.
        }
       
        wcout << lpStr << " " << rArea.right << ", " << rArea.bottom << " " <<
            IsIconic(hWnd) << std::endl;
        delete[] lpStr;
       
        return TRUE;
    }

    void CALLBACK
    WinEventCallback(
        _In_ HWINEVENTHOOK hWinEventHook,
        _In_ DWORD         event,
        _In_ HWND          hWnd,
        _In_ LONG          idObject,
        _In_ LONG          idChild,
        _In_ DWORD         dwEventThread,
        _In_ DWORD         dwmsEventTime
    )
    {
        if (!(event == EVENT_OBJECT_CREATE &&
              idObject == OBJID_WINDOW &&
              idChild == INDEXID_CONTAINER))
            return;
       
        LONG lStyle;
        if (!(lStyle = GetWindowLong(hWnd, GWL_STYLE)))
            return;
       
        // These conditions are fulfilled by windows with a desktop presence.
        if ((lStyle & (WS_BORDER | WS_VISIBLE)) != (WS_BORDER | WS_VISIBLE))
            return;
       
        cout << "WinEventCallback: EVENT_OBJECT_CREATE" << std::endl;
    }

    void
    ErrorExit(
        const wchar_t *lpszFunction
    )
    {
        // Retrieve the system error message for the last error code.
        LPVOID lpMsgBuf;
        LPVOID lpDisplayBuf;
        DWORD lerr = GetLastError();
        DWORD size = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            lerr,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&lpMsgBuf,
            0, NULL);

        UNREFERENCED_PARAMETER(lpDisplayBuf);
        UNREFERENCED_PARAMETER(size);
           
        // Print the error message to the console.
        wprintf(L"%s: %s", lpszFunction, (wchar_t *)lpMsgBuf);

        // Display the error message in a message box.
        /*
        lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
            (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
        StringCchPrintf((LPTSTR)lpDisplayBuf,
            LocalSize(lpDisplayBuf) / sizeof(TCHAR),
            TEXT("%s failed with error %d: %s"),
            lpszFunction, lerr, lpMsgBuf);
        MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

        LocalFree(lpMsgBuf);
        */

        ExitProcess(lerr);
    }
    Last edited by R0b0t1; 06-15-2017 at 08:45 PM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  2. #2
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Changing WinEventCallback like so helps a bit, but there are many UI elements which have a "window title" - for some it is the form element type, for others, like entry boxes, it will be the text entered into them. For some reason the returned style doesn't have the usual flags set. If anyone knows what else I can select on, please comment.

    c++ Code:
    void CALLBACK
    WinEventCallback(
        _In_ HWINEVENTHOOK hWinEventHook,
        _In_ DWORD         event,
        _In_ HWND          hWnd,
        _In_ LONG          idObject,
        _In_ LONG          idChild,
        _In_ DWORD         dwEventThread,
        _In_ DWORD         dwmsEventTime
    )
    {
        if (!(event == EVENT_OBJECT_CREATE &&
              idObject == OBJID_WINDOW &&
              idChild == INDEXID_CONTAINER))
            return;
       
        int nMax = GetWindowTextLengthW(hWnd) + 1;
        LPWSTR lpStr = new wchar_t[nMax];
       
        if (!(nMax - 1))
            return;
       
        if (!GetWindowTextW(hWnd, lpStr, nMax))
            return;
       
        LONG lStyle;
        if (!(lStyle = GetWindowLong(hWnd, GWL_STYLE)))
            return;
       
        RECT rArea;
        if (!GetClientRect(hWnd, &rArea))
            return;
       
        // These conditions are fulfilled by windows with a desktop presence.
        //if ((lStyle & (WS_BORDER | WS_VISIBLE)) != (WS_BORDER | WS_VISIBLE))
        //  return;

        wcout << lpStr << std::endl;
        delete[] lpStr;
    }
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  3. #3
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Removing the check for WS_VISIBLE seems to fix it, but per my other thread there is weirdness with Windows Store applications. E.g. Settings will not trigger a callback if it has already been launched and backgrounded. Another issue is that additional Firefox windows do not trigger the callback.

    Still looking for suggestions.

    c++ Code:
    void CALLBACK
    WinEventCallback(
        _In_ HWINEVENTHOOK hWinEventHook,
        _In_ DWORD         event,
        _In_ HWND          hWnd,
        _In_ LONG          idObject,
        _In_ LONG          idChild,
        _In_ DWORD         dwEventThread,
        _In_ DWORD         dwmsEventTime
    )
    {
        if (!(event == EVENT_OBJECT_CREATE &&
              idObject == OBJID_WINDOW &&
              idChild == INDEXID_CONTAINER))
            return;
       
        int nMax = GetWindowTextLengthW(hWnd) + 1;
        LPWSTR lpStr = new wchar_t[nMax];
       
        if (!(nMax - 1))
            return;
       
        if (!GetWindowTextW(hWnd, lpStr, nMax))
            return;
       
        LONG lStyle;
        if (!(lStyle = GetWindowLong(hWnd, GWL_STYLE)))
            return;
       
        RECT rArea;
        if (!GetClientRect(hWnd, &rArea))
            return;
       
        // These conditions are fulfilled by windows with a desktop presence.
        // Original check was for WS_BORDER and WS_VISIBLE. WS_VISIBLE is not set.
        if ((lStyle & (WS_BORDER)) != (WS_BORDER))
            return;

        wcout << lpStr << std::endl;
        delete[] lpStr;
    }
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  4. #4
    Join Date
    Jun 2017
    Posts
    13
    Mentioned
    0 Post(s)
    Quoted
    2 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    Removing the check for WS_VISIBLE seems to fix it, but per my other thread there is weirdness with Windows Store applications. E.g. Settings will not trigger a callback if it has already been launched and backgrounded. Another issue is that additional Firefox windows do not trigger the callback.
    Assuming your HWND is hooked properly, and listening to the correct thread, you may want to try a clean install of firefox, while disabling all of their default plugins. Also, is this only happening with firefox, or have you attempted other browsers, such as Chrome? The entire source would be useful if you want a flat answer.

    As far as Windows store applications, it likely has to do with the thread process remaining in an idle state, i.e; it is the same state of execution as when you minimized, which obviously wouldn't invoke a callback, unless you were performing a function.

  5. #5
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by dotdotdot View Post
    Assuming your HWND is hooked properly, and listening to the correct thread, you may want to try a clean install of firefox, while disabling all of their default plugins. Also, is this only happening with firefox, or have you attempted other browsers, such as Chrome? The entire source would be useful if you want a flat answer.

    As far as Windows store applications, it likely has to do with the thread process remaining in an idle state, i.e; it is the same state of execution as when you minimized, which obviously wouldn't invoke a callback, unless you were performing a function.
    No HWND is specified so all HWNDs are hooked (technically I think I am hooking the desktop, so I receive events for it and all descendants). Likewise no thread is specified and I receive events for all threads in all programs.

    Eventually I might try reinstalling Firefox but I think it may have to do with what Firefox does when it creates a window. Separate explorer windows trigger the event, but they may be separate processes. I'll repeat this with any other program I can find (Chrome is a good suggestion) but there's only one event that seems to directly correspond to window creation - the one I'm hooking.

    The thing that confuses me about the Windows Store apps is that the window doesn't exist. It has no desktop presence - it's not minimized, there is no icon, and the only way to bring it back is to "launch" the app. It is, however, still marked as running but in the background in task manager. Unfortunately I can't find any way to tell if a program has been backgrounded.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  6. #6
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Here is an attempt in Pascal. It doesn't work, likely because it's a console application. Is there an easy way to add LCL and the form components without actually using a GUI? Does the callback need to be declared to use the C calling convention?

    pascal Code:
    program PTile;

    {$mode objfpc}{$H+}

    uses
      {$IFDEF UNIX}{$IFDEF UseCThreads}
      cthreads,
      {$ENDIF}{$ENDIF}
      Classes, Windows;

    type
      HWINEVENTHOOK = HANDLE;
      WINEVENTPROC = procedure(
        EventHook: HWINEVENTHOOK;
        Event: DWORD;
        HWnd: HWND;
        IDObject, IDChild: LONG;
        EventThread, EventTime: DWORD); cdecl;

    const
      EVENT_OBJECT_CREATE  = $8000;
      EVENT_OBJECT_DESTROY = $8001;

    function SetWinEventHook(
      EventMin, EventMax: UINT;
      HMod: HMODULE;
      EventProc: WINEVENTPROC;
      IDProcess, IDThread: DWORD;
      Flags: UINT): HWINEVENTHOOK; cdecl; external 'User32.dll';

    function UnhookWinEvent(
      EventHook: HWINEVENTHOOK): boolean; cdecl; external 'User32.dll';

    procedure WinEventCallback(
      EventHook: HWINEVENTHOOK;
      Event: DWORD;
      HWnd: HWND;
      IDObject, IDChild: LONG;
      EventThread, EventTime: DWORD); cdecl;
    begin
      WriteLn('.');
    end;

    var
      EventHook: HWINEVENTHOOK;
    begin
      EventHook := SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, 0,
        @WinEventCallback, 0, 0, 0);

      ReadLn;

      if EventHook <> 0 then
        UnhookWinEvent(EventHook);
    end.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  7. #7
    Join Date
    Feb 2012
    Location
    Norway
    Posts
    968
    Mentioned
    142 Post(s)
    Quoted
    582 Post(s)

    Default

    cdecl calling? sup with that? x86 windows uses stdcall, while 64bit will use the "win64" calling.

    So then you get this:
    pascal Code:
    {$calling stdcall}

    function SetWinEventHook(
      EventMin, EventMax: UINT;
      HMod: HMODULE;
      EventProc: WINEVENTPROC;
      IDProcess, IDThread: DWORD;
      Flags: UINT): HWINEVENTHOOK; external 'User32.dll';

    function UnhookWinEvent(
      EventHook: HWINEVENTHOOK): LongBool; external 'User32.dll';

    procedure WinEventCallback(
      EventHook: HWINEVENTHOOK;
      Event: DWORD;
      HWnd: HWND;
      IDObject, IDChild: LONG;
      EventThread, EventTime: DWORD);
    begin
      WriteLn('.');
    end;

    Do note that I also changed the result type of UnhookWinEvent as Boolean is only 1 byte, and Windows' BOOL is 4 bytes (LongBool).
    The above should work for both 32bit and 64bit, on 64bit stdcall calling specification will be ignored, and the correct calling will be used.
    !No priv. messages please

  8. #8
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Does cdecl not default to stdcall or the x86-64 register convention (which I assume is shared between FreePascal and C/C++ if there's no keyword for it?)?

    Unfortunately I still don't receive any events. I attempted to add the Interfaces unit to the project to see if that would pull in the LCL event processing code, but I receive this:

    Code:
    PTile.lpr(7,19) Fatal: Cannot find Interfaces used by PTile. Check if package LCL is in the dependencies of the Project Inspector.
    pascal Code:
    program PTile;

    {$mode objfpc}{$H+}
    {$calling stdcall}

    uses
      Classes, Windows;

    type
      HWINEVENTHOOK = HANDLE;
      WINEVENTPROC = procedure(
        EventHook: HWINEVENTHOOK;
        Event: DWORD;
        HWnd: HWND;
        IDObject, IDChild: LONG;
        EventThread, EventTime: DWORD);

    const
      WINEVENT_OUTOFCONTEXT = $0000;
      EVENT_OBJECT_CREATE   = $8000;
      EVENT_OBJECT_DESTROY  = $8001;

    function SetWinEventHook(
      EventMin, EventMax: UINT;
      HMod: HMODULE;
      EventProc: WINEVENTPROC;
      IDProcess, IDThread: DWORD;
      Flags: UINT): HWINEVENTHOOK; external 'User32.dll';

    function UnhookWinEvent(
      EventHook: HWINEVENTHOOK): LongBool; external 'User32.dll';

    procedure WinEventCallback(
      EventHook: HWINEVENTHOOK;
      Event: DWORD;
      HWnd: HWND;
      IDObject, IDChild: LONG;
      EventThread, EventTime: DWORD);
    begin
      WriteLn('.');
    end;

    var
      EventHook: HWINEVENTHOOK;
    begin
      EventHook := SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, 0,
        @WinEventCallback, 0, 0, WINEVENT_OUTOFCONTEXT);

      ReadLn;

      if EventHook <> 0 then
        UnhookWinEvent(EventHook);
    end.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  9. #9
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,063
    Mentioned
    289 Post(s)
    Quoted
    1088 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    Does cdecl not default to stdcall or the x86-64 register convention (which I assume is shared between FreePascal and C/C++ if there's no keyword for it?)?

    Unfortunately I still don't receive any events. I attempted to add the Interfaces unit to the project to see if that would pull in the LCL event processing code, but I receive this:

    Code:
    PTile.lpr(7,19) Fatal: Cannot find Interfaces used by PTile. Check if package LCL is in the dependencies of the Project Inspector.
    pascal Code:
    program PTile;

    {$mode objfpc}{$H+}
    {$calling stdcall}

    uses
      Classes, Windows;

    type
      HWINEVENTHOOK = HANDLE;
      WINEVENTPROC = procedure(
        EventHook: HWINEVENTHOOK;
        Event: DWORD;
        HWnd: HWND;
        IDObject, IDChild: LONG;
        EventThread, EventTime: DWORD);

    const
      WINEVENT_OUTOFCONTEXT = $0000;
      EVENT_OBJECT_CREATE   = $8000;
      EVENT_OBJECT_DESTROY  = $8001;

    function SetWinEventHook(
      EventMin, EventMax: UINT;
      HMod: HMODULE;
      EventProc: WINEVENTPROC;
      IDProcess, IDThread: DWORD;
      Flags: UINT): HWINEVENTHOOK; external 'User32.dll';

    function UnhookWinEvent(
      EventHook: HWINEVENTHOOK): LongBool; external 'User32.dll';

    procedure WinEventCallback(
      EventHook: HWINEVENTHOOK;
      Event: DWORD;
      HWnd: HWND;
      IDObject, IDChild: LONG;
      EventThread, EventTime: DWORD);
    begin
      WriteLn('.');
    end;

    var
      EventHook: HWINEVENTHOOK;
    begin
      EventHook := SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, 0,
        @WinEventCallback, 0, 0, WINEVENT_OUTOFCONTEXT);

      ReadLn;

      if EventHook <> 0 then
        UnhookWinEvent(EventHook);
    end.
    You need a message loop to receive the hooks.

  10. #10
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by Olly View Post
    You need a message loop to receive the hooks.
    That's why I was trying to include the Interfaces unit. What should I include instead? Should I do all of it myself?

    EDIT: I added an event loop and it works, but I'd still like to know how to do this with the LCL so I can make use of Lazarus form controls later if need be.
    Last edited by R0b0t1; 06-21-2017 at 04:54 AM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  11. #11
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,909
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Here is the working code, hopefully someone can play with it and figure out why certain window types are or are not detected.

    pascal Code:
    program PTile;

    {$mode objfpc}{$H+}
    {$calling stdcall}

    uses
      Classes, Windows;

    type
      HWINEVENTHOOK = HANDLE;
      WINEVENTPROC = procedure(
        EventHook: HWINEVENTHOOK;
        Event: DWORD;
        HWnd: HWND;
        IDObject, IDChild: LONG;
        EventThread, EventTime: DWORD);

    const
      WINEVENT_OUTOFCONTEXT = $0000;
      EVENT_OBJECT_CREATE   = $8000;
      EVENT_OBJECT_DESTROY  = $8001;
      OBJID_WINDOW          = $0000;
      INDEXID_CONTAINER     = $0000;

    function SetWinEventHook(
      EventMin, EventMax: UINT;
      HMod: HMODULE;
      EventProc: WINEVENTPROC;
      IDProcess, IDThread: DWORD;
      Flags: UINT): HWINEVENTHOOK; external 'User32.dll';

    function UnhookWinEvent(
      EventHook: HWINEVENTHOOK): LongBool; external 'User32.dll';

    function {%H-}EnumWindowsCallback(
      {%H-}HWnd: HWND;
      {%H-}LParam: LPARAM): LongBool;
    begin
      Result := True;
      WriteLn('.');
    end;

    procedure WinEventCallback(
      {%H-}EventHook: HWINEVENTHOOK;
      {%H-}Event: DWORD;
      {%H-}HWnd: HWND;
      {%H-}IDObject, {%H-}IDChild: LONG;
      {%H-}EventThread, {%H-}EventTime: DWORD);
    var
      Len: LongInt;
      Title: array of WideChar;
      LStyle: LONG;
    begin
      if not ((Event = EVENT_OBJECT_CREATE) and
              (IDObject = OBJID_WINDOW) and
              (IDChild = INDEXID_CONTAINER)) then
        exit;

      Len := GetWindowTextLengthW(HWnd) + 1;
      if Len - 1 = 0 then
        exit;

      SetLength(Title, Len);
      GetWindowTextW(HWnd, @Title[0], Len);

      LStyle := GetWindowLong(HWnd, GWL_STYLE);
      if LStyle = 0 then
        exit;

      if (LStyle and WS_BORDER) <> WS_BORDER then
        exit;

      WriteLn(WideCharToString(@Title[0]));
    end;

    var
      Message: TMsg;
      EventHook: HWINEVENTHOOK;
    begin
      Message := Default(TMsg);
      EventHook := SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, 0,
        @WinEventCallback, 0, 0, WINEVENT_OUTOFCONTEXT);

      while GetMessage(Message, 0, 0, 0) do
        DispatchMessage(Message);

      if EventHook <> 0 then
        UnhookWinEvent(EventHook);
    end.
    Last edited by R0b0t1; 06-22-2017 at 03:39 PM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •