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);
CodePudding user response:
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 byDWORD*
pointer to the callback'sLPARAM
, but then you are treating theLPARAM
's value as-is, which means your comparison to each window's process ID will never find a match. You would need to cast theLPARAM
to aDWORD*
pointer and then dereference that pointer in order to get the correctDWORD
value for the comparisons, egif (lpdwProcessId == *(DWORD*)lParam)
. Otherwise, just pass in the caller'sparentId
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 singleDWORD
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...
CodePudding user response:
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);
}