I have a hooked dll with WH_CALLWNDPROC from a x86 program (MSVS 2017). The code goes as follows:
extern "C" __declspec(dllexport) LRESULT SysMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam);
if (nCode == HC_ACTION) {
CWPSTRUCT* pCWP = (CWPSTRUCT*)lParam;
HWND hWnd = pCWP->hwnd;
char wclass[256]; wclass[0] = 0;
if (GetClassNameA(hWnd, wclass, 255) != 0) {
if (pCWP->message == WM_NOTIFY && (strcmp(wclass, "SysHeader32") == 0 || strcmp(wclass, "SysListView32") == 0)) {
// LPNMLISTVIEW pnm = (LPNMLISTVIEW)pCWP->lParam;
// pnm->lParam is always 0
NMHDR* hdr = (NMHDR*)pCWP->lParam;
if ((int)hdr->code == NM_CUSTOMDRAW) {
char out[256]; out[0] = 0;
sprintf_s(out, 255,
"class: '%s', hWnd: %08X, msg: %08X, ptr: %08X, HDR[%08X, %i, %i]\n",
wclass, (UINT)hWnd, pCWP->message, (UINT)pCWP->lParam,
(UINT)hdr->hwndFrom, hdr->idFrom, (int)hdr->code);
FILE *stream;
if (fopen_s(&stream, "F:\\tmp\\_dll\\out.txt", "a+") == 0) {
fprintf(stream, out);
fclose(stream);
}
// how to get
// LPNMLVCUSTOMDRAW lplvcd = ???;
// lplvcd->clrFace, lplvcd->clrText, lplvcd->clrTextBk ???
}
}
}
When I record messages I get the same results as in Spy++:
It seems strange that hdr->code is UINT and NM_CUSTOMDRAW = -12 so I have to convert to (int) but the result matches the output from Spy++.
Initially I thought that I can do this:
LPNMLISTVIEW pnm = (LPNMLISTVIEW)pCWP->lParam;
...
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pnm->lParam;
...
print(lplvcd->clrFace, lplvcd->clrText, lplvcd->clrTextBk);
but pnm->lParam is always zero; pCWP->wParam is also zero.
- How to I get to LPNMLISTVIEW and LPNMLVCUSTOMDRAW structures ?
EDIT:
CWPSTRUCT* pCWP = (CWPSTRUCT*)lParam;
HWND hWnd = pCWP->hwnd;
UINT msg = pCWP->message;
char wclass[256]; wclass[0] = 0;
if (RealGetWindowClassA(hWnd, wclass, 255) != 0) {
if (msg == WM_NOTIFY && (strcmp(wclass, "SysHeader32") == 0 || strcmp(wclass, "SysListView32") == 0)) {
NMHDR* hdr = (NMHDR*)pCWP->lParam;
if (hdr->code == NM_CUSTOMDRAW) {
if (strcmp(wclass, "SysListView32") == 0) {
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pCWP->lParam;
if (lplvcd) {
RECT r = lplvcd->rcText;
char out[512]; out[0] = 0;
sprintf_s(out, 511,
"class: '%s', hWnd: %08X, msg: %08X, ptr: %08X, \
draw: %08X, rect: (%i, %i)-(%i, %i), face: %08X, txt: %08X, txtBg: %08X\n",
wclass, (UINT)hWnd, msg, (UINT)lplvcd,
lplvcd->nmcd.dwDrawStage, r.left, r.top, r.right, r.bottom,
lplvcd->clrFace, lplvcd->clrText, lplvcd->clrTextBk);
FILE *stream;
if (fopen_s(&stream, "F:\\tmp\\_dll\\out.txt", "a+") == 0) {
fprintf(stream, out);
fclose(stream);
}
}
}
Example output:
class: 'SysListView32', hWnd: 000A04F4, msg: 0000004E, ptr: 02E3F33C, draw: 00000001, rect: (0, 1)-(-1241442379, 48493508), face: FFB617B5, txt: 02E3F400, txtBg: 6F010726
GetClassName()
may not return what you want for subclassed controls. Consider usingRealGetWindowClass()
instead. See What makes RealGetWindowClass so much more real than GetClassName?. – Remy Lebeau