mirror of
https://github.com/valinet/ExplorerPatcher.git
synced 2024-11-15 03:27:35 +01:00
File Explorer: Add support for classic drive groupings in This PC
This commit is contained in:
parent
21f3377a27
commit
3f96325ad6
@ -111,6 +111,12 @@ BEGIN
|
||||
IDS_INSTALL_SUCCESS_TEXT "Installation succeeded."
|
||||
IDS_INSTALL_ERROR_TEXT "Installation failed."
|
||||
IDS_UNINSTALL_SUCCESS_TEXT "Uninstallation succeeded."
|
||||
IDS_DRIVECATEGORY_HARDDISKDRIVES "Hard Disk Drives"
|
||||
IDS_DRIVECATEGORY_REMOVABLESTORAGE "Devices with Removable Storage"
|
||||
IDS_DRIVECATEGORY_OTHER "Other"
|
||||
IDS_DRIVECATEGORY_IMAGING "Scanners and Cameras"
|
||||
IDS_DRIVECATEGORY_PORTABLEMEDIADEVICE "Portable Media Devices"
|
||||
IDS_DRIVECATEGORY_PORTABLEDEVICE "Portable Devices"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <Uxtheme.h>
|
||||
#pragma comment(lib, "UxTheme.lib")
|
||||
#include <Shlobj_core.h>
|
||||
#include <propvarutil.h>
|
||||
#pragma comment(lib, "Propsys.lib")
|
||||
#include <commctrl.h>
|
||||
#pragma comment(lib, "Comctl32.lib")
|
||||
#include <dwmapi.h>
|
||||
@ -78,6 +80,7 @@ DWORD bEnableArchivePlugin = FALSE;
|
||||
DWORD bMonitorOverride = TRUE;
|
||||
DWORD bOpenAtLogon = FALSE;
|
||||
DWORD bClockFlyoutOnWinC = FALSE;
|
||||
DWORD bUseClassicDriveGrouping = FALSE;
|
||||
DWORD bDisableImmersiveContextMenu = FALSE;
|
||||
DWORD bClassicThemeMitigations = FALSE;
|
||||
DWORD bWasClassicThemeMitigationsSet = FALSE;
|
||||
@ -106,6 +109,7 @@ DWORD bNoPropertiesInContextMenu = FALSE;
|
||||
DWORD dwTaskbarGlomLevel = TASKBARGLOMLEVEL_DEFAULT;
|
||||
DWORD dwMMTaskbarGlomLevel = MMTASKBARGLOMLEVEL_DEFAULT;
|
||||
HMODULE hModule = NULL;
|
||||
HANDLE hShell32 = NULL;
|
||||
HANDLE hDelayedInjectionThread = NULL;
|
||||
HANDLE hIsWinXShown = NULL;
|
||||
HANDLE hWinXThread = NULL;
|
||||
@ -5859,6 +5863,15 @@ void WINAPI LoadSettings(LPARAM lParam)
|
||||
&dwSize
|
||||
);
|
||||
dwSize = sizeof(DWORD);
|
||||
RegQueryValueExW(
|
||||
hKey,
|
||||
TEXT("UseClassicDriveGrouping"),
|
||||
0,
|
||||
NULL,
|
||||
&bUseClassicDriveGrouping,
|
||||
&dwSize
|
||||
);
|
||||
dwSize = sizeof(DWORD);
|
||||
RegQueryValueExW(
|
||||
hKey,
|
||||
TEXT("DisableImmersiveContextMenu"),
|
||||
@ -7905,6 +7918,252 @@ HINSTANCE explorer_ShellExecuteW(
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#pragma region "Classic Drive Grouping"
|
||||
|
||||
const struct { DWORD dwDescriptionId; UINT uResourceId; } driveCategoryMap[] = {
|
||||
{ SHDID_FS_DIRECTORY, 9338 }, //shell32
|
||||
{ SHDID_COMPUTER_SHAREDDOCS, 9338 }, //shell32
|
||||
{ SHDID_COMPUTER_FIXED, IDS_DRIVECATEGORY_HARDDISKDRIVES },
|
||||
{ SHDID_COMPUTER_DRIVE35, IDS_DRIVECATEGORY_REMOVABLESTORAGE },
|
||||
{ SHDID_COMPUTER_REMOVABLE, IDS_DRIVECATEGORY_REMOVABLESTORAGE },
|
||||
{ SHDID_COMPUTER_CDROM, IDS_DRIVECATEGORY_REMOVABLESTORAGE },
|
||||
{ SHDID_COMPUTER_DRIVE525, IDS_DRIVECATEGORY_REMOVABLESTORAGE },
|
||||
{ SHDID_COMPUTER_NETDRIVE, 9340 }, //shell32
|
||||
{ SHDID_COMPUTER_OTHER, IDS_DRIVECATEGORY_OTHER },
|
||||
{ SHDID_COMPUTER_RAMDISK, IDS_DRIVECATEGORY_OTHER },
|
||||
{ SHDID_COMPUTER_IMAGING, IDS_DRIVECATEGORY_IMAGING },
|
||||
{ SHDID_COMPUTER_AUDIO, IDS_DRIVECATEGORY_PORTABLEMEDIADEVICE },
|
||||
{ SHDID_MOBILE_DEVICE, IDS_DRIVECATEGORY_PORTABLEDEVICE }
|
||||
};
|
||||
|
||||
//Represents the true data structure that is returned from shell32!DllGetClassObject
|
||||
typedef struct {
|
||||
const IClassFactoryVtbl* lpVtbl;
|
||||
ULONG flags;
|
||||
REFCLSID rclsid;
|
||||
HRESULT(*pfnCreateInstance)(IUnknown*, REFIID, void**);
|
||||
} Shell32ClassFactoryEntry;
|
||||
|
||||
//Represents a custom ICategorizer/IShellExtInit
|
||||
typedef struct _EPCategorizer
|
||||
{
|
||||
ICategorizerVtbl* categorizer;
|
||||
IShellExtInitVtbl* shellExtInit;
|
||||
|
||||
ULONG ulRefCount;
|
||||
IShellFolder2* pShellFolder;
|
||||
} EPCategorizer;
|
||||
|
||||
#pragma region "EPCategorizer: ICategorizer"
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_ICategorizer_QueryInterface(ICategorizer* _this, REFIID riid, void** ppvObject)
|
||||
{
|
||||
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ICategorizer))
|
||||
{
|
||||
*ppvObject = _this;
|
||||
}
|
||||
else if (IsEqualIID(riid, &IID_IShellExtInit))
|
||||
{
|
||||
*ppvObject = &((EPCategorizer*) _this)->shellExtInit;
|
||||
}
|
||||
else
|
||||
{
|
||||
ppvObject = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
_this->lpVtbl->AddRef(_this);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE EPCategorizer_ICategorizer_AddRef(ICategorizer* _this)
|
||||
{
|
||||
return InterlockedIncrement(&((EPCategorizer*)_this)->ulRefCount);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE EPCategorizer_ICategorizer_Release(ICategorizer* _this)
|
||||
{
|
||||
ULONG ulNewCount = InterlockedDecrement(&((EPCategorizer*)_this)->ulRefCount);
|
||||
|
||||
//When the window is closed or refreshed the object is finally freed
|
||||
if (ulNewCount == 0)
|
||||
{
|
||||
EPCategorizer* epCategorizer = (EPCategorizer*)_this;
|
||||
|
||||
if (epCategorizer->pShellFolder != NULL)
|
||||
{
|
||||
epCategorizer->pShellFolder->lpVtbl->Release(epCategorizer->pShellFolder);
|
||||
epCategorizer->pShellFolder = NULL;
|
||||
}
|
||||
|
||||
free(epCategorizer);
|
||||
}
|
||||
|
||||
return ulNewCount;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_ICategorizer_GetDescription(ICategorizer* _this, LPWSTR pszDesc, UINT cch)
|
||||
{
|
||||
//As of writing returns the string "Type". Same implementation as shell32!CStorageSystemTypeCategorizer::GetDescription
|
||||
LoadStringW(hShell32, 0x3105, pszDesc, cch);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_ICategorizer_GetCategory(ICategorizer* _this, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD* rgCategoryIds)
|
||||
{
|
||||
EPCategorizer* epCategorizer = (EPCategorizer*)_this;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
for (UINT i = 0; i < cidl; i++)
|
||||
{
|
||||
rgCategoryIds[i] = IDS_DRIVECATEGORY_OTHER;
|
||||
|
||||
PROPERTYKEY key = { FMTID_ShellDetails, PID_DESCRIPTIONID };
|
||||
VARIANT variant;
|
||||
VariantInit(&variant);
|
||||
|
||||
hr = epCategorizer->pShellFolder->lpVtbl->GetDetailsEx(epCategorizer->pShellFolder, apidl[i], &key, &variant);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
SHDESCRIPTIONID did;
|
||||
|
||||
if (SUCCEEDED(VariantToBuffer(&variant, &did, sizeof(did))))
|
||||
{
|
||||
for (int j = 0; j < ARRAYSIZE(driveCategoryMap); j++)
|
||||
{
|
||||
if (did.dwDescriptionId == driveCategoryMap[j].dwDescriptionId)
|
||||
{
|
||||
rgCategoryIds[i] = driveCategoryMap[j].uResourceId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VariantClear(&variant);
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_ICategorizer_GetCategoryInfo(ICategorizer* _this, DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||||
{
|
||||
//Now retrieve the display name to use for the resource ID dwCategoryId.
|
||||
//pci is already populated with most of the information it needs, we just need to fill in the wszName
|
||||
if (!LoadStringW(hModule, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName)))
|
||||
LoadStringW(hShell32, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_ICategorizer_CompareCategory(ICategorizer* _this, CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||||
{
|
||||
//Typically a categorizer would use the resource IDs containing the names of each category as the "category ID" as well. In our case however, we're using
|
||||
//a combination of resource/category IDs provided by shell32 and resource/category IDs we're overriding ourselves. As a result, we are forced to compare
|
||||
//not by the value of the category/resource IDs themselves, but by their _position_ within our category ID map
|
||||
|
||||
int categoryArraySize = ARRAYSIZE(driveCategoryMap);
|
||||
|
||||
int firstPos = -1;
|
||||
int secondPos = -1;
|
||||
|
||||
for (int i = 0; i < categoryArraySize; i++)
|
||||
{
|
||||
if (driveCategoryMap[i].uResourceId == dwCategoryId1)
|
||||
{
|
||||
firstPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < categoryArraySize; i++)
|
||||
{
|
||||
if (driveCategoryMap[i].uResourceId == dwCategoryId2)
|
||||
{
|
||||
secondPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int diff = firstPos - secondPos;
|
||||
|
||||
if (diff < 0)
|
||||
return 0xFFFF;
|
||||
|
||||
return diff > 0;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
#pragma region "EPCategorizer: IShellExtInit"
|
||||
|
||||
//Adjustor Thunks: https://devblogs.microsoft.com/oldnewthing/20040206-00/?p=40723
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_IShellExtInit_QueryInterface(IShellExtInit* _this, REFIID riid, void** ppvObject)
|
||||
{
|
||||
return EPCategorizer_ICategorizer_QueryInterface((ICategorizer*)((char*)_this - sizeof(IShellExtInitVtbl*)), riid, ppvObject);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE EPCategorizer_IShellExtInit_AddRef(IShellExtInit* _this)
|
||||
{
|
||||
return EPCategorizer_ICategorizer_AddRef((ICategorizer*)((char*)_this - sizeof(IShellExtInitVtbl*)));
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE EPCategorizer_IShellExtInit_Release(IShellExtInit* _this)
|
||||
{
|
||||
return EPCategorizer_ICategorizer_Release((ICategorizer*)((char*)_this - sizeof(IShellExtInitVtbl*)));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EPCategorizer_IShellExtInit_Initialize(IShellExtInit* _this, PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID)
|
||||
{
|
||||
EPCategorizer* epCategorizer = (EPCategorizer*)((char*)_this - sizeof(IShellExtInitVtbl*));
|
||||
|
||||
return SHBindToObject(NULL, pidlFolder, NULL, &IID_IShellFolder2, (void**)&epCategorizer->pShellFolder);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
const ICategorizerVtbl EPCategorizer_categorizerVtbl = {
|
||||
EPCategorizer_ICategorizer_QueryInterface,
|
||||
EPCategorizer_ICategorizer_AddRef,
|
||||
EPCategorizer_ICategorizer_Release,
|
||||
EPCategorizer_ICategorizer_GetDescription,
|
||||
EPCategorizer_ICategorizer_GetCategory,
|
||||
EPCategorizer_ICategorizer_GetCategoryInfo,
|
||||
EPCategorizer_ICategorizer_CompareCategory
|
||||
};
|
||||
|
||||
const IShellExtInitVtbl EPCategorizer_shellExtInitVtbl = {
|
||||
EPCategorizer_IShellExtInit_QueryInterface,
|
||||
EPCategorizer_IShellExtInit_AddRef,
|
||||
EPCategorizer_IShellExtInit_Release,
|
||||
EPCategorizer_IShellExtInit_Initialize
|
||||
};
|
||||
|
||||
HRESULT(STDMETHODCALLTYPE *shell32_DriveTypeCategorizer_CreateInstanceFunc)(IUnknown* pUnkOuter, REFIID riid, void** ppvObject);
|
||||
|
||||
HRESULT shell32_DriveTypeCategorizer_CreateInstanceHook(IUnknown* pUnkOuter, REFIID riid, void** ppvObject)
|
||||
{
|
||||
if (IsEqualIID(riid, &IID_ICategorizer))
|
||||
{
|
||||
EPCategorizer* epCategorizer = (EPCategorizer*) malloc(sizeof(EPCategorizer));
|
||||
epCategorizer->categorizer = &EPCategorizer_categorizerVtbl;
|
||||
epCategorizer->shellExtInit = &EPCategorizer_shellExtInitVtbl;
|
||||
epCategorizer->ulRefCount = 1;
|
||||
epCategorizer->pShellFolder = NULL;
|
||||
|
||||
*ppvObject = epCategorizer;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return shell32_DriveTypeCategorizer_CreateInstanceFunc(pUnkOuter, riid, ppvObject);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#pragma region "Change language UI style"
|
||||
#ifdef _WIN64
|
||||
DEFINE_GUID(CLSID_InputSwitchControl,
|
||||
@ -9529,7 +9788,7 @@ DWORD Inject(BOOL bIsExplorer)
|
||||
#endif
|
||||
|
||||
|
||||
HANDLE hShell32 = GetModuleHandleW(L"shell32.dll");
|
||||
hShell32 = GetModuleHandleW(L"shell32.dll");
|
||||
if (hShell32)
|
||||
{
|
||||
HRESULT(*SHELL32_Create_IEnumUICommand)(IUnknown*, int*, int, IUnknown**) = GetProcAddress(hShell32, (LPCSTR)0x2E8);
|
||||
@ -9563,6 +9822,38 @@ DWORD Inject(BOOL bIsExplorer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bUseClassicDriveGrouping)
|
||||
{
|
||||
HRESULT(*SHELL32_DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv) = GetProcAddress(hShell32, "DllGetClassObject");
|
||||
|
||||
if (SHELL32_DllGetClassObject)
|
||||
{
|
||||
IClassFactory* pClassFactory;
|
||||
SHELL32_DllGetClassObject(&CLSID_DriveTypeCategorizer, &IID_IClassFactory, &pClassFactory);
|
||||
|
||||
if (pClassFactory)
|
||||
{
|
||||
//DllGetClassObject hands out a unique "factory entry" data structure for each type of CLSID, containing a pointer to an IClassFactoryVtbl as well as some other members including
|
||||
//the _true_ create instance function that should be called (in this instance, shell32!CDriveTypeCategorizer_CreateInstance). When the IClassFactory::CreateInstance method is called,
|
||||
//shell32!ECFCreateInstance will cast the IClassFactory* passed to it back into a factory entry, and then invoke the pfnCreateInstance function defined in that entry directly.
|
||||
//Thus, rather than hooking the shared shell32!ECFCreateInstance function found on the IClassFactoryVtbl* shared by all class objects returned by shell32!DllGetClassObject, we get the real
|
||||
//CreateInstance function that will be called and hook that instead
|
||||
Shell32ClassFactoryEntry* pClassFactoryEntry = (Shell32ClassFactoryEntry*)pClassFactory;
|
||||
|
||||
DWORD flOldProtect = 0;
|
||||
|
||||
if (VirtualProtect(pClassFactoryEntry, sizeof(Shell32ClassFactoryEntry), PAGE_EXECUTE_READWRITE, &flOldProtect))
|
||||
{
|
||||
shell32_DriveTypeCategorizer_CreateInstanceFunc = pClassFactoryEntry->pfnCreateInstance;
|
||||
pClassFactoryEntry->pfnCreateInstance = shell32_DriveTypeCategorizer_CreateInstanceHook;
|
||||
VirtualProtect(pClassFactoryEntry, sizeof(Shell32ClassFactoryEntry), flOldProtect, &flOldProtect);
|
||||
}
|
||||
|
||||
pClassFactory->lpVtbl->Release(pClassFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Setup shell32 functions done\n");
|
||||
|
||||
|
@ -16,6 +16,13 @@
|
||||
#define IDS_UNINSTALL_ERROR_TEXT 112
|
||||
#define IDS_OPERATION_NONE 113
|
||||
#define IDR_REGISTRY2 114
|
||||
#define IDS_DRIVECATEGORY_HARDDISKDRIVES 40000
|
||||
#define IDS_DRIVECATEGORY_REMOVABLESTORAGE 40001
|
||||
#define IDS_DRIVECATEGORY_OTHER 40002
|
||||
#define IDS_DRIVECATEGORY_IMAGING 40003
|
||||
#define IDS_DRIVECATEGORY_PORTABLEMEDIA 40004
|
||||
#define IDS_DRIVECATEGORY_PORTABLEMEDIADEVICE 40004
|
||||
#define IDS_DRIVECATEGORY_PORTABLEDEVICE 40005
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
|
@ -164,6 +164,9 @@
|
||||
[-HKEY_CURRENT_USER\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32]
|
||||
;d Disable the Windows 11 context menu *
|
||||
@=""
|
||||
[HKEY_CURRENT_USER\Software\ExplorerPatcher]
|
||||
;b Use classic drive groupings in This PC *
|
||||
"UseClassicDriveGrouping"=dword:00000000
|
||||
;t The following settings take effect on newly created File Explorer windows:
|
||||
[HKEY_CURRENT_USER\Software\ExplorerPatcher]
|
||||
;i Use immersive menus when displaying Windows 10 context menus **
|
||||
|
@ -142,6 +142,9 @@
|
||||
;https://github.com/valinet/ExplorerPatcher/wiki/Using-ExplorerPatcher-as-shell-extension
|
||||
;q
|
||||
[HKEY_CURRENT_USER\Software\ExplorerPatcher]
|
||||
;b Use classic drive groupings in This PC *
|
||||
"UseClassicDriveGrouping"=dword:00000000
|
||||
[HKEY_CURRENT_USER\Software\ExplorerPatcher]
|
||||
;i Use immersive menus when displaying Windows 10 context menus **
|
||||
"DisableImmersiveContextMenu"=dword:00000000
|
||||
[-HKEY_CURRENT_USER\Software\Classes\CLSID\{056440FD-8568-48e7-A632-72157243B55B}\InprocServer32]
|
||||
|
Loading…
Reference in New Issue
Block a user