From b199e9d05f3f9722ac419c6a72e3a81f444617f4 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 22 Jun 2025 09:52:37 +0900 Subject: [PATCH] [SHLWAPI][SDK][BOOTDATA] Implement SHGetAppCompatFlags (#8137) Compatibility is a key that the system works well. JIRA issue: CORE-19278 - Add appcompat.c. - Implement SHGetAppCompatFlags function. - Add Str_SetPtrA prototype to . - Add SHACF_... flags to . - Add comctl32 delay import (for Str_SetPtrA). - Modify boot/bootdata/hivesft.inf for registry key HRESULTKLM\SOFTWARE\Microsoft\ Windows\CurrentVersion\ ShellCompatibility\Applications. --- boot/bootdata/hivesft.inf | 79 +++++ dll/win32/shlwapi/CMakeLists.txt | 3 +- dll/win32/shlwapi/appcompat.c | 472 ++++++++++++++++++++++++++++ dll/win32/shlwapi/ordinal.c | 4 +- sdk/include/psdk/commctrl.h | 12 + sdk/include/reactos/shlwapi_undoc.h | 30 +- 6 files changed, 595 insertions(+), 5 deletions(-) create mode 100644 dll/win32/shlwapi/appcompat.c diff --git a/boot/bootdata/hivesft.inf b/boot/bootdata/hivesft.inf index d5a40479801..8248b441e20 100644 --- a/boot/bootdata/hivesft.inf +++ b/boot/bootdata/hivesft.inf @@ -222,6 +222,85 @@ HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies",,0x00000012 HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer",,0x00000012 HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer","NoSimpleStartMenu",0x00010003,1 +; Application compatibility +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coolcat.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coolcat.exe","Version",2,"4.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coolcat.exe","ANSIDISPLAYNAMES",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coreldrw.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coreldrw.exe","Version",2,"9.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\coreldrw.exe","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","Version",2,"3.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","MYCOMPUTERFIRST",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","WIN95DEFVIEW",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","OLDCREATEVIEWWND",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","IGNOREENUMRESET",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\creatr32.exe","RequiredFile",2,"trkwrite.dll" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\cutftp32.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\cutftp32.exe","Version",2,"3.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\cutftp32.exe","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\diskjockey.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\diskjockey.exe","Version",2,"2,*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\diskjockey.exe","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 6.*",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 6.*","Version",2,"6.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 6.*","FILEOPENBOGUSCTRLID",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 7.01",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 7.01","Version",2,"7.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\dlwin.exe\DownLoader 7.01","FILEOPENBOGUSCTRLID",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\fusion.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\fusion.exe","Version",2,"5.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\fusion.exe","FILEOPENNEEDSEXT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\install.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\install.exe\Starcraft 1.03",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\install.exe\Starcraft 1.03","RequiredFile",2,"help\\star.htm" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\install.exe\Starcraft 1.03","Version",2,"Version 1.03" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\install.exe\Starcraft 1.03","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\msdev.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\msdev.exe","Version",2,"6.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\msdev.exe","FILEOPENNEEDSEXT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 6.*",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 6.*","Version",2,"6.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 6.*","FILEOPENBOGUSCTRLID",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 7.01",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 7.01","Version",2,"7.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\mswin.exe\Metastock 7.01","FILEOPENBOGUSCTRLID",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\photoed.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\photoed.exe","Version",2,"3.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\photoed.exe","FILEOPENNEEDSEXT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\pwrplay.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\pwrplay.exe","Version",2,"6.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\pwrplay.exe","FILEOPENBOGUSCTRLID",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe","Version",2,"1,*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe","WIN95BINDTOOBJECT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe\Version 2",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe\Version 2","Version",2,"2.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\qstitch.exe\Version 2","WIN95BINDTOOBJECT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\recycle.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\recycle.exe","Version",2,"1,*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\recycle.exe","FILEOPENNEEDSEXT",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\sesapp.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\sesapp.exe","Version",2,"3.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\sesapp.exe","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\snapshot.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\snapshot.exe","RequiredFile",2,"ssres.dll" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\snapshot.exe","Version",2,"1.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\snapshot.exe","LOADCOLUMNHANDLER",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\trace.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\trace.exe","Version",2,"9.*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\trace.exe","OLDREGITEMGDN",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\uni.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\uni.exe","Version",2,"5, 5,*" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\uni.exe","ANSIDISPLAYNAMES",2,"" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\vcafe.exe",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\vcafe.exe","Version",2,"4.0" +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\vcafe.exe","FORCELFNIDLIST",2,"" + ; Extra Control Panel Applets HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls",,0x00000012 HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls","Console",0x00020000,"%SystemRoot%\system32\console.dll" diff --git a/dll/win32/shlwapi/CMakeLists.txt b/dll/win32/shlwapi/CMakeLists.txt index 80e14eecc7e..6bd20ce40d9 100644 --- a/dll/win32/shlwapi/CMakeLists.txt +++ b/dll/win32/shlwapi/CMakeLists.txt @@ -8,6 +8,7 @@ add_definitions( spec2def(shlwapi.dll shlwapi.spec ADD_IMPORTLIB) list(APPEND SOURCE + appcompat.c clist.c istream.c msgbox.c @@ -50,7 +51,7 @@ add_dependencies(shlwapi_autocomp psdk) set_module_type(shlwapi win32dll UNICODE) target_link_libraries(shlwapi uuid wine cpprt) -add_delay_importlibs(shlwapi userenv oleaut32 ole32 comdlg32 mpr mlang urlmon shell32 winmm version) +add_delay_importlibs(shlwapi userenv oleaut32 ole32 comctl32 comdlg32 mpr mlang urlmon shell32 winmm version) add_importlibs(shlwapi user32 gdi32 advapi32 wininet msvcrt kernel32 ntdll) add_pch(shlwapi precomp.h "${PCH_SKIP_SOURCE}") add_cd_file(TARGET shlwapi DESTINATION reactos/system32 FOR all) diff --git a/dll/win32/shlwapi/appcompat.c b/dll/win32/shlwapi/appcompat.c new file mode 100644 index 00000000000..b8adb7a134f --- /dev/null +++ b/dll/win32/shlwapi/appcompat.c @@ -0,0 +1,472 @@ +/* + * PROJECT: ReactOS Shell + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Shell application compatibility flags + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ + */ + +#include "precomp.h" +#include +#include +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +// Indicates if the compatibility system has been initialized +static BOOL g_fAppCompatInitialized = FALSE; + +static DWORD g_dwAppCompatFlags = 0; // Cached compatibility flags + +// SHACF flags and their corresponding names +typedef struct tagFLAGMAP +{ + DWORD flags; + LPCSTR name; +} FLAGMAP, *PFLAGMAP; +static FLAGMAP g_appCompatFlagMaps[] = +{ + { SHACF_CONTEXTMENU, "CONTEXTMENU" }, + { SHACF_CORELINTERNETENUM, "CORELINTERNETENUM" }, + { SHACF_OLDCREATEVIEWWND, "OLDCREATEVIEWWND" }, + { SHACF_WIN95DEFVIEW, "WIN95DEFVIEW" }, + { SHACF_DOCOBJECT, "DOCOBJECT" }, + { SHACF_FLUSHNOWAITALWAYS, "FLUSHNOWAITALWAYS" }, + { SHACF_MYCOMPUTERFIRST, "MYCOMPUTERFIRST" }, + { SHACF_OLDREGITEMGDN, "OLDREGITEMGDN" }, + { SHACF_LOADCOLUMNHANDLER, "LOADCOLUMNHANDLER" }, + { SHACF_ANSI, "ANSI" }, + { SHACF_STAROFFICE5PRINTER, "STAROFFICE5PRINTER" }, + { SHACF_NOVALIDATEFSIDS, "NOVALIDATEFSIDS" }, + { SHACF_WIN95SHLEXEC, "WIN95SHLEXEC" }, + { SHACF_FILEOPENNEEDSEXT, "FILEOPENNEEDSEXT" }, + { SHACF_WIN95BINDTOOBJECT, "WIN95BINDTOOBJECT" }, + { SHACF_IGNOREENUMRESET, "IGNOREENUMRESET" }, + { SHACF_ANSIDISPLAYNAMES, "ANSIDISPLAYNAMES" }, + { SHACF_FILEOPENBOGUSCTRLID, "FILEOPENBOGUSCTRLID" }, + { SHACF_FORCELFNIDLIST, "FORCELFNIDLIST" }, +}; + +// Get compatibility flags from registry values +static DWORD +SHLWAPI_GetMappedFlags(_In_ HKEY hKey, _In_ const FLAGMAP *pEntries, _In_ UINT nEntries) +{ + DWORD flags = 0; + for (UINT iEntry = 0; iEntry < nEntries; ++iEntry) + { + DWORD error = SHGetValueA(hKey, NULL, pEntries[iEntry].name, NULL, NULL, NULL); + if (error == ERROR_SUCCESS) + flags |= pEntries[iEntry].flags; + } + return flags; +} + +#define MAJOR_VER_ONLY "\x01" // A mark to indicate that only major version will be compared. + +// App compatibility info +// Extracted from: https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/util/getappcompatflags.htm +typedef struct tagAPPCOMPATINFO +{ + PCSTR pszAppName; + PCSTR pszAppVersion; + DWORD dwCompatFlags; +} APPCOMPATINFO, *PAPPCOMPATINFO; +static APPCOMPATINFO g_appCompatInfo[] = +{ + { "abcmm.exe", NULL, SHACF_LOADCOLUMNHANDLER }, + { "autorun.exe", "4.00.950", SHACF_ANSI }, + { "autorun.exe", "4.10.1998", SHACF_ANSI }, + { "coreldrw.exe", MAJOR_VER_ONLY "7", SHACF_OLDREGITEMGDN }, + { "dad9.exe", MAJOR_VER_ONLY "9", SHACF_CORELINTERNETENUM }, + { "filler51.exe", NULL, SHACF_OLDREGITEMGDN }, + { "hotdog4.exe", NULL, SHACF_DOCOBJECT }, + { "msmoney.exe", "7.05.1107", SHACF_WIN95SHLEXEC }, + { "pdexplo.exe", MAJOR_VER_ONLY "1", SHACF_CONTEXTMENU | SHACF_MYCOMPUTERFIRST }, + { "pdexplo.exe", MAJOR_VER_ONLY "2", SHACF_CONTEXTMENU | SHACF_MYCOMPUTERFIRST }, + { "pdexplo.exe", MAJOR_VER_ONLY "3", SHACF_MYCOMPUTERFIRST | SHACF_OLDREGITEMGDN }, + { "pdxwin32.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "pfim80.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "powerpnt.exe", MAJOR_VER_ONLY "8", SHACF_WIN95SHLEXEC }, + { "pp70.exe", NULL, SHACF_LOADCOLUMNHANDLER }, + { "pp80.exe", NULL, SHACF_LOADCOLUMNHANDLER }, + { "prwin70.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM }, + { "prwin8.exe", NULL, SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "prwin9.exe", MAJOR_VER_ONLY "9", SHACF_CORELINTERNETENUM }, + { "ps80.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "ps80.exe", NULL, SHACF_OLDREGITEMGDN }, + { "qfinder.exe", NULL, SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "qpw.exe", MAJOR_VER_ONLY "7", SHACF_CONTEXTMENU }, + { "qpw.exe", MAJOR_VER_ONLY "8", SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN | SHACF_ANSIDISPLAYNAMES }, + { "qpw.exe", MAJOR_VER_ONLY "9", SHACF_CORELINTERNETENUM }, + { "rnaapp.exe", NULL, SHACF_CONTEXTMENU }, + { "sitebuilder.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "sizemgr.exe", MAJOR_VER_ONLY "3", SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "smartctr.exe", "96.0", SHACF_CONTEXTMENU }, + { "soffice.exe", MAJOR_VER_ONLY "5", SHACF_STAROFFICE5PRINTER }, + { "ua80.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "ue32.exe", "2.00.0.0", SHACF_OLDREGITEMGDN }, + { "wpwin7.exe", NULL, SHACF_CONTEXTMENU | SHACF_CORELINTERNETENUM }, + { "wpwin8.exe", NULL, SHACF_CORELINTERNETENUM | SHACF_OLDREGITEMGDN }, + { "wpwin9.exe", MAJOR_VER_ONLY "9", SHACF_CORELINTERNETENUM }, +}; + +// Window class name and compatibility flags +typedef struct tagWNDCOMPATINFO +{ + PCSTR pszLengthAndClassName; + DWORD dwCompatFlags; +} WNDCOMPATINFO, *PWNDCOMPATINFO; +static WNDCOMPATINFO g_wndCompatInfo[] = +{ + // The first byte indicates the string length + { "\x09" "bosa_sdm_", SHACF_UNKNOWN1 | SHACF_UNKNOWN2 }, + { "\x18" "File Open Message Window", SHACF_UNKNOWN1 | SHACF_UNKNOWN2 }, +}; + +// Internal structure for SHLWAPI_WndCompatEnumProc +typedef struct tagAPPCOMPATENUM +{ + PWNDCOMPATINFO pItems; + SIZE_T nItems; + DWORD dwProcessId; + INT iFound; +} APPCOMPATENUM, *PAPPCOMPATENUM; + +static BOOL CALLBACK +SHLWAPI_WndCompatEnumProc(_In_ HWND hWnd, _In_ LPARAM lParam) +{ + PAPPCOMPATENUM pEnum = (PAPPCOMPATENUM)lParam; + + CHAR szClass[256]; + if (!pEnum->nItems || !GetClassNameA(hWnd, szClass, _countof(szClass))) + return TRUE; // Ignore and continue + + // Search for the target window in pEnum + const INT cchClass = lstrlenA(szClass); + for (UINT iItem = 0; iItem < pEnum->nItems; ++iItem) + { + PCSTR pszLengthAndClassName = pEnum->pItems[iItem].pszLengthAndClassName; + + INT cchLength = pszLengthAndClassName[0]; // The first byte represents the length + if (cchClass < cchLength) + cchLength = cchClass; // Ignore trailing characters + + // Compare the string + if (StrCmpNA(szClass, &pszLengthAndClassName[1], cchLength) == 0) // Class name matched + { + // Get the process ID + DWORD dwProcessId; + GetWindowThreadProcessId(hWnd, &dwProcessId); + if (dwProcessId == pEnum->dwProcessId) // Same process + { + pEnum->iFound = iItem; // Found + return FALSE; // Quit enumeration + } + } + } + + return TRUE; // Continue enumeration +} + +// English (US) UTF-16 +#define PRODUCT_VER_ENGLISH_US_UTF16 "\\StringFileInfo\\040904E4\\ProductVersion" +// German (Germany) UTF-16 +#define PRODUCT_VER_GERMAN_UTF16 "\\StringFileInfo\\040704E4\\ProductVersion" +// English (US) Western European +#define PRODUCT_VER_ENGLISH_US_WE "\\StringFileInfo\\040904B0\\ProductVersion" +// English (US) Neutral +#define PRODUCT_VER_ENGLISH_US_NEUTRAL "\\StringFileInfo\\04090000\\ProductVersion" +// Swedish (Sweden) Western European +#define PRODUCT_VER_SWEDISH_WE "\\StringFileInfo\\041D04B0\\ProductVersion" + +typedef struct tagLANGANDCODEPAGE +{ + WORD wLanguage; + WORD wCodePage; +} LANGANDCODEPAGE, *PLANGANDCODEPAGE; + +static HRESULT +SHLWAPI_GetModuleVersion(_In_ PCSTR pszFileName, _Out_ PSTR *ppszDest) +{ + DWORD dwHandle; + PBYTE pbData; + PVOID pvData; + + *ppszDest = NULL; + + // Obtain the version info + UINT size = GetFileVersionInfoSizeA(pszFileName, &dwHandle); + if (!size) + { + TRACE("No version info\n"); + return E_FAIL; + } + pbData = (PBYTE)LocalAlloc(LPTR, size); + if (!pbData) + { + ERR("E_OUTOFMEMORY\n"); + return E_OUTOFMEMORY; + } + GetFileVersionInfoA(pszFileName, dwHandle, size, pbData); + + // Getting the product version + HRESULT hr = E_FAIL; + if ((VerQueryValueA(pbData, PRODUCT_VER_ENGLISH_US_UTF16, &pvData, &size) || + VerQueryValueA(pbData, PRODUCT_VER_GERMAN_UTF16, &pvData, &size) || + VerQueryValueA(pbData, PRODUCT_VER_ENGLISH_US_WE, &pvData, &size) || + VerQueryValueA(pbData, PRODUCT_VER_ENGLISH_US_NEUTRAL, &pvData, &size) || + VerQueryValueA(pbData, PRODUCT_VER_SWEDISH_WE, &pvData, &size)) && size) + { + // NOTE: *ppszDest must be freed using LocalFree later + *ppszDest = StrDupA((PSTR)pvData); + hr = *ppszDest ? S_OK : E_OUTOFMEMORY; + } + else if (VerQueryValueA(pbData, "\\VarFileInfo\\Translation", &pvData, &size)) + { + PVOID pDataSaved = pvData; + PLANGANDCODEPAGE pEntry = (PLANGANDCODEPAGE)pvData; + for (; (PBYTE)pEntry + sizeof(LANGANDCODEPAGE) <= (PBYTE)pDataSaved + size; ++pEntry) + { + CHAR szPath[MAX_PATH]; + wnsprintfA(szPath, _countof(szPath), "\\StringFileInfo\\%04X%04X\\ProductVersion", + pEntry->wLanguage, pEntry->wCodePage); + if (VerQueryValueA(pbData, szPath, &pvData, &size) && size) + { + // NOTE: *ppszDest must be freed using LocalFree later + *ppszDest = StrDupA((PSTR)pvData); + hr = *ppszDest ? S_OK : E_OUTOFMEMORY; + } + } + } + + if (FAILED(hr)) + WARN("hr: 0x%lX\n", hr); + + LocalFree(pbData); + return hr; +} + +static BOOL +SHLWAPI_DoesModuleVersionMatch(_In_ PCSTR pszFileName, _In_opt_ PCSTR pszVersionPattern) +{ + if (!pszVersionPattern) + return TRUE; + + PSTR pszModuleVersion = NULL; + HRESULT hr = SHLWAPI_GetModuleVersion(pszFileName, &pszModuleVersion); + if (FAILED(hr)) + return FALSE; + + BOOL ret = FALSE; + if (pszVersionPattern[0] == MAJOR_VER_ONLY[0]) // Special handling for major version only + { + // Truncate at comma (',') if present + PSTR pchComma = StrChrA(pszModuleVersion, ','); + if (pchComma) + *pchComma = ANSI_NULL; + + // Truncate at dot ('.') if present + PSTR pchDot = StrChrA(pszModuleVersion, '.'); + if (pchDot) + *pchDot = ANSI_NULL; + + ret = (lstrcmpiA(pszModuleVersion, &pszVersionPattern[1]) == 0); + } + else // Otherwise, perform a normal match + { + PSTR pchAsterisk = StrChrA(pszVersionPattern, '*'); // Find an asterisk ('*') + if (pchAsterisk) // Asterisk found + { + // Check for a match, ignoring the substring after '*' + INT cchPrefix = (INT)(pchAsterisk - pszVersionPattern); + if (cchPrefix > 0) + ret = (StrCmpNIA(pszModuleVersion, pszVersionPattern, cchPrefix) == 0); + } + + if (!ret) + ret = (lstrcmpiA(pszModuleVersion, pszVersionPattern) == 0); // Full match + } + + LocalFree(pszModuleVersion); + return ret; +} + +static DWORD +SHLWAPI_GetRegistryCompatFlags(_In_ PCSTR pszPath) +{ + // Build the path to the "application compatibility" registry key + CHAR szText[MAX_PATH]; + wnsprintfA(szText, _countof(szText), + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Applications\\%s", + PathFindFileNameA(pszPath)); + + // Open the key + HKEY hKey; + const REGSAM samDesired = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS; + LSTATUS error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, szText, 0, samDesired, &hKey); + if (error != ERROR_SUCCESS) + { + ERR("error: %lu\n", error); + return 0; // Failed + } + + // Build the base directory + CHAR szBaseDir[MAX_PATH]; + lstrcpynA(szBaseDir, pszPath, _countof(szBaseDir)); + PathRemoveFileSpecA(szBaseDir); + + // Search from the registry key + DWORD dwCompatFlags = 0; + szText[0] = ANSI_NULL; // The first attempt is for the non-subkey path + for (DWORD dwIndex = 0; error == ERROR_SUCCESS;) + { + HKEY hSubKey; + error = RegOpenKeyExA(hKey, szText, 0, KEY_QUERY_VALUE, &hSubKey); + if (error != ERROR_SUCCESS) + break; + + // Get the "RequiredFile" value + CHAR szRequired[MAX_PATH]; + DWORD cbData = sizeof(szRequired); + error = SHGetValueA(hSubKey, NULL, "RequiredFile", NULL, szRequired, &cbData); + BOOL bValueExists = (error == ERROR_SUCCESS); + BOOL bRequiredFileExists = FALSE; + if (bValueExists) // Does the "RequiredFile" value exist? + { + // Build required file path to szText + PathCombineA(szText, szBaseDir, szRequired); + TRACE("RequiredFile: %s\n", wine_dbgstr_a(szRequired)); + TRACE("szText: %s\n", wine_dbgstr_a(szText)); + // Now szText is a full path + bRequiredFileExists = (GetFileAttributesA(szText) != INVALID_FILE_ATTRIBUTES); + } + + // If the "RequiredFile" value doesn't exist, or the file specified by szText exists: + if (!bValueExists || bRequiredFileExists) + { + // Check the "Version" value if necessary + error = SHGetValueA(hSubKey, NULL, "Version", NULL, szText, &cbData); + PCSTR pszVersionPattern = ((error == ERROR_SUCCESS) ? szText : NULL); + TRACE("pszVersionPattern: %s\n", wine_dbgstr_a(pszVersionPattern)); + + // Does the pattern match? + if (SHLWAPI_DoesModuleVersionMatch(pszPath, pszVersionPattern)) + { + // Add additional flags from the registry key + dwCompatFlags |= SHLWAPI_GetMappedFlags(hSubKey, g_appCompatFlagMaps, + _countof(g_appCompatFlagMaps)); + } + } + + RegCloseKey(hSubKey); + + // Go to the next sub-key + ++dwIndex; + error = RegEnumKeyA(hKey, dwIndex, szText, _countof(szText)); + } + + RegCloseKey(hKey); + + return dwCompatFlags; +} + +static DWORD +SHLWAPI_InitAppCompat(VOID) +{ + if (GetProcessVersion(0) >= MAKELONG(0, 5)) + return 0; // Flags are not needed + + // Get module pathname + CHAR szModulePathA[MAX_PATH]; + if (!GetModuleFileNameA(NULL, szModulePathA, _countof(szModulePathA))) + return 0; + + PCSTR pszFileName = PathFindFileNameA(szModulePathA); // Get the file title from the path + + // Search the file title from g_appCompatInfo + DWORD dwAppCompatFlags = 0; + for (UINT iItem = 0; iItem < _countof(g_appCompatInfo); ++iItem) + { + const APPCOMPATINFO *pInfo = &g_appCompatInfo[iItem]; + if (lstrcmpiA(pInfo->pszAppName, pszFileName) == 0 && + SHLWAPI_DoesModuleVersionMatch(pszFileName, pInfo->pszAppVersion)) + { + // Found, set the flags + dwAppCompatFlags = g_appCompatInfo[iItem].dwCompatFlags; + break; + } + } + + // Add additional flags from the registry + dwAppCompatFlags |= SHLWAPI_GetRegistryCompatFlags(pszFileName); + + return dwAppCompatFlags; +} + +// These flags require SHLWAPI_InitAppCompat +#define SHACF_TO_INIT ( \ + SHACF_CONTEXTMENU | \ + SHACF_DOCOBJECT | \ + SHACF_CORELINTERNETENUM | \ + SHACF_MYCOMPUTERFIRST | \ + SHACF_OLDREGITEMGDN | \ + SHACF_LOADCOLUMNHANDLER | \ + SHACF_ANSI | \ + SHACF_WIN95SHLEXEC | \ + SHACF_STAROFFICE5PRINTER | \ + SHACF_NOVALIDATEFSIDS | \ + SHACF_FILEOPENNEEDSEXT | \ + SHACF_WIN95BINDTOOBJECT | \ + SHACF_IGNOREENUMRESET | \ + SHACF_ANSIDISPLAYNAMES | \ + SHACF_FILEOPENBOGUSCTRLID | \ + SHACF_FORCELFNIDLIST \ +) + +/************************************************************************* + * SHGetAppCompatFlags [SHLWAPI.461] + * + * Thanks to Geoff Chappell. + * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/util/getappcompatflags.htm + */ +DWORD WINAPI +SHGetAppCompatFlags(_In_ DWORD dwMask) +{ + TRACE("(0x%lX)\n", dwMask); + + // Initialize and retrieve flags if necessary + if (dwMask & SHACF_TO_INIT) + { + if (!g_fAppCompatInitialized) + { + DWORD dwAppCompatFlags = SHLWAPI_InitAppCompat(); + InterlockedExchange((PLONG)&g_dwAppCompatFlags, dwAppCompatFlags); + + g_fAppCompatInitialized = TRUE; // Mark as initialized + } + } + + // Retrieve additional flags if necessary + DWORD dwAppCompatFlags = g_dwAppCompatFlags; + if (dwAppCompatFlags && (dwMask & (SHACF_UNKNOWN1 | SHACF_UNKNOWN2))) + { + // Find the target window and its flags using g_wndCompatInfo + APPCOMPATENUM data = + { + g_wndCompatInfo, _countof(g_wndCompatInfo), GetCurrentProcessId(), -1 + }; + EnumWindows(SHLWAPI_WndCompatEnumProc, (LPARAM)&data); + + // Add the target flags if a match is found + if (data.iFound >= 0) + dwAppCompatFlags |= g_wndCompatInfo[data.iFound].dwCompatFlags; + + dwAppCompatFlags |= SHACF_UNKNOWN3; + + InterlockedExchange((PLONG)&g_dwAppCompatFlags, dwAppCompatFlags); + } + + return dwAppCompatFlags; +} + +// FIXME: SHGetObjectCompatFlags diff --git a/dll/win32/shlwapi/ordinal.c b/dll/win32/shlwapi/ordinal.c index 5b6e842a4e8..1ad9aefc2bb 100644 --- a/dll/win32/shlwapi/ordinal.c +++ b/dll/win32/shlwapi/ordinal.c @@ -4725,7 +4725,7 @@ VOID WINAPI FixSlashesAndColonW(LPWSTR lpwstr) } } - +#ifndef __REACTOS__ /* See appcompat.c */ /************************************************************************* * @ [SHLWAPI.461] */ @@ -4734,7 +4734,7 @@ DWORD WINAPI SHGetAppCompatFlags(DWORD dwUnknown) FIXME("(0x%08x) stub\n", dwUnknown); return 0; } - +#endif /************************************************************************* * @ [SHLWAPI.549] diff --git a/sdk/include/psdk/commctrl.h b/sdk/include/psdk/commctrl.h index 989342c71d6..ae7a8eb1d63 100644 --- a/sdk/include/psdk/commctrl.h +++ b/sdk/include/psdk/commctrl.h @@ -4879,12 +4879,24 @@ typedef struct { _In_ LPARAM lParam, _In_ UINT options); + WINCOMMCTRLAPI + BOOL + WINAPI Str_SetPtrA( + _Inout_ LPSTR *ppsz, + _In_opt_ LPCSTR psz); + WINCOMMCTRLAPI BOOL WINAPI Str_SetPtrW( _Inout_ LPWSTR *ppsz, _In_opt_ LPCWSTR psz); +#ifdef UNICODE +#define Str_SetPtr Str_SetPtrW +#else +#define Str_SetPtr Str_SetPtrA +#endif + typedef struct _DPASTREAMINFO { int iPos; void *pvItem; diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index 28aac00b281..3e5751b38c9 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -397,8 +397,34 @@ HRESULT WINAPI SHRunIndirectRegClientCommand(_In_ HWND hWnd, _In_ PCWSTR pszClie DWORD WINAPI SHGetObjectCompatFlags(IUnknown *pUnk, const CLSID *clsid); -#define SHACF_WIN95SHLEXEC 0x00000200 /* Geoff Chappell */ -DWORD WINAPI SHGetAppCompatFlags(DWORD dwMask); +/* Flags for SHGetAppCompatFlags */ +#define SHACF_CONTEXTMENU 0x00000001 +#define SHACF_FLUSHNOWAITALWAYS SHACF_CONTEXTMENU +#define SHACF_DOCOBJECT 0x00000002 +#define SHACF_CORELINTERNETENUM 0x00000004 +#define SHACF_OLDCREATEVIEWWND SHACF_CORELINTERNETENUM +#define SHACF_WIN95DEFVIEW SHACF_CORELINTERNETENUM +#define SHACF_MYCOMPUTERFIRST 0x00000008 +#define SHACF_OLDREGITEMGDN 0x00000010 +// 0x00000020 +#define SHACF_LOADCOLUMNHANDLER 0x00000040 +#define SHACF_ANSI 0x00000080 +#define SHACF_UNKNOWN1 0x00000100 +#define SHACF_WIN95SHLEXEC 0x00000200 +#define SHACF_STAROFFICE5PRINTER 0x00000400 +#define SHACF_NOVALIDATEFSIDS 0x00000800 +#define SHACF_FILEOPENNEEDSEXT 0x00001000 +#define SHACF_WIN95BINDTOOBJECT 0x00002000 +#define SHACF_IGNOREENUMRESET 0x00004000 +// 0x00008000 +#define SHACF_ANSIDISPLAYNAMES 0x00010000 +#define SHACF_FILEOPENBOGUSCTRLID 0x00020000 +#define SHACF_FORCELFNIDLIST 0x00040000 +// 0x00080000 +#define SHACF_UNKNOWN2 0x01000000 +#define SHACF_UNKNOWN3 0x80000000 + +DWORD WINAPI SHGetAppCompatFlags(_In_ DWORD dwMask); /***************************************************************************** * IAssociationElementOld interface