mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 09:34:43 +00:00
[SHLEXTDBG] Added IQueryAssociations, SHGetFileInfo and ShellExecute actions (#6030)
Added multiple new actions, most of them will use the system default implementation but can be forced to use a specific CLSID or IShellFolder implementation. - /shgfi (SHGetFileInfo) - /assocq (IQueryAssociations) - /shellexec (ShellExecuteEx on pidl) - /dumpmenu (Dumps the HMENU of a IContextMenu with its menu ids and verbs) Added /explorerinstance as a new wait mode and made it the default. All the other wait modes are hacks that just works around bugs in ROS.
This commit is contained in:
parent
7dd8a30cf9
commit
f6cf6954eb
2 changed files with 576 additions and 54 deletions
|
@ -3,5 +3,5 @@ add_executable(shlextdbg shlextdbg.cpp shlextdbg.rc)
|
|||
|
||||
set_module_type(shlextdbg win32cui UNICODE)
|
||||
target_link_libraries(shlextdbg uuid cpprt atl_classes)
|
||||
add_importlibs(shlextdbg ole32 comctl32 shell32 user32 msvcrt kernel32)
|
||||
add_importlibs(shlextdbg ole32 comctl32 shell32 shlwapi advapi32 user32 msvcrt kernel32)
|
||||
add_cd_file(TARGET shlextdbg DESTINATION reactos/system32 FOR all)
|
||||
|
|
|
@ -7,12 +7,380 @@
|
|||
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#include <atlbase.h> // thanks gcc
|
||||
#include <atlcom.h> // thanks gcc
|
||||
#include <atlstr.h>
|
||||
#include <atlsimpcoll.h>
|
||||
#include <conio.h>
|
||||
#include <shellutils.h>
|
||||
#include <shlwapi_undoc.h>
|
||||
|
||||
static void PrintHelp(PCWSTR ExtraLine = NULL)
|
||||
{
|
||||
if (ExtraLine)
|
||||
wprintf(L"%s\n\n", ExtraLine);
|
||||
|
||||
wprintf(L"shlextdbg /clsid={clsid} [/dll=dllname] /IShellExtInit=filename |shlextype| |waitoptions|\n");
|
||||
wprintf(L" {clsid}: The CLSID or ProgID of the object to create\n");
|
||||
wprintf(L" dll: Optional dllname to create the object from, instead of CoCreateInstance\n");
|
||||
wprintf(L" filename: The filename to pass to IShellExtInit->Initialze\n");
|
||||
wprintf(L" shlextype: The type of shell extention to run:\n");
|
||||
wprintf(L" /IShellPropSheetExt to create a property sheet\n");
|
||||
wprintf(L" /IContextMenu=verb to activate the specified verb\n");
|
||||
wprintf(L" waitoptions: Specify how to wait:\n");
|
||||
wprintf(L" /explorerinstance: Wait for SHGetInstanceExplorer (Default)\n");
|
||||
wprintf(L" /infinite: Keep on waiting infinitely\n");
|
||||
wprintf(L" /openwindows: Wait for all windows from the current application to close\n");
|
||||
wprintf(L" /input: Wait for input\n");
|
||||
wprintf(L" /nowait\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"shlextdbg /shgfi=path\n");
|
||||
wprintf(L" Call SHGetFileInfo. Prefix path with $ to parse as a pidl.\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"shlextdbg /assocq <[{bhid}]path> <string|data|key> <type> <initflags> <queryflags> <initstring> [extra] [maxsize]\n");
|
||||
wprintf(L" Uses the default implementation from AssocCreate if path is empty.\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"shlextdbg /shellexec=path [/see] [verb] [class]\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"shlextdbg /dumpmenu=[{clsid}]path [/cmf]\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Examples:
|
||||
|
||||
/clsid={513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8} /IShellExtInit=C:\RosBE\Uninstall.exe /IShellPropSheetExt
|
||||
/clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows
|
||||
/clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows /dll=R:\build\dev\devenv\dll\shellext\zipfldr\Debug\zipfldr.dll
|
||||
/shgfi=c:\freeldr.ini
|
||||
/assocq "" string 1 0 0 .txt
|
||||
/assocq "" string friendlytypename 0x400 0 .txt "" 10
|
||||
/openwindows /shellexec=c: /invoke properties
|
||||
/dumpmenu=%windir%\explorer.exe /extended
|
||||
/dumpmenu {D969A300-E7FF-11d0-A93B-00A0C90F2719}c:
|
||||
|
||||
*/
|
||||
|
||||
static LONG StrToNum(PCWSTR in)
|
||||
{
|
||||
PWCHAR end;
|
||||
LONG v = wcstol(in, &end, 0);
|
||||
return (end > in) ? v : 0;
|
||||
}
|
||||
|
||||
static int ErrMsg(int Error)
|
||||
{
|
||||
WCHAR buf[400];
|
||||
for (UINT e = Error, cch; ;)
|
||||
{
|
||||
lstrcpynW(buf, L"?", _countof(buf));
|
||||
cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, e, 0, buf, _countof(buf), NULL);
|
||||
while (cch && buf[cch - 1] <= ' ')
|
||||
buf[--cch] = UNICODE_NULL; // Remove trailing newlines
|
||||
if (cch || HIWORD(e) != HIWORD(HRESULT_FROM_WIN32(1)))
|
||||
break;
|
||||
e = HRESULT_CODE(e); // "WIN32_FROM_HRESULT"
|
||||
}
|
||||
wprintf(Error < 0 ? L"Error 0x%.8X %s\n" : L"Error %d %s\n", Error, buf);
|
||||
return Error;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static bool CLSIDPrefix(T& String, CLSID& Clsid)
|
||||
{
|
||||
WCHAR buf[38 + 1];
|
||||
if (String[0] == '{')
|
||||
{
|
||||
lstrcpynW(buf, String, _countof(buf));
|
||||
if (SUCCEEDED(CLSIDFromString(buf, &Clsid)))
|
||||
{
|
||||
String = String + 38;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static HRESULT GetUIObjectOfAbsolute(LPCITEMIDLIST pidl, REFIID riid, void** ppv)
|
||||
{
|
||||
CComPtr<IShellFolder> shellFolder;
|
||||
PCUITEMID_CHILD child;
|
||||
HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &child);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = shellFolder->GetUIObjectOf(NULL, 1, &child, riid, NULL, ppv);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT CreateShellItemFromParse(PCWSTR Path, IShellItem** ppSI)
|
||||
{
|
||||
PIDLIST_ABSOLUTE pidl = NULL;
|
||||
HRESULT hr = SHParseDisplayName(Path, NULL, &pidl, 0, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = SHCreateShellItem(NULL, NULL, pidl, ppSI);
|
||||
SHFree(pidl);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void GetAssocClass(LPCWSTR Path, LPCITEMIDLIST pidl, HKEY& hKey)
|
||||
{
|
||||
hKey = NULL;
|
||||
IQueryAssociations* pQA;
|
||||
if (SUCCEEDED(GetUIObjectOfAbsolute(pidl, IID_PPV_ARG(IQueryAssociations, &pQA))))
|
||||
{
|
||||
pQA->GetKey(0, ASSOCKEY_CLASS, NULL, &hKey); // Not implemented in ROS
|
||||
pQA->Release();
|
||||
}
|
||||
if (!hKey)
|
||||
{
|
||||
DWORD cb;
|
||||
WCHAR buf[MAX_PATH];
|
||||
PWSTR ext = PathFindExtensionW(Path);
|
||||
SHFILEINFOW info;
|
||||
info.dwAttributes = 0;
|
||||
SHGetFileInfoW((LPWSTR)pidl, 0, &info, sizeof(info), SHGFI_PIDL | SHGFI_ATTRIBUTES);
|
||||
if (info.dwAttributes & SFGAO_FOLDER)
|
||||
{
|
||||
ext = const_cast<LPWSTR>(L"Directory");
|
||||
}
|
||||
else if (info.dwAttributes & SFGAO_BROWSABLE)
|
||||
{
|
||||
ext = const_cast<LPWSTR>(L"Folder"); // Best guess
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = sizeof(buf);
|
||||
if (!SHGetValueW(HKEY_CLASSES_ROOT, ext, NULL, NULL, buf, &cb))
|
||||
{
|
||||
RegOpenKeyExW(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &hKey);
|
||||
}
|
||||
}
|
||||
if (!hKey)
|
||||
{
|
||||
RegOpenKeyExW(HKEY_CLASSES_ROOT, ext, 0, KEY_READ, &hKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpBytes(const void *Data, SIZE_T cb)
|
||||
{
|
||||
for (SIZE_T i = 0; i < cb; ++i)
|
||||
{
|
||||
wprintf(L"%s%.2X", i ? L" " : L"", ((LPCBYTE)Data)[i]);
|
||||
}
|
||||
wprintf(L"\n");
|
||||
}
|
||||
|
||||
static HRESULT GetCommandString(IContextMenu& CM, UINT Id, UINT Type, LPWSTR buf, UINT cchMax)
|
||||
{
|
||||
if (cchMax < 1) return E_INVALIDARG;
|
||||
*buf = UNICODE_NULL;
|
||||
|
||||
// First try to retrieve the UNICODE string directly
|
||||
HRESULT hr = CM.GetCommandString(Id, Type | GCS_UNICODE, 0, (char*)buf, cchMax);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// It failed, try to retrieve an ANSI string instead then convert it to UNICODE
|
||||
STRRET sr;
|
||||
sr.uType = STRRET_CSTR;
|
||||
hr = CM.GetCommandString(Id, Type & ~GCS_UNICODE, 0, sr.cStr, _countof(sr.cStr));
|
||||
if (SUCCEEDED(hr))
|
||||
hr = StrRetToBufW(&sr, NULL, buf, cchMax);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void DumpMenu(HMENU hMenu, UINT IdOffset, IContextMenu* pCM, BOOL FakeInit, UINT Indent)
|
||||
{
|
||||
bool recurse = Indent != UINT(-1);
|
||||
WCHAR buf[MAX_PATH];
|
||||
MENUITEMINFOW mii;
|
||||
mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
|
||||
|
||||
for (UINT i = 0, defid = GetMenuDefaultItem(hMenu, FALSE, 0); ; ++i)
|
||||
{
|
||||
mii.fMask = MIIM_STRING;
|
||||
mii.dwTypeData = buf;
|
||||
mii.cch = _countof(buf);
|
||||
*buf = UNICODE_NULL;
|
||||
if (!GetMenuItemInfo(hMenu, i, TRUE, &mii))
|
||||
lstrcpynW(buf, L"?", _countof(buf)); // Tolerate string failure
|
||||
mii.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_FTYPE;
|
||||
mii.hSubMenu = NULL;
|
||||
mii.dwTypeData = NULL;
|
||||
mii.cch = 0;
|
||||
if (!GetMenuItemInfo(hMenu, i, TRUE, &mii))
|
||||
break;
|
||||
|
||||
BOOL sep = mii.fType & MFT_SEPARATOR;
|
||||
wprintf(L"%-4d", (sep || mii.wID == UINT(-1)) ? mii.wID : (mii.wID - IdOffset));
|
||||
for (UINT j = 0; j < Indent && recurse; ++j)
|
||||
wprintf(L" ");
|
||||
wprintf(L"%s%s", mii.hSubMenu ? L">" : L"|", sep ? L"----------" : buf);
|
||||
if (!sep && pCM && SUCCEEDED(GetCommandString(*pCM, mii.wID - IdOffset,
|
||||
GCS_VERB, buf, _countof(buf))))
|
||||
{
|
||||
wprintf(L" [%s]", buf);
|
||||
}
|
||||
wprintf(L"%s\n", (defid == mii.wID && defid != UINT(-1)) ? L" (Default)" : L"");
|
||||
if (mii.hSubMenu && recurse)
|
||||
{
|
||||
if (FakeInit)
|
||||
SHForwardContextMenuMsg(pCM, WM_INITMENUPOPUP, (WPARAM)mii.hSubMenu, LOWORD(i), NULL, TRUE);
|
||||
DumpMenu(mii.hSubMenu, IdOffset, pCM, FakeInit, Indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int SHGFI(PCWSTR Path)
|
||||
{
|
||||
PIDLIST_ABSOLUTE pidl = NULL;
|
||||
UINT flags = 0, ret = 0;
|
||||
|
||||
if (*Path == L'$')
|
||||
{
|
||||
HRESULT hr = SHParseDisplayName(++Path, NULL, &pidl, 0, NULL);
|
||||
if (FAILED(hr))
|
||||
return ErrMsg(hr);
|
||||
flags |= SHGFI_PIDL;
|
||||
Path = (LPCWSTR)pidl;
|
||||
}
|
||||
else if (GetFileAttributes(Path) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
flags |= SHGFI_USEFILEATTRIBUTES;
|
||||
}
|
||||
SHFILEINFOW info;
|
||||
if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags |
|
||||
SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_TYPENAME))
|
||||
{
|
||||
info.szDisplayName[0] = info.szTypeName[0] = UNICODE_NULL;
|
||||
info.dwAttributes = 0;
|
||||
ret = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
wprintf(L"Display: %s\n", info.szDisplayName);
|
||||
wprintf(L"Attributes: 0x%x\n", info.dwAttributes);
|
||||
wprintf(L"Type: %s\n", info.szTypeName);
|
||||
|
||||
if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags | SHGFI_ICONLOCATION))
|
||||
{
|
||||
info.szDisplayName[0] = UNICODE_NULL;
|
||||
info.iIcon = -1;
|
||||
}
|
||||
wprintf(L"Icon: %s,%d\n", info.szDisplayName, info.iIcon);
|
||||
|
||||
if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags | SHGFI_SYSICONINDEX))
|
||||
{
|
||||
info.iIcon = -1;
|
||||
}
|
||||
wprintf(L"Index: %d\n", info.iIcon);
|
||||
SHFree(pidl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static HRESULT AssocQ(int argc, WCHAR **argv)
|
||||
{
|
||||
UINT qtype = StrToNum(argv[2]);
|
||||
ASSOCF iflags = StrToNum(argv[3]);
|
||||
ASSOCF qflags = StrToNum(argv[4]);
|
||||
PCWSTR extra = (argc > 6 && *argv[6]) ? argv[6] : NULL;
|
||||
WCHAR buf[MAX_PATH * 2];
|
||||
DWORD maxSize = (argc > 7 && *argv[7]) ? StrToNum(argv[7]) : sizeof(buf);
|
||||
|
||||
HRESULT hr;
|
||||
CComPtr<IQueryAssociations> qa;
|
||||
PWSTR path = argv[0];
|
||||
if (*path)
|
||||
{
|
||||
CLSID clsid, *pclsid = NULL;
|
||||
if (CLSIDPrefix(path, clsid))
|
||||
pclsid = &clsid;
|
||||
CComPtr<IShellItem> si;
|
||||
if (SUCCEEDED(hr = CreateShellItemFromParse(path, &si)))
|
||||
{
|
||||
hr = si->BindToHandler(NULL, pclsid ? *pclsid : BHID_AssociationArray, IID_PPV_ARG(IQueryAssociations, &qa));
|
||||
if (FAILED(hr) && !pclsid)
|
||||
hr = si->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IQueryAssociations, &qa));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &qa));
|
||||
}
|
||||
if (FAILED(hr))
|
||||
return ErrMsg(hr);
|
||||
hr = qa->Init(iflags, argv[5], NULL, NULL);
|
||||
if (FAILED(hr))
|
||||
return ErrMsg(hr);
|
||||
|
||||
DWORD size = maxSize;
|
||||
if (!wcsicmp(argv[1], L"string"))
|
||||
{
|
||||
if (!wcsicmp(argv[2], L"COMMAND"))
|
||||
qtype = ASSOCSTR_COMMAND;
|
||||
if (!wcsicmp(argv[2], L"EXECUTABLE"))
|
||||
qtype = ASSOCSTR_EXECUTABLE;
|
||||
if (!wcsicmp(argv[2], L"FRIENDLYDOCNAME") || !wcsicmp(argv[2], L"FriendlyTypeName"))
|
||||
qtype = ASSOCSTR_FRIENDLYDOCNAME;
|
||||
if (!wcsicmp(argv[2], L"DEFAULTICON"))
|
||||
qtype = ASSOCSTR_DEFAULTICON;
|
||||
|
||||
buf[0] = UNICODE_NULL;
|
||||
size /= sizeof(buf[0]); // Convert to number of characters
|
||||
hr = qa->GetString(qflags, (ASSOCSTR)qtype, extra, buf, &size);
|
||||
size *= sizeof(buf[0]); // Convert back to bytes
|
||||
if (SUCCEEDED(hr) ||
|
||||
hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
|
||||
{
|
||||
wprintf(L"0x%.8X: %s\n", hr, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
wprintf(size != maxSize ? L"%u " : L"", size);
|
||||
ErrMsg(hr);
|
||||
}
|
||||
}
|
||||
else if (!wcsicmp(argv[1], L"data"))
|
||||
{
|
||||
if (!wcsicmp(argv[2], L"EDITFLAGS"))
|
||||
qtype = ASSOCDATA_EDITFLAGS;
|
||||
if (!wcsicmp(argv[2], L"VALUE"))
|
||||
qtype = ASSOCDATA_VALUE;
|
||||
|
||||
hr = qa->GetData(qflags, (ASSOCDATA)qtype, extra, buf, &size);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wprintf(L"0x%.8X: %u byte(s) ", hr, size);
|
||||
DumpBytes(buf, min(size, maxSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
wprintf(size != maxSize ? L"%u " : L"", size);
|
||||
ErrMsg(hr);
|
||||
}
|
||||
}
|
||||
else if (!wcsicmp(argv[1], L"key"))
|
||||
{
|
||||
HKEY hKey = NULL;
|
||||
hr = qa->GetKey(qflags, (ASSOCKEY)qtype, extra, &hKey);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wprintf(L"0x%.8X: hKey %p\n", hr, hKey);
|
||||
RegQueryValueExW(hKey, L"shlextdbg", 0, NULL, NULL, NULL); // Filter by this in Process Monitor
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrMsg(hr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintHelp(L"Unknown query");
|
||||
return ErrMsg(ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
enum WaitType
|
||||
{
|
||||
|
@ -20,6 +388,7 @@ enum WaitType
|
|||
Wait_Infinite,
|
||||
Wait_OpenWindows,
|
||||
Wait_Input,
|
||||
Wait_ExplorerInstance,
|
||||
};
|
||||
|
||||
CLSID g_CLSID = { 0 };
|
||||
|
@ -27,7 +396,7 @@ CStringW g_DLL;
|
|||
CStringW g_ShellExtInit;
|
||||
bool g_bIShellPropSheetExt = false;
|
||||
CStringA g_ContextMenu;
|
||||
WaitType g_Wait = Wait_None;
|
||||
WaitType g_Wait = Wait_ExplorerInstance;
|
||||
|
||||
HRESULT CreateIDataObject(CComHeapPtr<ITEMIDLIST>& pidl, CComPtr<IDataObject>& dataObject, PCWSTR FileName)
|
||||
{
|
||||
|
@ -105,7 +474,11 @@ HRESULT LoadAndInitialize(REFIID riid, LPVOID* ppv)
|
|||
if (!SUCCEEDED(hr))
|
||||
return hr;
|
||||
|
||||
hr = spShellExtInit->Initialize(pidl, spDataObject, NULL);
|
||||
HKEY hKey = NULL;
|
||||
GetAssocClass(g_ShellExtInit.GetString(), pidl, hKey);
|
||||
hr = spShellExtInit->Initialize(pidl, spDataObject, hKey);
|
||||
if (hKey)
|
||||
RegCloseKey(hKey);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
wprintf(L"IShellExtInit->Initialize failed: 0x%x\n", hr);
|
||||
|
@ -154,7 +527,73 @@ void WaitWindows()
|
|||
wprintf(L"All windows closed (ignoring console window)\n");
|
||||
}
|
||||
|
||||
struct ExplorerInstance : public IUnknown
|
||||
{
|
||||
HWND m_hWnd;
|
||||
volatile LONG m_rc;
|
||||
|
||||
ExplorerInstance() : m_hWnd(NULL), m_rc(1) {}
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
static const QITAB rgqit[] = { { 0 } };
|
||||
return QISearch(this, rgqit, riid, ppv);
|
||||
}
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
if (g_Wait == Wait_ExplorerInstance)
|
||||
wprintf(L"INFO: SHGetInstanceExplorer\n");
|
||||
return InterlockedIncrement(&m_rc);
|
||||
}
|
||||
virtual ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
if (g_Wait == Wait_ExplorerInstance)
|
||||
wprintf(L"INFO: Release ExplorerInstance\n");
|
||||
ULONG r = InterlockedDecrement(&m_rc);
|
||||
if (!r)
|
||||
PostMessage(m_hWnd, WM_CLOSE, 0, 0);
|
||||
return r;
|
||||
}
|
||||
void Wait()
|
||||
{
|
||||
SHSetInstanceExplorer(NULL);
|
||||
m_hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("STATIC"), NULL, WS_POPUP,
|
||||
0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
|
||||
BOOL loop = InterlockedDecrement(&m_rc) != 0;
|
||||
MSG msg;
|
||||
while (loop && (int)GetMessage(&msg, NULL, 0, 0) > 0)
|
||||
{
|
||||
if (msg.hwnd == m_hWnd && msg.message == WM_CLOSE)
|
||||
PostMessage(m_hWnd, WM_QUIT, 0, 0);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
} g_EI;
|
||||
|
||||
static void Wait()
|
||||
{
|
||||
LPCWSTR nag = L"(Please use SHGetInstanceExplorer in your code instead)";
|
||||
switch (g_Wait)
|
||||
{
|
||||
case Wait_None:
|
||||
break;
|
||||
case Wait_Infinite:
|
||||
_putws(nag);
|
||||
while (true)
|
||||
Sleep(1000);
|
||||
break;
|
||||
case Wait_OpenWindows:
|
||||
_putws(nag);
|
||||
WaitWindows();
|
||||
break;
|
||||
case Wait_Input:
|
||||
wprintf(L"Press any key to continue... %s\n", nag);
|
||||
_getch();
|
||||
break;
|
||||
case Wait_ExplorerInstance:
|
||||
g_EI.Wait();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CSimpleArray<HPROPSHEETPAGE> g_Pages;
|
||||
static BOOL CALLBACK cb_AddPage(HPROPSHEETPAGE page, LPARAM lParam)
|
||||
|
@ -196,36 +635,14 @@ static bool isCmd(int argc, WCHAR** argv, int n, PCWSTR check)
|
|||
return !wcsicmp(argv[n] + 1, check);
|
||||
}
|
||||
|
||||
static void PrintHelp(PCWSTR ExtraLine)
|
||||
{
|
||||
if (ExtraLine)
|
||||
wprintf(L"%s\n", ExtraLine);
|
||||
|
||||
wprintf(L"shlextdbg /clsid={clsid} [/dll=dllname] /IShellExtInit=filename |shlextype| |waitoptions|\n");
|
||||
wprintf(L" {clsid}: The CLSID or ProgID of the object to create\n");
|
||||
wprintf(L" dll: Optional dllname to create the object from, instead of CoCreateInstance\n");
|
||||
wprintf(L" filename: The filename to pass to IShellExtInit->Initialze\n");
|
||||
wprintf(L" shlextype: The type of shell extention to run:\n");
|
||||
wprintf(L" /IShellPropSheetExt to create a property sheet\n");
|
||||
wprintf(L" /IContextMenu=verb to activate the specified verb\n");
|
||||
wprintf(L" waitoptions: Specify how to wait:\n");
|
||||
wprintf(L" /infinite: Keep on waiting infinitely\n");
|
||||
wprintf(L" /openwindows: Wait for all windows from the current application to close\n");
|
||||
wprintf(L" /input: Wait for input\n");
|
||||
wprintf(L"\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Examples:
|
||||
|
||||
/clsid={513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8} /IShellExtInit=C:\RosBE\Uninstall.exe /IShellPropSheetExt
|
||||
/clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows
|
||||
/clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows /dll=R:\build\dev\devenv\dll\shellext\zipfldr\Debug\zipfldr.dll
|
||||
|
||||
*/
|
||||
extern "C" // and another hack for gcc
|
||||
int wmain(int argc, WCHAR **argv)
|
||||
{
|
||||
INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_LINK_CLASS | ICC_STANDARD_CLASSES };
|
||||
InitCommonControlsEx(&icc);
|
||||
CoInitialize(NULL);
|
||||
SHSetInstanceExplorer(static_cast<IUnknown*>(&g_EI));
|
||||
|
||||
bool failArgs = false;
|
||||
for (int n = 1; n < argc; ++n)
|
||||
{
|
||||
|
@ -233,7 +650,124 @@ int wmain(int argc, WCHAR **argv)
|
|||
if (cmd[0] == '-' || cmd[0] == '/')
|
||||
{
|
||||
PCWSTR arg;
|
||||
if (isCmdWithArg(argc, argv, n, L"clsid", arg))
|
||||
if (isCmdWithArg(argc, argv, n, L"shgfi", arg))
|
||||
{
|
||||
failArgs = true;
|
||||
if (*arg)
|
||||
return SHGFI(arg);
|
||||
}
|
||||
else if (isCmd(argc, argv, n, L"assocq"))
|
||||
{
|
||||
failArgs = true;
|
||||
if (argc - (n + 1) >= 6 && argc - (n + 1) <= 8)
|
||||
return AssocQ(argc - (n + 1), &argv[(n + 1)]);
|
||||
}
|
||||
else if (isCmdWithArg(argc, argv, n, L"shellexec", arg))
|
||||
{
|
||||
PIDLIST_ABSOLUTE pidl = NULL;
|
||||
HRESULT hr = SHParseDisplayName(arg, NULL, &pidl, 0, NULL);
|
||||
if (FAILED(hr))
|
||||
return ErrMsg(hr);
|
||||
SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_IDLIST | SEE_MASK_UNICODE };
|
||||
sei.lpIDList = pidl;
|
||||
sei.nShow = SW_SHOW;
|
||||
while (++n < argc)
|
||||
{
|
||||
if (argv[n][0] != '-' && argv[n][0] != '/')
|
||||
break;
|
||||
else if (isCmd(argc, argv, n, L"INVOKE"))
|
||||
sei.fMask |= SEE_MASK_INVOKEIDLIST;
|
||||
else if (isCmd(argc, argv, n, L"NOUI"))
|
||||
sei.fMask |= SEE_MASK_FLAG_NO_UI;
|
||||
else if (isCmd(argc, argv, n, L"ASYNCOK"))
|
||||
sei.fMask |= SEE_MASK_ASYNCOK ;
|
||||
else if (isCmd(argc, argv, n, L"NOASYNC"))
|
||||
sei.fMask |= SEE_MASK_NOASYNC;
|
||||
else
|
||||
wprintf(L"WARN: Ignoring switch %s\n", argv[n]);
|
||||
}
|
||||
if (n < argc && *argv[n++])
|
||||
{
|
||||
sei.lpVerb = argv[n - 1];
|
||||
}
|
||||
if (n < argc && *argv[n++])
|
||||
{
|
||||
sei.lpClass = argv[n - 1];
|
||||
sei.fMask |= SEE_MASK_CLASSNAME;
|
||||
}
|
||||
UINT succ = ShellExecuteExW(&sei), gle = GetLastError();
|
||||
SHFree(pidl);
|
||||
if (!succ)
|
||||
return ErrMsg(gle);
|
||||
Wait();
|
||||
return 0;
|
||||
}
|
||||
else if (isCmdWithArg(argc, argv, n, L"dumpmenu", arg))
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IContextMenu> cm;
|
||||
if (CLSIDPrefix(arg, g_CLSID))
|
||||
{
|
||||
g_ShellExtInit = arg;
|
||||
hr = LoadAndInitialize(IID_PPV_ARG(IContextMenu, &cm));
|
||||
}
|
||||
else
|
||||
{
|
||||
CComPtr<IShellItem> si;
|
||||
hr = CreateShellItemFromParse(arg, &si);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = si->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IContextMenu, &cm));
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
UINT first = 10, last = 9000;
|
||||
UINT cmf = 0, nosub = 0, fakeinit = 0;
|
||||
while (++n < argc)
|
||||
{
|
||||
if (argv[n][0] != '-' && argv[n][0] != '/')
|
||||
break;
|
||||
else if (isCmd(argc, argv, n, L"DEFAULTONLY"))
|
||||
cmf |= CMF_DEFAULTONLY;
|
||||
else if (isCmd(argc, argv, n, L"NODEFAULT"))
|
||||
cmf |= CMF_NODEFAULT;
|
||||
else if (isCmd(argc, argv, n, L"DONOTPICKDEFAULT"))
|
||||
cmf |= CMF_DONOTPICKDEFAULT;
|
||||
else if (isCmd(argc, argv, n, L"EXTENDED") || isCmd(argc, argv, n, L"EXTENDEDVERBS"))
|
||||
cmf |= CMF_EXTENDEDVERBS;
|
||||
else if (isCmd(argc, argv, n, L"SYNCCASCADEMENU"))
|
||||
cmf |= CMF_SYNCCASCADEMENU;
|
||||
else if (isCmd(argc, argv, n, L"EXPLORE"))
|
||||
cmf |= CMF_EXPLORE;
|
||||
else if (isCmd(argc, argv, n, L"VERBSONLY"))
|
||||
cmf |= CMF_VERBSONLY;
|
||||
else if (isCmd(argc, argv, n, L"NOVERBS"))
|
||||
cmf |= CMF_NOVERBS;
|
||||
else if (isCmd(argc, argv, n, L"DISABLEDVERBS"))
|
||||
cmf |= CMF_DISABLEDVERBS;
|
||||
else if (isCmd(argc, argv, n, L"OPTIMIZEFORINVOKE"))
|
||||
cmf |= CMF_OPTIMIZEFORINVOKE;
|
||||
else if (isCmd(argc, argv, n, L"CANRENAME"))
|
||||
cmf |= CMF_CANRENAME;
|
||||
else if (isCmd(argc, argv, n, L"NOSUBMENU"))
|
||||
nosub++;
|
||||
else if (isCmd(argc, argv, n, L"INITMENUPOPUP"))
|
||||
fakeinit++; // Tickle async submenus
|
||||
else
|
||||
wprintf(L"WARN: Ignoring switch %s\n", argv[n]);
|
||||
}
|
||||
HMENU hMenu = CreatePopupMenu();
|
||||
hr = cm->QueryContextMenu(hMenu, 0, first, last, cmf);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
DumpMenu(hMenu, first, cm, fakeinit, nosub ? -1 : 0);
|
||||
}
|
||||
DestroyMenu(hMenu);
|
||||
}
|
||||
if (FAILED(hr))
|
||||
return ErrMsg(hr);
|
||||
return 0;
|
||||
}
|
||||
else if (isCmdWithArg(argc, argv, n, L"clsid", arg))
|
||||
{
|
||||
HRESULT hr = CLSIDFromString(arg, &g_CLSID);
|
||||
if (!SUCCEEDED(hr))
|
||||
|
@ -270,6 +804,14 @@ int wmain(int argc, WCHAR **argv)
|
|||
{
|
||||
g_Wait = Wait_Input;
|
||||
}
|
||||
else if (isCmd(argc, argv, n, L"explorerinstance"))
|
||||
{
|
||||
g_Wait = Wait_ExplorerInstance;
|
||||
}
|
||||
else if (isCmd(argc, argv, n, L"nowait"))
|
||||
{
|
||||
g_Wait = Wait_None;
|
||||
}
|
||||
else
|
||||
{
|
||||
wprintf(L"Unknown argument: %s\n", cmd);
|
||||
|
@ -284,7 +826,6 @@ int wmain(int argc, WCHAR **argv)
|
|||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
|
||||
CLSID EmptyCLSID = { 0 };
|
||||
if (EmptyCLSID == g_CLSID)
|
||||
{
|
||||
|
@ -298,10 +839,6 @@ int wmain(int argc, WCHAR **argv)
|
|||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_LINK_CLASS | ICC_STANDARD_CLASSES };
|
||||
InitCommonControlsEx(&icc);
|
||||
CoInitialize(NULL);
|
||||
|
||||
HRESULT hr;
|
||||
if (g_bIShellPropSheetExt)
|
||||
{
|
||||
|
@ -337,6 +874,8 @@ int wmain(int argc, WCHAR **argv)
|
|||
if (!SUCCEEDED(hr))
|
||||
return hr;
|
||||
|
||||
// FIXME: Must call QueryContextMenu before InvokeCommand?
|
||||
|
||||
CMINVOKECOMMANDINFO cm = { sizeof(cm), 0 };
|
||||
cm.lpVerb = g_ContextMenu.GetString();
|
||||
cm.nShow = SW_SHOW;
|
||||
|
@ -350,23 +889,6 @@ int wmain(int argc, WCHAR **argv)
|
|||
wprintf(L"IContextMenu->InvokeCommand returned: 0x%x\n", hr);
|
||||
}
|
||||
|
||||
switch (g_Wait)
|
||||
{
|
||||
case Wait_None:
|
||||
break;
|
||||
case Wait_Infinite:
|
||||
while (true) {
|
||||
Sleep(1000);
|
||||
}
|
||||
break;
|
||||
case Wait_OpenWindows:
|
||||
WaitWindows();
|
||||
break;
|
||||
case Wait_Input:
|
||||
wprintf(L"Press any key to continue...\n");
|
||||
_getch();
|
||||
break;
|
||||
|
||||
}
|
||||
Wait();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue