Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classic List Group Fix 1.1.0 #1049

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 171 additions & 73 deletions mods/classic-list-group-fix.wh.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// ==WindhawkMod==
// @id classic-list-group-fix
// @name Classic List Group Fix
// @description Fixes the appearance of list group headers in classic theme.
// @version 1.0.0
// @description Makes the appearance of list group headers in classic theme like Windows XP.
// @version 1.1.0
// @author aubymori
// @github https://github.com/aubymori
// @include *
Expand All @@ -11,35 +11,66 @@

// ==WindhawkModReadme==
/*
# Classic List View Fix
The appearance of list group headers in classic theme is wrong. They have
incorrect margins and the font is not bolded. This mod fixes that.
# Classic List Group Fix
This mod makes list group headers in classic theme look as they did in Windows XP.

**Before**:

![Before](https://raw.githubusercontent.com/aubymori/images/refs/heads/main/classic-list-group-fix-before.png)

**After**:

![After](https://raw.githubusercontent.com/aubymori/images/refs/heads/main/classic-list-group-fix-after.png)
*/
// ==/WindhawkModReadme==

// ==WindhawkModSettings==
/*
- hidecollapse: true
$name: Hide collapse button
$description: Hides the button that expands and collapses the list group. This is purely visual; you can still click where the button once was.
*/
// ==/WindhawkModSettings==

#include <commctrl.h>
#include <windhawk_utils.h>

#ifdef _WIN64
# define THISCALL __cdecl
# define STHISCALL L"__cdecl"
#else
# define THISCALL __thiscall
# define STHISCALL L"__thiscall"
#endif

#define Wh_LogRect(lprc) \
Wh_Log( \
L"Top: %d, Left: %d, Right: %d, Bottom: %d", \
lprc->top, \
lprc->left, \
lprc->right, \
lprc->bottom \
)
bool g_fHideCollapse = false;

/* CListGroup::HeaderHeight causes crashes when allocating stack memory,
so store icon font globally so it can access it. */
LOGFONTW g_lfIconFont = { 0 };

typedef INT_PTR (THISCALL *CListGroup_GetGroupRect_t)(void *, LPRECT);
CListGroup_GetGroupRect_t CListGroup_GetGroupRect_orig;
INT_PTR THISCALL CListGroup_GetGroupRect_hook(
/* I can't for the life of me find an API to get this. */
bool GetIconFont(LOGFONTW *lplf)
{
HKEY hKey;
if (ERROR_SUCCESS != RegOpenKeyExW(
HKEY_CURRENT_USER,
L"Control Panel\\Desktop\\WindowMetrics",
NULL,
KEY_READ,
&hKey
))
return false;

DWORD cbData = sizeof(LOGFONTW);
bool bSuccess = ERROR_SUCCESS == RegQueryValueExW(
hKey, L"IconFont", NULL, NULL,
(BYTE *)lplf, &cbData
);
RegCloseKey(hKey);
return bSuccess;
}

INT_PTR (__thiscall *CListGroup_GetGroupRect_orig)(void *, LPRECT);
INT_PTR __thiscall CListGroup_GetGroupRect_hook(
void *pThis,
LPRECT lprc
)
Expand All @@ -56,9 +87,8 @@ INT_PTR THISCALL CListGroup_GetGroupRect_hook(
return res;
}

typedef void (THISCALL *CListGroup__PaintHeader_t)(void *, ULONG, LPNMLVCUSTOMDRAW);
CListGroup__PaintHeader_t CListGroup__PaintHeader_orig;
void THISCALL CListGroup__PaintHeader_hook(
void (__thiscall *CListGroup__PaintHeader_orig)(void *, ULONG, LPNMLVCUSTOMDRAW);
void __thiscall CListGroup__PaintHeader_hook(
void *pThis,
ULONG uFlags,
LPNMLVCUSTOMDRAW lpcd
Expand All @@ -67,83 +97,151 @@ void THISCALL CListGroup__PaintHeader_hook(
lpcd->rcText.left = 6;
lpcd->rcText.top++;

NONCLIENTMETRICSW ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICSW);

SystemParametersInfoW(
SPI_GETNONCLIENTMETRICS,
sizeof(NONCLIENTMETRICSW),
&ncm,
NULL
);

HFONT hfTitle = CreateFontIndirectW(
&(ncm.lfCaptionFont)
);
SelectObject(lpcd->nmcd.hdc, hfTitle);
GetIconFont(&g_lfIconFont);
LOGFONTW lf;
lf = g_lfIconFont;
lf.lfWeight = FW_BOLD;
lf.lfItalic = FALSE;
HFONT hfIcon = CreateFontIndirectW(&lf);
SelectObject(lpcd->nmcd.hdc, hfIcon);

CListGroup__PaintHeader_orig(
pThis, uFlags, lpcd
);

DeleteObject(hfTitle);
DeleteObject(hfIcon);
}

typedef INT_PTR (THISCALL *CListGroup_HeaderHeight_t)(void *);
CListGroup_HeaderHeight_t CListGroup_HeaderHeight_orig;
INT_PTR THISCALL CListGroup_HeaderHeight_hook(
INT_PTR (__thiscall *CListGroup_HeaderHeight_orig)(void *);
INT_PTR __thiscall CListGroup_HeaderHeight_hook(
void *pThis
)
{
return 13;
return -g_lfIconFont.lfHeight + 2;
}

BOOL Wh_ModInit(void)
{
void (__thiscall *CListGroup__PaintCollapse_orig)(void *, LPNMLVCUSTOMDRAW);
void __thiscall CListGroup__PaintCollapse_hook(
void *pThis,
LPNMLVCUSTOMDRAW lpcd
)
{
if (!g_fHideCollapse)
CListGroup__PaintCollapse_orig(pThis, lpcd);
}

#ifdef _WIN64
# define PATHCACHE_VALNAME L"last-comctl32-v6-path"
#else
# define PATHCACHE_VALNAME L"last-comctl32-v6-path-wow64"
#endif

#define COMCTL_582_SEARCH L"microsoft.windows.common-controls_6595b64144ccf1df_5.82"

/* Load the ComCtl32 module */
HMODULE LoadComCtlModule(void)
{
HMODULE hComCtl = LoadLibraryW(L"comctl32.dll");
if (!hComCtl)
{
Wh_Log(L"Failed to load comctl32.dll");
return FALSE;
return NULL;
}

WindhawkUtils::SYMBOL_HOOK hooks[] = {
WCHAR szPath[MAX_PATH];
GetModuleFileNameW(hComCtl, szPath, MAX_PATH);

WCHAR szv6Path[MAX_PATH];
BOOL bNoCache = FALSE;
if (!Wh_GetStringValue(PATHCACHE_VALNAME, szv6Path, MAX_PATH))
{
bNoCache = TRUE;
}

/**
* the !bNoCache check here is nested because we only want to fall through
* to the cacher if the current comctl32 path is NOT 5.82.
*/
if (wcsstr(szPath, COMCTL_582_SEARCH)
|| wcsstr(szPath, L"\\System32")
|| wcsstr(szPath, L"\\SysWOW64"))
{
if (!bNoCache)
{
{
L"public: int "
STHISCALL
L" CListGroup::GetGroupRect(struct tagRECT *)const "
},
(void **)&CListGroup_GetGroupRect_orig,
(void *)CListGroup_GetGroupRect_hook,
false
hComCtl = LoadLibraryW(szv6Path);
}
}
else if (bNoCache || wcsicmp(szPath, szv6Path))
{
Wh_SetStringValue(PATHCACHE_VALNAME, szPath);
}

return hComCtl;
}

const WindhawkUtils::SYMBOL_HOOK comctl32DllHooks[] = {
{
{
L"public: int "
STHISCALL
L" CListGroup::GetGroupRect(struct tagRECT *)const "
},
&CListGroup_GetGroupRect_orig,
CListGroup_GetGroupRect_hook,
false
},
{
{
{
L"private: void "
STHISCALL
L" CListGroup::_PaintHeader(unsigned long,struct tagNMLVCUSTOMDRAW *)"
},
(void **)&CListGroup__PaintHeader_orig,
(void *)CListGroup__PaintHeader_hook,
false
L"private: void "
STHISCALL
L" CListGroup::_PaintHeader(unsigned long,struct tagNMLVCUSTOMDRAW *)"
},
&CListGroup__PaintHeader_orig,
CListGroup__PaintHeader_hook,
false
},
{
{
{
L"public: int "
STHISCALL
L" CListGroup::HeaderHeight(void)const "
},
(void **)&CListGroup_HeaderHeight_orig,
(void *)CListGroup_HeaderHeight_hook,
false
}
};
L"public: int "
STHISCALL
L" CListGroup::HeaderHeight(void)const "
},
&CListGroup_HeaderHeight_orig,
CListGroup_HeaderHeight_hook,
false
},
{
{
L"private: void "
STHISCALL
L" CListGroup::_PaintCollapse(struct tagNMLVCUSTOMDRAW *)"
},
&CListGroup__PaintCollapse_orig,
CListGroup__PaintCollapse_hook,
false
}
};

void Wh_ModSettingsChanged(void)
{
g_fHideCollapse = Wh_GetIntSetting(L"hidecollapse");
}

BOOL Wh_ModInit(void)
{
GetIconFont(&g_lfIconFont);
g_fHideCollapse = Wh_GetIntSetting(L"hidecollapse");

HMODULE hComCtl = LoadComCtlModule();
if (!hComCtl)
{
Wh_Log(L"Failed to load comctl32.dll");
return FALSE;
}

if (!WindhawkUtils::HookSymbols(
hComCtl,
hooks,
ARRAYSIZE(hooks)
comctl32DllHooks,
ARRAYSIZE(comctl32DllHooks)
))
{
Wh_Log(L"Failed to hook one or more symbol functions");
Expand Down