diff --git a/modules/rosapps/applications/devutils/shlextdbg/shlextdbg.cpp b/modules/rosapps/applications/devutils/shlextdbg/shlextdbg.cpp index 1d743180360..2220c4ebda1 100644 --- a/modules/rosapps/applications/devutils/shlextdbg/shlextdbg.cpp +++ b/modules/rosapps/applications/devutils/shlextdbg/shlextdbg.cpp @@ -44,6 +44,8 @@ static void PrintHelp(PCWSTR ExtraLine = NULL) wprintf(L"shlextdbg /shellexec=path [/see] [verb] [class]\n"); wprintf(L"\n"); wprintf(L"shlextdbg /dumpmenu=[{clsid}]path [/cmf]\n"); + wprintf(L"shlextdbg /dumpfoldercolumns=folder\n"); + wprintf(L"shlextdbg /dumppidl=path\n"); } /* @@ -58,6 +60,8 @@ Examples: /openwindows /shellexec=c: /invoke properties /dumpmenu=%windir%\explorer.exe /extended /dumpmenu {D969A300-E7FF-11d0-A93B-00A0C90F2719}c: +/dumpfoldercolumns=c:\ +/dumppidl=c:\ */ @@ -143,14 +147,17 @@ static HRESULT GetUIObjectOfAbsolute(LPCITEMIDLIST pidl, REFIID riid, void** ppv return hr; } -static HRESULT CreateShellItemFromParse(PCWSTR Path, IShellItem** ppSI) +static HRESULT CreateShellItemFromParse(PCWSTR Path, IShellItem** ppSI, PIDLIST_ABSOLUTE *ppidl = NULL) { PIDLIST_ABSOLUTE pidl = NULL; HRESULT hr = SHParseDisplayName(Path, NULL, &pidl, 0, NULL); if (SUCCEEDED(hr)) { hr = SHCreateShellItem(NULL, NULL, pidl, ppSI); - SHFree(pidl); + if (ppidl) + *ppidl = pidl; + else + SHFree(pidl); } return hr; } @@ -204,6 +211,25 @@ static void DumpBytes(const void *Data, SIZE_T cb) wprintf(L"\n"); } +static inline CHAR SafeDumpChar(UINT Ch) +{ + return Ch <= 255 && isprint(static_cast(Ch)) ? (BYTE)Ch : '.'; +} + +static void DumpHexBytes(LPCVOID Buffer, SIZE_T cb) +{ + UINT cbPerLine = 16, cbLine; + for (SIZE_T i = 0, j; i < cb; i += cbLine) + { + cbLine = min(cbPerLine, cb - i); + for (j = 0; j < cbLine; ++j) wprintf(L" %.2X", ((BYTE*)Buffer)[i + j]); + for (j = 0; j < cbPerLine - cbLine; ++j) wprintf(L" "); + for (j = 0; j < cbLine; ++j) wprintf(L"%s%c", !j ? L" " : L"", SafeDumpChar(((BYTE*)Buffer)[i + j])); + for (j = 0; j < cbPerLine - cbLine; ++j) wprintf(L" "); + wprintf(L"\n"); + } +} + static HRESULT GetCommandString(IContextMenu& CM, UINT Id, UINT Type, LPWSTR buf, UINT cchMax) { if (cchMax < 1) return E_INVALIDARG; @@ -310,6 +336,127 @@ static int SHGFI(PCWSTR Path) return ret; } +static HRESULT DumpPidl(PCWSTR Path) +{ + PIDLIST_ABSOLUTE pidl; + CComPtr si; + HRESULT hr = CreateShellItemFromParse(Path, &si, &pidl); + if (FAILED(hr)) + { + wprintf(L"Can't parse %s\n", Path); + return hr; + } + + UINT lvl = 0; + for (PUITEMID_CHILD p = pidl; p; ++lvl) + { + if (!p->mkid.cb && p > pidl) + break; + PUITEMID_CHILD pNext = ILGetNext(p); + const WORD save = pNext ? pNext->mkid.cb : 0; + if (pNext) + pNext->mkid.cb = 0; + wprintf(L"#%u %ub", lvl, p->mkid.cb); + + WCHAR buf[100]; + VARIANT v; + V_VT(&v) = VT_EMPTY; + static const SHCOLUMNID pkey_descriptionid = { FMTID_ShellDetails, PID_DESCRIPTIONID }; + CComPtr pParentFolder; + PCUITEMID_CHILD pidlChildSelf; + hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder2, &pParentFolder), &pidlChildSelf); + if (SUCCEEDED(hr) && SUCCEEDED(hr = pParentFolder->GetDetailsEx(pidlChildSelf, &pkey_descriptionid, &v))) + { + if (V_VT(&v) == (VT_UI1 | VT_ARRAY)) + { + const SHDESCRIPTIONID &shdid = *(SHDESCRIPTIONID*)V_ARRAY(&v)->pvData; + StringFromGUID2(shdid.clsid, buf, _countof(buf)); + PCWSTR did = L"?"; + switch (shdid.dwDescriptionId) + { + case SHDID_ROOT_REGITEM: did = L"ROOT_REGITEM"; break; + case SHDID_FS_FILE: did = L"FS_FILE"; break; + case SHDID_FS_DIRECTORY: did = L"FS_DIRECTORY"; break; + case SHDID_FS_OTHER: did = L"FS_OTHER"; break; + case SHDID_COMPUTER_FIXED: did = L"COMPUTER_FIXED"; break; + } + wprintf(L" %s %s(%#.2x)", buf, did, shdid.dwDescriptionId); + } + PropVariantClear((PROPVARIANT*)&v); // No need to drag in oleaut32, ole32 knows how to handle this + } + wprintf(L"\n"); + DumpHexBytes(p->mkid.abID, p->mkid.cb > 2 ? p->mkid.cb - 2 : 0); + + if (pNext) + pNext->mkid.cb = save; + p = pNext; + } + SHFree(pidl); + return S_OK; +} + +static HRESULT DumpFolderColumns(PCWSTR Path) +{ + CComPtr si; + HRESULT hr = CreateShellItemFromParse(Path, &si); + if (FAILED(hr)) + { + wprintf(L"Can't parse %s\n", Path); + return hr; + } + CComPtr sf; + if (FAILED(hr = si->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder2, &sf)))) + { + wprintf(L"Could not bind to IShellFolder2\n"); + return hr; + } + + ULONG columns, defSort, defDisp; + for (columns = 0; ; ++columns) + { + SHELLDETAILS sd = {}; + if (FAILED(sf->GetDetailsOf(NULL, columns, &sd))) + break; + PWSTR str; + if (FAILED(StrRetToStrW(&sd.str, NULL, &str))) + str = NULL; + SHCOLSTATEF flags; + if (FAILED(sf->GetDefaultColumnState(columns, &flags))) + flags = 0; + wprintf(L"%lu: %s 0x%.4x %d 0x%.8x\n", columns, str ? str : L"", (UINT)sd.fmt, sd.cxChar, flags); + SHFree(str); + } + + if (SUCCEEDED(sf->GetDefaultColumn(0, &defSort, &defDisp))) + wprintf(L"%lu is the default sort column and %lu is the default display column.\n", defSort, defDisp); + + HRESULT (WINAPI* PSGNFPK)(SHCOLUMNID*, PWSTR*) = NULL; + if (HMODULE hPS = LoadLibraryW(L"propsys.dll")) + { + // HACKFIX: PSGetNameFromPropertyKey is a stub in ROS, we will crash if we call it + HRESULT (WINAPI* PSGPD)(SHCOLUMNID*, REFIID, void**); + (FARPROC&)PSGPD = GetProcAddress(hPS, "PSGetPropertyDescription"); + CComPtr dummy; + SHCOLUMNID scid = {}; + if (PSGPD && PSGPD(&scid, IID_PPV_ARG(IShellFolder2, &dummy)) != E_NOTIMPL) + (FARPROC&)PSGNFPK = GetProcAddress(hPS, "PSGetNameFromPropertyKey"); + } + for (ULONG i = 0; i < max(columns, 999); ++i) + { + SHCOLUMNID scid = {}; + if (FAILED(sf->MapColumnToSCID(i, &scid))) + continue; + WCHAR Buffer[100]; + StringFromGUID2(scid.fmtid, Buffer, _countof(Buffer)); + wprintf(L"%lu: %s,%-3lu", i, Buffer, scid.pid); + CComHeapPtr str; + if (PSGNFPK && SUCCEEDED(PSGNFPK(&scid, &str)) && str) + wprintf(L" %s", (PWSTR)str); + wprintf(L"\n"); + } + return columns ? S_OK : S_FALSE; +} + static HRESULT AssocQ(int argc, WCHAR **argv) { UINT qtype = StrToNum(argv[2]); @@ -819,6 +966,14 @@ int wmain(int argc, WCHAR **argv) return ErrMsg(hr); return 0; } + else if (isCmdWithArg(argc, argv, n, L"dumpfoldercolumns", arg)) + { + return DumpFolderColumns(arg); + } + else if (isCmdWithArg(argc, argv, n, L"dumppidl", arg)) + { + return DumpPidl(arg); + } else if (isCmdWithArg(argc, argv, n, L"clsid", arg)) { HRESULT hr = CLSIDFromString(arg, &g_CLSID);