0

How to call the winapi EnumWindows in a lambda? My attempt:

        DWORD parentId;
        // ...
        HWND parent_hwnd;
        EnumWindows([parent_hwnd](HWND hwnd, LPARAM lParam)
        {
            DWORD lpdwProcessId;
            GetWindowThreadProcessId(hwnd,&lpdwProcessId);
            if(lpdwProcessId == lParam)
            {
                parent_hwnd = hwnd;
                return FALSE;
            }
            return TRUE;
        }, (LPARAM)&parentId);
Cesar
  • 41
  • 2
  • 5
  • 16

2 Answers2

4

EnumWindows() takes a plain ordinary C-style function pointer for its lpEnumFunc parameter. However, a capturing lambda carries state information and thus is NOT convertible to a function pointer, since there is nowhere for the lambda to store its state in such a pointer. Only a non-capturing lambda, which has no state, is convertible to a function pointer.

Note that you have two bugs in your original lambda, which would have caused your code to fail even if you could have used a capturing lambda as the callback:

  • You are capturing the caller's parent_hwnd variable by-value, which means the lambda makes a copy and won't modify the value of the caller's original variable. You would need to capture the variable by-reference instead. Not that it matters in this case, since you can't use a capturing lambda anyway.

  • (this one affects a non-capturing lambda, too) You are passing the caller's parentId variable by DWORD* pointer to the callback's LPARAM, but then you are treating the LPARAM's value as-is, which means your comparison to each window's process ID will never find a match. You would need to cast the LPARAM to a DWORD* pointer and then dereference that pointer in order to get the correct DWORD value for the comparisons, eg if (lpdwProcessId == *(DWORD*)lParam). Otherwise, just pass in the caller's parentId variable by-value instead of by-pointer, then you don't have to worry about this casting. Not that it matters in this case, since you need to pass in more than just a single DWORD to the callback in order for your logic to work.

As such, you need to use a non-capturing lambda, and use the callback's LPARAM parameter to pass around all of your lambda's state information.

Try something more like this instead:

struct enumInfo {
    DWORD procId;
    HWND hwnd;
};

enumInfo info;
info.procId = ...;
info.hwnd = NULL;

EnumWindows(
    [](HWND hwnd, LPARAM lParam)
    {
        enumInfo *info = reinterpret_cast<enumInfo*>(lParam);

        DWORD dwProcessId;
        GetWindowThreadProcessId(hwnd, &dwProcessId);

        if (dwProcessId == info->procId)
        {
            info->hwnd = hwnd;
            return FALSE;
        }
        return TRUE;
    },
    reinterpret_cast<LPARAM>(&info)
);

HWND parent_hwnd = info.hwnd;
// use parent_hwnd as needed...
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Why isnt possible to capture parameters in the lambda when using with winapis? – Cesar Dec 19 '22 at 20:59
  • @Natalia The Win32 API is based on C, and doesn't have the concept of a hidden `this` value which capturing lambdas rely on. – Jonathan Potter Dec 19 '22 at 21:00
  • @Natalia See [this answer](https://stackoverflow.com/a/7852154) – Axalo Dec 19 '22 at 21:00
  • It is technically possible to implement a C-compatible stateful function by hiding its state inside of a thunk which is itself compatible with C-style function pointers. Some libraries do take advantage of this feature when using stateful objects with APIs that don't offer an `lParam`-like parameter. However, the C++ standard does not define such behavior for capturing lambdas at this time. Perhaps someone will propose such a feature in a future standard? Who knows. – Remy Lebeau Dec 19 '22 at 21:10
0

This is usually what I do when working with C APIs, provided they allow you to pass a context pointer (lParam in this case):

template<typename T>
BOOL EnumWindowsWrapper(const T &callback)
{
    return ::EnumWindows(
        [](HWND hWnd, LPARAM lParam)
        {
            auto &cb = *reinterpret_cast<decltype(&callback)>(lParam);
            return cb(hWnd, lParam);
        },
        (LPARAM)&callback);
}
Axalo
  • 2,953
  • 4
  • 25
  • 39