1cb885480SMark Jansen /*
2cb885480SMark Jansen * PROJECT: shlextdbg
3cb885480SMark Jansen * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4cb885480SMark Jansen * PURPOSE: Shell extension debug utility
5cb885480SMark Jansen * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6cb885480SMark Jansen */
7cb885480SMark Jansen
8cb885480SMark Jansen #include <windows.h>
9cb885480SMark Jansen #include <shlobj.h>
10f6cf6954SWhindmar Saksit #include <shlwapi.h>
11cb885480SMark Jansen #include <atlbase.h> // thanks gcc
12cb885480SMark Jansen #include <atlcom.h> // thanks gcc
13cb885480SMark Jansen #include <atlstr.h>
14cb885480SMark Jansen #include <atlsimpcoll.h>
15cb885480SMark Jansen #include <conio.h>
16cb885480SMark Jansen #include <shellutils.h>
17f6cf6954SWhindmar Saksit #include <shlwapi_undoc.h>
18f6cf6954SWhindmar Saksit
PrintHelp(PCWSTR ExtraLine=NULL)19f6cf6954SWhindmar Saksit static void PrintHelp(PCWSTR ExtraLine = NULL)
20f6cf6954SWhindmar Saksit {
21f6cf6954SWhindmar Saksit if (ExtraLine)
22f6cf6954SWhindmar Saksit wprintf(L"%s\n\n", ExtraLine);
23f6cf6954SWhindmar Saksit
24f6cf6954SWhindmar Saksit wprintf(L"shlextdbg /clsid={clsid} [/dll=dllname] /IShellExtInit=filename |shlextype| |waitoptions|\n");
25f6cf6954SWhindmar Saksit wprintf(L" {clsid}: The CLSID or ProgID of the object to create\n");
26f6cf6954SWhindmar Saksit wprintf(L" dll: Optional dllname to create the object from, instead of CoCreateInstance\n");
27f6cf6954SWhindmar Saksit wprintf(L" filename: The filename to pass to IShellExtInit->Initialze\n");
28f6cf6954SWhindmar Saksit wprintf(L" shlextype: The type of shell extention to run:\n");
29f6cf6954SWhindmar Saksit wprintf(L" /IShellPropSheetExt to create a property sheet\n");
30f6cf6954SWhindmar Saksit wprintf(L" /IContextMenu=verb to activate the specified verb\n");
31f6cf6954SWhindmar Saksit wprintf(L" waitoptions: Specify how to wait:\n");
32f6cf6954SWhindmar Saksit wprintf(L" /explorerinstance: Wait for SHGetInstanceExplorer (Default)\n");
33f6cf6954SWhindmar Saksit wprintf(L" /infinite: Keep on waiting infinitely\n");
34f6cf6954SWhindmar Saksit wprintf(L" /openwindows: Wait for all windows from the current application to close\n");
35f6cf6954SWhindmar Saksit wprintf(L" /input: Wait for input\n");
36f6cf6954SWhindmar Saksit wprintf(L" /nowait\n");
37f6cf6954SWhindmar Saksit wprintf(L"\n");
38f6cf6954SWhindmar Saksit wprintf(L"shlextdbg /shgfi=path\n");
39f6cf6954SWhindmar Saksit wprintf(L" Call SHGetFileInfo. Prefix path with $ to parse as a pidl.\n");
40f6cf6954SWhindmar Saksit wprintf(L"\n");
41f6cf6954SWhindmar Saksit wprintf(L"shlextdbg /assocq <[{bhid}]path> <string|data|key> <type> <initflags> <queryflags> <initstring> [extra] [maxsize]\n");
42f6cf6954SWhindmar Saksit wprintf(L" Uses the default implementation from AssocCreate if path is empty.\n");
43f6cf6954SWhindmar Saksit wprintf(L"\n");
44f6cf6954SWhindmar Saksit wprintf(L"shlextdbg /shellexec=path [/see] [verb] [class]\n");
45f6cf6954SWhindmar Saksit wprintf(L"\n");
46f6cf6954SWhindmar Saksit wprintf(L"shlextdbg /dumpmenu=[{clsid}]path [/cmf]\n");
47f6cf6954SWhindmar Saksit }
48f6cf6954SWhindmar Saksit
49f6cf6954SWhindmar Saksit /*
50f6cf6954SWhindmar Saksit Examples:
51f6cf6954SWhindmar Saksit
52f6cf6954SWhindmar Saksit /clsid={513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8} /IShellExtInit=C:\RosBE\Uninstall.exe /IShellPropSheetExt
53f6cf6954SWhindmar Saksit /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows
54f6cf6954SWhindmar Saksit /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows /dll=R:\build\dev\devenv\dll\shellext\zipfldr\Debug\zipfldr.dll
55f6cf6954SWhindmar Saksit /shgfi=c:\freeldr.ini
56f6cf6954SWhindmar Saksit /assocq "" string 1 0 0 .txt
57f6cf6954SWhindmar Saksit /assocq "" string friendlytypename 0x400 0 .txt "" 10
58f6cf6954SWhindmar Saksit /openwindows /shellexec=c: /invoke properties
59f6cf6954SWhindmar Saksit /dumpmenu=%windir%\explorer.exe /extended
60f6cf6954SWhindmar Saksit /dumpmenu {D969A300-E7FF-11d0-A93B-00A0C90F2719}c:
61f6cf6954SWhindmar Saksit
62f6cf6954SWhindmar Saksit */
63f6cf6954SWhindmar Saksit
StrToNum(PCWSTR in)64f6cf6954SWhindmar Saksit static LONG StrToNum(PCWSTR in)
65f6cf6954SWhindmar Saksit {
66f6cf6954SWhindmar Saksit PWCHAR end;
67f6cf6954SWhindmar Saksit LONG v = wcstol(in, &end, 0);
68f6cf6954SWhindmar Saksit return (end > in) ? v : 0;
69f6cf6954SWhindmar Saksit }
70f6cf6954SWhindmar Saksit
ErrMsg(int Error)71f6cf6954SWhindmar Saksit static int ErrMsg(int Error)
72f6cf6954SWhindmar Saksit {
73f6cf6954SWhindmar Saksit WCHAR buf[400];
74f6cf6954SWhindmar Saksit for (UINT e = Error, cch; ;)
75f6cf6954SWhindmar Saksit {
76f6cf6954SWhindmar Saksit lstrcpynW(buf, L"?", _countof(buf));
77f6cf6954SWhindmar Saksit cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, e, 0, buf, _countof(buf), NULL);
78f6cf6954SWhindmar Saksit while (cch && buf[cch - 1] <= ' ')
79f6cf6954SWhindmar Saksit buf[--cch] = UNICODE_NULL; // Remove trailing newlines
80f6cf6954SWhindmar Saksit if (cch || HIWORD(e) != HIWORD(HRESULT_FROM_WIN32(1)))
81f6cf6954SWhindmar Saksit break;
82f6cf6954SWhindmar Saksit e = HRESULT_CODE(e); // "WIN32_FROM_HRESULT"
83f6cf6954SWhindmar Saksit }
84f6cf6954SWhindmar Saksit wprintf(Error < 0 ? L"Error 0x%.8X %s\n" : L"Error %d %s\n", Error, buf);
85f6cf6954SWhindmar Saksit return Error;
86f6cf6954SWhindmar Saksit }
87f6cf6954SWhindmar Saksit
88f6cf6954SWhindmar Saksit template<class T>
CLSIDPrefix(T & String,CLSID & Clsid)89f6cf6954SWhindmar Saksit static bool CLSIDPrefix(T& String, CLSID& Clsid)
90f6cf6954SWhindmar Saksit {
91f6cf6954SWhindmar Saksit WCHAR buf[38 + 1];
92f6cf6954SWhindmar Saksit if (String[0] == '{')
93f6cf6954SWhindmar Saksit {
94f6cf6954SWhindmar Saksit lstrcpynW(buf, String, _countof(buf));
95f6cf6954SWhindmar Saksit if (SUCCEEDED(CLSIDFromString(buf, &Clsid)))
96f6cf6954SWhindmar Saksit {
97f6cf6954SWhindmar Saksit String = String + 38;
98f6cf6954SWhindmar Saksit return true;
99f6cf6954SWhindmar Saksit }
100f6cf6954SWhindmar Saksit }
101f6cf6954SWhindmar Saksit return false;
102f6cf6954SWhindmar Saksit }
103f6cf6954SWhindmar Saksit
GetUIObjectOfAbsolute(LPCITEMIDLIST pidl,REFIID riid,void ** ppv)104f6cf6954SWhindmar Saksit static HRESULT GetUIObjectOfAbsolute(LPCITEMIDLIST pidl, REFIID riid, void** ppv)
105f6cf6954SWhindmar Saksit {
106f6cf6954SWhindmar Saksit CComPtr<IShellFolder> shellFolder;
107f6cf6954SWhindmar Saksit PCUITEMID_CHILD child;
108f6cf6954SWhindmar Saksit HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &child);
109f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
110f6cf6954SWhindmar Saksit hr = shellFolder->GetUIObjectOf(NULL, 1, &child, riid, NULL, ppv);
111f6cf6954SWhindmar Saksit return hr;
112f6cf6954SWhindmar Saksit }
113f6cf6954SWhindmar Saksit
CreateShellItemFromParse(PCWSTR Path,IShellItem ** ppSI)114f6cf6954SWhindmar Saksit static HRESULT CreateShellItemFromParse(PCWSTR Path, IShellItem** ppSI)
115f6cf6954SWhindmar Saksit {
116f6cf6954SWhindmar Saksit PIDLIST_ABSOLUTE pidl = NULL;
117f6cf6954SWhindmar Saksit HRESULT hr = SHParseDisplayName(Path, NULL, &pidl, 0, NULL);
118f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
119f6cf6954SWhindmar Saksit {
120f6cf6954SWhindmar Saksit hr = SHCreateShellItem(NULL, NULL, pidl, ppSI);
121f6cf6954SWhindmar Saksit SHFree(pidl);
122f6cf6954SWhindmar Saksit }
123f6cf6954SWhindmar Saksit return hr;
124f6cf6954SWhindmar Saksit }
125f6cf6954SWhindmar Saksit
GetAssocClass(LPCWSTR Path,LPCITEMIDLIST pidl,HKEY & hKey)126f6cf6954SWhindmar Saksit static void GetAssocClass(LPCWSTR Path, LPCITEMIDLIST pidl, HKEY& hKey)
127f6cf6954SWhindmar Saksit {
128f6cf6954SWhindmar Saksit hKey = NULL;
129f6cf6954SWhindmar Saksit IQueryAssociations* pQA;
130f6cf6954SWhindmar Saksit if (SUCCEEDED(GetUIObjectOfAbsolute(pidl, IID_PPV_ARG(IQueryAssociations, &pQA))))
131f6cf6954SWhindmar Saksit {
132f6cf6954SWhindmar Saksit pQA->GetKey(0, ASSOCKEY_CLASS, NULL, &hKey); // Not implemented in ROS
133f6cf6954SWhindmar Saksit pQA->Release();
134f6cf6954SWhindmar Saksit }
135f6cf6954SWhindmar Saksit if (!hKey)
136f6cf6954SWhindmar Saksit {
137f6cf6954SWhindmar Saksit DWORD cb;
138f6cf6954SWhindmar Saksit WCHAR buf[MAX_PATH];
139f6cf6954SWhindmar Saksit PWSTR ext = PathFindExtensionW(Path);
140f6cf6954SWhindmar Saksit SHFILEINFOW info;
141f6cf6954SWhindmar Saksit info.dwAttributes = 0;
142f6cf6954SWhindmar Saksit SHGetFileInfoW((LPWSTR)pidl, 0, &info, sizeof(info), SHGFI_PIDL | SHGFI_ATTRIBUTES);
143f6cf6954SWhindmar Saksit if (info.dwAttributes & SFGAO_FOLDER)
144f6cf6954SWhindmar Saksit {
145f6cf6954SWhindmar Saksit ext = const_cast<LPWSTR>(L"Directory");
146f6cf6954SWhindmar Saksit }
147f6cf6954SWhindmar Saksit else if (info.dwAttributes & SFGAO_BROWSABLE)
148f6cf6954SWhindmar Saksit {
149f6cf6954SWhindmar Saksit ext = const_cast<LPWSTR>(L"Folder"); // Best guess
150f6cf6954SWhindmar Saksit }
151f6cf6954SWhindmar Saksit else
152f6cf6954SWhindmar Saksit {
153f6cf6954SWhindmar Saksit cb = sizeof(buf);
154f6cf6954SWhindmar Saksit if (!SHGetValueW(HKEY_CLASSES_ROOT, ext, NULL, NULL, buf, &cb))
155f6cf6954SWhindmar Saksit {
156f6cf6954SWhindmar Saksit RegOpenKeyExW(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &hKey);
157f6cf6954SWhindmar Saksit }
158f6cf6954SWhindmar Saksit }
159f6cf6954SWhindmar Saksit if (!hKey)
160f6cf6954SWhindmar Saksit {
161f6cf6954SWhindmar Saksit RegOpenKeyExW(HKEY_CLASSES_ROOT, ext, 0, KEY_READ, &hKey);
162f6cf6954SWhindmar Saksit }
163f6cf6954SWhindmar Saksit }
164f6cf6954SWhindmar Saksit }
165f6cf6954SWhindmar Saksit
DumpBytes(const void * Data,SIZE_T cb)166f6cf6954SWhindmar Saksit static void DumpBytes(const void *Data, SIZE_T cb)
167f6cf6954SWhindmar Saksit {
168f6cf6954SWhindmar Saksit for (SIZE_T i = 0; i < cb; ++i)
169f6cf6954SWhindmar Saksit {
170f6cf6954SWhindmar Saksit wprintf(L"%s%.2X", i ? L" " : L"", ((LPCBYTE)Data)[i]);
171f6cf6954SWhindmar Saksit }
172f6cf6954SWhindmar Saksit wprintf(L"\n");
173f6cf6954SWhindmar Saksit }
174f6cf6954SWhindmar Saksit
GetCommandString(IContextMenu & CM,UINT Id,UINT Type,LPWSTR buf,UINT cchMax)175f6cf6954SWhindmar Saksit static HRESULT GetCommandString(IContextMenu& CM, UINT Id, UINT Type, LPWSTR buf, UINT cchMax)
176f6cf6954SWhindmar Saksit {
177f6cf6954SWhindmar Saksit if (cchMax < 1) return E_INVALIDARG;
178f6cf6954SWhindmar Saksit *buf = UNICODE_NULL;
179f6cf6954SWhindmar Saksit
180f6cf6954SWhindmar Saksit // First try to retrieve the UNICODE string directly
181f6cf6954SWhindmar Saksit HRESULT hr = CM.GetCommandString(Id, Type | GCS_UNICODE, 0, (char*)buf, cchMax);
182f6cf6954SWhindmar Saksit if (FAILED(hr))
183f6cf6954SWhindmar Saksit {
184f6cf6954SWhindmar Saksit // It failed, try to retrieve an ANSI string instead then convert it to UNICODE
185f6cf6954SWhindmar Saksit STRRET sr;
186f6cf6954SWhindmar Saksit sr.uType = STRRET_CSTR;
187f6cf6954SWhindmar Saksit hr = CM.GetCommandString(Id, Type & ~GCS_UNICODE, 0, sr.cStr, _countof(sr.cStr));
188f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
189f6cf6954SWhindmar Saksit hr = StrRetToBufW(&sr, NULL, buf, cchMax);
190f6cf6954SWhindmar Saksit }
191f6cf6954SWhindmar Saksit return hr;
192f6cf6954SWhindmar Saksit }
193f6cf6954SWhindmar Saksit
DumpMenu(HMENU hMenu,UINT IdOffset,IContextMenu * pCM,BOOL FakeInit,UINT Indent)194f6cf6954SWhindmar Saksit static void DumpMenu(HMENU hMenu, UINT IdOffset, IContextMenu* pCM, BOOL FakeInit, UINT Indent)
195f6cf6954SWhindmar Saksit {
196f6cf6954SWhindmar Saksit bool recurse = Indent != UINT(-1);
197f6cf6954SWhindmar Saksit WCHAR buf[MAX_PATH];
198f6cf6954SWhindmar Saksit MENUITEMINFOW mii;
199f6cf6954SWhindmar Saksit mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
200f6cf6954SWhindmar Saksit
201f6cf6954SWhindmar Saksit for (UINT i = 0, defid = GetMenuDefaultItem(hMenu, FALSE, 0); ; ++i)
202f6cf6954SWhindmar Saksit {
203f6cf6954SWhindmar Saksit mii.fMask = MIIM_STRING;
204f6cf6954SWhindmar Saksit mii.dwTypeData = buf;
205f6cf6954SWhindmar Saksit mii.cch = _countof(buf);
206f6cf6954SWhindmar Saksit *buf = UNICODE_NULL;
207f6cf6954SWhindmar Saksit if (!GetMenuItemInfo(hMenu, i, TRUE, &mii))
208f6cf6954SWhindmar Saksit lstrcpynW(buf, L"?", _countof(buf)); // Tolerate string failure
209f6cf6954SWhindmar Saksit mii.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_FTYPE;
210f6cf6954SWhindmar Saksit mii.hSubMenu = NULL;
211f6cf6954SWhindmar Saksit mii.dwTypeData = NULL;
212f6cf6954SWhindmar Saksit mii.cch = 0;
213f6cf6954SWhindmar Saksit if (!GetMenuItemInfo(hMenu, i, TRUE, &mii))
214f6cf6954SWhindmar Saksit break;
215f6cf6954SWhindmar Saksit
216f6cf6954SWhindmar Saksit BOOL sep = mii.fType & MFT_SEPARATOR;
217f6cf6954SWhindmar Saksit wprintf(L"%-4d", (sep || mii.wID == UINT(-1)) ? mii.wID : (mii.wID - IdOffset));
218f6cf6954SWhindmar Saksit for (UINT j = 0; j < Indent && recurse; ++j)
219f6cf6954SWhindmar Saksit wprintf(L" ");
220f6cf6954SWhindmar Saksit wprintf(L"%s%s", mii.hSubMenu ? L">" : L"|", sep ? L"----------" : buf);
221f6cf6954SWhindmar Saksit if (!sep && pCM && SUCCEEDED(GetCommandString(*pCM, mii.wID - IdOffset,
222f6cf6954SWhindmar Saksit GCS_VERB, buf, _countof(buf))))
223f6cf6954SWhindmar Saksit {
224f6cf6954SWhindmar Saksit wprintf(L" [%s]", buf);
225f6cf6954SWhindmar Saksit }
226f6cf6954SWhindmar Saksit wprintf(L"%s\n", (defid == mii.wID && defid != UINT(-1)) ? L" (Default)" : L"");
227f6cf6954SWhindmar Saksit if (mii.hSubMenu && recurse)
228f6cf6954SWhindmar Saksit {
229f6cf6954SWhindmar Saksit if (FakeInit)
230f6cf6954SWhindmar Saksit SHForwardContextMenuMsg(pCM, WM_INITMENUPOPUP, (WPARAM)mii.hSubMenu, LOWORD(i), NULL, TRUE);
231f6cf6954SWhindmar Saksit DumpMenu(mii.hSubMenu, IdOffset, pCM, FakeInit, Indent + 1);
232f6cf6954SWhindmar Saksit }
233f6cf6954SWhindmar Saksit }
234f6cf6954SWhindmar Saksit }
235f6cf6954SWhindmar Saksit
SHGFI(PCWSTR Path)236f6cf6954SWhindmar Saksit static int SHGFI(PCWSTR Path)
237f6cf6954SWhindmar Saksit {
238f6cf6954SWhindmar Saksit PIDLIST_ABSOLUTE pidl = NULL;
239f6cf6954SWhindmar Saksit UINT flags = 0, ret = 0;
240f6cf6954SWhindmar Saksit
241f6cf6954SWhindmar Saksit if (*Path == L'$')
242f6cf6954SWhindmar Saksit {
243f6cf6954SWhindmar Saksit HRESULT hr = SHParseDisplayName(++Path, NULL, &pidl, 0, NULL);
244f6cf6954SWhindmar Saksit if (FAILED(hr))
245f6cf6954SWhindmar Saksit return ErrMsg(hr);
246f6cf6954SWhindmar Saksit flags |= SHGFI_PIDL;
247f6cf6954SWhindmar Saksit Path = (LPCWSTR)pidl;
248f6cf6954SWhindmar Saksit }
249f6cf6954SWhindmar Saksit else if (GetFileAttributes(Path) == INVALID_FILE_ATTRIBUTES)
250f6cf6954SWhindmar Saksit {
251f6cf6954SWhindmar Saksit flags |= SHGFI_USEFILEATTRIBUTES;
252f6cf6954SWhindmar Saksit }
253f6cf6954SWhindmar Saksit SHFILEINFOW info;
254f6cf6954SWhindmar Saksit if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags |
255f6cf6954SWhindmar Saksit SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_TYPENAME))
256f6cf6954SWhindmar Saksit {
257f6cf6954SWhindmar Saksit info.szDisplayName[0] = info.szTypeName[0] = UNICODE_NULL;
258f6cf6954SWhindmar Saksit info.dwAttributes = 0;
259f6cf6954SWhindmar Saksit ret = ERROR_FILE_NOT_FOUND;
260f6cf6954SWhindmar Saksit }
261f6cf6954SWhindmar Saksit wprintf(L"Display: %s\n", info.szDisplayName);
262f6cf6954SWhindmar Saksit wprintf(L"Attributes: 0x%x\n", info.dwAttributes);
263f6cf6954SWhindmar Saksit wprintf(L"Type: %s\n", info.szTypeName);
264f6cf6954SWhindmar Saksit
265f6cf6954SWhindmar Saksit if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags | SHGFI_ICONLOCATION))
266f6cf6954SWhindmar Saksit {
267f6cf6954SWhindmar Saksit info.szDisplayName[0] = UNICODE_NULL;
268f6cf6954SWhindmar Saksit info.iIcon = -1;
269f6cf6954SWhindmar Saksit }
270f6cf6954SWhindmar Saksit wprintf(L"Icon: %s,%d\n", info.szDisplayName, info.iIcon);
271f6cf6954SWhindmar Saksit
272f6cf6954SWhindmar Saksit if (!SHGetFileInfoW(Path, 0, &info, sizeof(info), flags | SHGFI_SYSICONINDEX))
273f6cf6954SWhindmar Saksit {
274f6cf6954SWhindmar Saksit info.iIcon = -1;
275f6cf6954SWhindmar Saksit }
276f6cf6954SWhindmar Saksit wprintf(L"Index: %d\n", info.iIcon);
277f6cf6954SWhindmar Saksit SHFree(pidl);
278f6cf6954SWhindmar Saksit return ret;
279f6cf6954SWhindmar Saksit }
280f6cf6954SWhindmar Saksit
AssocQ(int argc,WCHAR ** argv)281f6cf6954SWhindmar Saksit static HRESULT AssocQ(int argc, WCHAR **argv)
282f6cf6954SWhindmar Saksit {
283f6cf6954SWhindmar Saksit UINT qtype = StrToNum(argv[2]);
284f6cf6954SWhindmar Saksit ASSOCF iflags = StrToNum(argv[3]);
285f6cf6954SWhindmar Saksit ASSOCF qflags = StrToNum(argv[4]);
286f6cf6954SWhindmar Saksit PCWSTR extra = (argc > 6 && *argv[6]) ? argv[6] : NULL;
287f6cf6954SWhindmar Saksit WCHAR buf[MAX_PATH * 2];
288f6cf6954SWhindmar Saksit DWORD maxSize = (argc > 7 && *argv[7]) ? StrToNum(argv[7]) : sizeof(buf);
289f6cf6954SWhindmar Saksit
290f6cf6954SWhindmar Saksit HRESULT hr;
291f6cf6954SWhindmar Saksit CComPtr<IQueryAssociations> qa;
292f6cf6954SWhindmar Saksit PWSTR path = argv[0];
293f6cf6954SWhindmar Saksit if (*path)
294f6cf6954SWhindmar Saksit {
295f6cf6954SWhindmar Saksit CLSID clsid, *pclsid = NULL;
296f6cf6954SWhindmar Saksit if (CLSIDPrefix(path, clsid))
297f6cf6954SWhindmar Saksit pclsid = &clsid;
298f6cf6954SWhindmar Saksit CComPtr<IShellItem> si;
299f6cf6954SWhindmar Saksit if (SUCCEEDED(hr = CreateShellItemFromParse(path, &si)))
300f6cf6954SWhindmar Saksit {
301f6cf6954SWhindmar Saksit hr = si->BindToHandler(NULL, pclsid ? *pclsid : BHID_AssociationArray, IID_PPV_ARG(IQueryAssociations, &qa));
302f6cf6954SWhindmar Saksit if (FAILED(hr) && !pclsid)
303f6cf6954SWhindmar Saksit hr = si->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IQueryAssociations, &qa));
304f6cf6954SWhindmar Saksit }
305f6cf6954SWhindmar Saksit }
306f6cf6954SWhindmar Saksit else
307f6cf6954SWhindmar Saksit {
308f6cf6954SWhindmar Saksit hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &qa));
309f6cf6954SWhindmar Saksit }
310f6cf6954SWhindmar Saksit if (FAILED(hr))
311f6cf6954SWhindmar Saksit return ErrMsg(hr);
312f6cf6954SWhindmar Saksit hr = qa->Init(iflags, argv[5], NULL, NULL);
313f6cf6954SWhindmar Saksit if (FAILED(hr))
314f6cf6954SWhindmar Saksit return ErrMsg(hr);
315f6cf6954SWhindmar Saksit
316f6cf6954SWhindmar Saksit DWORD size = maxSize;
317*e4930be4STimo Kreuzer if (!_wcsicmp(argv[1], L"string"))
318f6cf6954SWhindmar Saksit {
319*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"COMMAND"))
320f6cf6954SWhindmar Saksit qtype = ASSOCSTR_COMMAND;
321*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"EXECUTABLE"))
322f6cf6954SWhindmar Saksit qtype = ASSOCSTR_EXECUTABLE;
323*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"FRIENDLYDOCNAME") || !_wcsicmp(argv[2], L"FriendlyTypeName"))
324f6cf6954SWhindmar Saksit qtype = ASSOCSTR_FRIENDLYDOCNAME;
325*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"DEFAULTICON"))
326f6cf6954SWhindmar Saksit qtype = ASSOCSTR_DEFAULTICON;
327f6cf6954SWhindmar Saksit
328f6cf6954SWhindmar Saksit buf[0] = UNICODE_NULL;
329f6cf6954SWhindmar Saksit size /= sizeof(buf[0]); // Convert to number of characters
330f6cf6954SWhindmar Saksit hr = qa->GetString(qflags, (ASSOCSTR)qtype, extra, buf, &size);
331f6cf6954SWhindmar Saksit size *= sizeof(buf[0]); // Convert back to bytes
332f6cf6954SWhindmar Saksit if (SUCCEEDED(hr) ||
333f6cf6954SWhindmar Saksit hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
334f6cf6954SWhindmar Saksit {
335f6cf6954SWhindmar Saksit wprintf(L"0x%.8X: %s\n", hr, buf);
336f6cf6954SWhindmar Saksit }
337f6cf6954SWhindmar Saksit else
338f6cf6954SWhindmar Saksit {
339f6cf6954SWhindmar Saksit wprintf(size != maxSize ? L"%u " : L"", size);
340f6cf6954SWhindmar Saksit ErrMsg(hr);
341f6cf6954SWhindmar Saksit }
342f6cf6954SWhindmar Saksit }
343*e4930be4STimo Kreuzer else if (!_wcsicmp(argv[1], L"data"))
344f6cf6954SWhindmar Saksit {
345*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"EDITFLAGS"))
346f6cf6954SWhindmar Saksit qtype = ASSOCDATA_EDITFLAGS;
347*e4930be4STimo Kreuzer if (!_wcsicmp(argv[2], L"VALUE"))
348f6cf6954SWhindmar Saksit qtype = ASSOCDATA_VALUE;
349f6cf6954SWhindmar Saksit
350f6cf6954SWhindmar Saksit hr = qa->GetData(qflags, (ASSOCDATA)qtype, extra, buf, &size);
351f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
352f6cf6954SWhindmar Saksit {
353f6cf6954SWhindmar Saksit wprintf(L"0x%.8X: %u byte(s) ", hr, size);
354f6cf6954SWhindmar Saksit DumpBytes(buf, min(size, maxSize));
355f6cf6954SWhindmar Saksit }
356f6cf6954SWhindmar Saksit else
357f6cf6954SWhindmar Saksit {
358f6cf6954SWhindmar Saksit wprintf(size != maxSize ? L"%u " : L"", size);
359f6cf6954SWhindmar Saksit ErrMsg(hr);
360f6cf6954SWhindmar Saksit }
361f6cf6954SWhindmar Saksit }
362*e4930be4STimo Kreuzer else if (!_wcsicmp(argv[1], L"key"))
363f6cf6954SWhindmar Saksit {
364f6cf6954SWhindmar Saksit HKEY hKey = NULL;
365f6cf6954SWhindmar Saksit hr = qa->GetKey(qflags, (ASSOCKEY)qtype, extra, &hKey);
366f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
367f6cf6954SWhindmar Saksit {
368f6cf6954SWhindmar Saksit wprintf(L"0x%.8X: hKey %p\n", hr, hKey);
369f6cf6954SWhindmar Saksit RegQueryValueExW(hKey, L"shlextdbg", 0, NULL, NULL, NULL); // Filter by this in Process Monitor
370f6cf6954SWhindmar Saksit RegCloseKey(hKey);
371f6cf6954SWhindmar Saksit }
372f6cf6954SWhindmar Saksit else
373f6cf6954SWhindmar Saksit {
374f6cf6954SWhindmar Saksit ErrMsg(hr);
375f6cf6954SWhindmar Saksit }
376f6cf6954SWhindmar Saksit }
377f6cf6954SWhindmar Saksit else
378f6cf6954SWhindmar Saksit {
379f6cf6954SWhindmar Saksit PrintHelp(L"Unknown query");
380f6cf6954SWhindmar Saksit return ErrMsg(ERROR_INVALID_PARAMETER);
381f6cf6954SWhindmar Saksit }
382f6cf6954SWhindmar Saksit return hr;
383f6cf6954SWhindmar Saksit }
384cb885480SMark Jansen
385cb885480SMark Jansen enum WaitType
386cb885480SMark Jansen {
387cb885480SMark Jansen Wait_None,
388cb885480SMark Jansen Wait_Infinite,
389cb885480SMark Jansen Wait_OpenWindows,
390cb885480SMark Jansen Wait_Input,
391f6cf6954SWhindmar Saksit Wait_ExplorerInstance,
392cb885480SMark Jansen };
393cb885480SMark Jansen
394cb885480SMark Jansen CLSID g_CLSID = { 0 };
395cb885480SMark Jansen CStringW g_DLL;
396cb885480SMark Jansen CStringW g_ShellExtInit;
397cb885480SMark Jansen bool g_bIShellPropSheetExt = false;
398cb885480SMark Jansen CStringA g_ContextMenu;
399f6cf6954SWhindmar Saksit WaitType g_Wait = Wait_ExplorerInstance;
400cb885480SMark Jansen
CreateIDataObject(CComHeapPtr<ITEMIDLIST> & pidl,CComPtr<IDataObject> & dataObject,PCWSTR FileName)401cb885480SMark Jansen HRESULT CreateIDataObject(CComHeapPtr<ITEMIDLIST>& pidl, CComPtr<IDataObject>& dataObject, PCWSTR FileName)
402cb885480SMark Jansen {
403cb885480SMark Jansen HRESULT hr = SHParseDisplayName(FileName, NULL, &pidl, 0, NULL);
404cb885480SMark Jansen if (!SUCCEEDED(hr))
405cb885480SMark Jansen {
406cb885480SMark Jansen wprintf(L"Failed to create pidl from '%s': 0x%x\n", FileName, hr);
407cb885480SMark Jansen return hr;
408cb885480SMark Jansen }
409cb885480SMark Jansen
410cb885480SMark Jansen CComPtr<IShellFolder> shellFolder;
411cb885480SMark Jansen PCUITEMID_CHILD childs;
412cb885480SMark Jansen hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &childs);
413cb885480SMark Jansen if (!SUCCEEDED(hr))
414cb885480SMark Jansen {
415cb885480SMark Jansen wprintf(L"Failed to bind to parent: 0x%x\n", hr);
416cb885480SMark Jansen return hr;
417cb885480SMark Jansen }
418cb885480SMark Jansen hr = shellFolder->GetUIObjectOf(NULL, 1, &childs, IID_IDataObject, NULL, (PVOID*)&dataObject);
419cb885480SMark Jansen if (!SUCCEEDED(hr))
420cb885480SMark Jansen {
421cb885480SMark Jansen wprintf(L"Failed to query IDataObject: 0x%x\n", hr);
422cb885480SMark Jansen }
423cb885480SMark Jansen return hr;
424cb885480SMark Jansen }
425cb885480SMark Jansen
LoadAndInitialize(REFIID riid,LPVOID * ppv)426cb885480SMark Jansen HRESULT LoadAndInitialize(REFIID riid, LPVOID* ppv)
427cb885480SMark Jansen {
428cb885480SMark Jansen CComPtr<IShellExtInit> spShellExtInit;
429cb885480SMark Jansen HRESULT hr;
430cb885480SMark Jansen if (g_DLL.IsEmpty())
431cb885480SMark Jansen {
432cb885480SMark Jansen hr = CoCreateInstance(g_CLSID, NULL, CLSCTX_ALL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
433cb885480SMark Jansen if (!SUCCEEDED(hr))
434cb885480SMark Jansen {
435cb885480SMark Jansen WCHAR Buffer[100];
436cb885480SMark Jansen StringFromGUID2(g_CLSID, Buffer, _countof(Buffer));
437cb885480SMark Jansen wprintf(L"Failed to Create %s:IShellExtInit: 0x%x\n", Buffer, hr);
438cb885480SMark Jansen return hr;
439cb885480SMark Jansen }
440cb885480SMark Jansen }
441cb885480SMark Jansen else
442cb885480SMark Jansen {
443cb885480SMark Jansen typedef HRESULT (STDAPICALLTYPE *tDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
444cb885480SMark Jansen HMODULE mod = LoadLibraryW(g_DLL);
445cb885480SMark Jansen if (!mod)
446cb885480SMark Jansen {
447cb885480SMark Jansen wprintf(L"Failed to Load %s:(0x%x)\n", g_DLL.GetString(), GetLastError());
448cb885480SMark Jansen return E_FAIL;
449cb885480SMark Jansen }
450cb885480SMark Jansen tDllGetClassObject DllGet = (tDllGetClassObject)GetProcAddress(mod, "DllGetClassObject");
451cb885480SMark Jansen if (!DllGet)
452cb885480SMark Jansen {
453cb885480SMark Jansen wprintf(L"%s does not export DllGetClassObject\n", g_DLL.GetString());
454cb885480SMark Jansen return E_FAIL;
455cb885480SMark Jansen }
456cb885480SMark Jansen CComPtr<IClassFactory> spClassFactory;
457cb885480SMark Jansen hr = DllGet(g_CLSID, IID_PPV_ARG(IClassFactory, &spClassFactory));
458cb885480SMark Jansen if (!SUCCEEDED(hr))
459cb885480SMark Jansen {
460cb885480SMark Jansen wprintf(L"Failed to create IClassFactory: 0x%x\n", hr);
461cb885480SMark Jansen return hr;
462cb885480SMark Jansen }
463cb885480SMark Jansen hr = spClassFactory->CreateInstance(NULL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
464cb885480SMark Jansen if (!SUCCEEDED(hr))
465cb885480SMark Jansen {
466cb885480SMark Jansen wprintf(L"Failed to Request IShellExtInit from IClassFactory: 0x%x\n", hr);
467cb885480SMark Jansen return hr;
468cb885480SMark Jansen }
469cb885480SMark Jansen }
470cb885480SMark Jansen
471cb885480SMark Jansen CComPtr<IDataObject> spDataObject;
472cb885480SMark Jansen CComHeapPtr<ITEMIDLIST> pidl;
473cb885480SMark Jansen hr = CreateIDataObject(pidl, spDataObject, g_ShellExtInit.GetString());
474cb885480SMark Jansen if (!SUCCEEDED(hr))
475cb885480SMark Jansen return hr;
476cb885480SMark Jansen
477f6cf6954SWhindmar Saksit HKEY hKey = NULL;
478f6cf6954SWhindmar Saksit GetAssocClass(g_ShellExtInit.GetString(), pidl, hKey);
479f6cf6954SWhindmar Saksit hr = spShellExtInit->Initialize(pidl, spDataObject, hKey);
480f6cf6954SWhindmar Saksit if (hKey)
481f6cf6954SWhindmar Saksit RegCloseKey(hKey);
482cb885480SMark Jansen if (!SUCCEEDED(hr))
483cb885480SMark Jansen {
484cb885480SMark Jansen wprintf(L"IShellExtInit->Initialize failed: 0x%x\n", hr);
485cb885480SMark Jansen return hr;
486cb885480SMark Jansen }
487cb885480SMark Jansen hr = spShellExtInit->QueryInterface(riid, ppv);
488cb885480SMark Jansen if (!SUCCEEDED(hr))
489cb885480SMark Jansen {
490cb885480SMark Jansen WCHAR Buffer[100];
491cb885480SMark Jansen StringFromGUID2(riid, Buffer, _countof(Buffer));
492cb885480SMark Jansen wprintf(L"Failed to query %s from IShellExtInit: 0x%x\n", Buffer, hr);
493cb885480SMark Jansen }
494cb885480SMark Jansen return hr;
495cb885480SMark Jansen }
496cb885480SMark Jansen
497cb885480SMark Jansen
498cb885480SMark Jansen CSimpleArray<HWND> g_Windows;
499cb885480SMark Jansen HWND g_ConsoleWindow;
EnumWindowsProc(HWND hwnd,LPARAM lParam)500cb885480SMark Jansen BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
501cb885480SMark Jansen {
502cb885480SMark Jansen if (hwnd != g_ConsoleWindow)
503cb885480SMark Jansen {
504cb885480SMark Jansen DWORD pid = 0;
505cb885480SMark Jansen GetWindowThreadProcessId(hwnd, &pid);
506cb885480SMark Jansen if (pid == GetCurrentProcessId())
507cb885480SMark Jansen {
508cb885480SMark Jansen g_Windows.Add(hwnd);
509cb885480SMark Jansen }
510cb885480SMark Jansen }
511cb885480SMark Jansen return TRUE;
512cb885480SMark Jansen }
513cb885480SMark Jansen
WaitWindows()514cb885480SMark Jansen void WaitWindows()
515cb885480SMark Jansen {
516cb885480SMark Jansen /* Give the windows some time to spawn */
517cb885480SMark Jansen Sleep(2000);
518cb885480SMark Jansen g_ConsoleWindow = GetConsoleWindow();
519cb885480SMark Jansen while (true)
520cb885480SMark Jansen {
521cb885480SMark Jansen g_Windows.RemoveAll();
522cb885480SMark Jansen EnumWindows(EnumWindowsProc, NULL);
523cb885480SMark Jansen if (g_Windows.GetSize() == 0)
524cb885480SMark Jansen break;
525cb885480SMark Jansen Sleep(500);
526cb885480SMark Jansen }
527cb885480SMark Jansen wprintf(L"All windows closed (ignoring console window)\n");
528cb885480SMark Jansen }
529cb885480SMark Jansen
530f6cf6954SWhindmar Saksit struct ExplorerInstance : public IUnknown
531f6cf6954SWhindmar Saksit {
532f6cf6954SWhindmar Saksit HWND m_hWnd;
533f6cf6954SWhindmar Saksit volatile LONG m_rc;
534cb885480SMark Jansen
ExplorerInstanceExplorerInstance535f6cf6954SWhindmar Saksit ExplorerInstance() : m_hWnd(NULL), m_rc(1) {}
QueryInterfaceExplorerInstance536f6cf6954SWhindmar Saksit virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
537f6cf6954SWhindmar Saksit {
538f6cf6954SWhindmar Saksit static const QITAB rgqit[] = { { 0 } };
539f6cf6954SWhindmar Saksit return QISearch(this, rgqit, riid, ppv);
540f6cf6954SWhindmar Saksit }
AddRefExplorerInstance541f6cf6954SWhindmar Saksit virtual ULONG STDMETHODCALLTYPE AddRef()
542f6cf6954SWhindmar Saksit {
543f6cf6954SWhindmar Saksit if (g_Wait == Wait_ExplorerInstance)
544f6cf6954SWhindmar Saksit wprintf(L"INFO: SHGetInstanceExplorer\n");
545f6cf6954SWhindmar Saksit return InterlockedIncrement(&m_rc);
546f6cf6954SWhindmar Saksit }
ReleaseExplorerInstance547f6cf6954SWhindmar Saksit virtual ULONG STDMETHODCALLTYPE Release()
548f6cf6954SWhindmar Saksit {
549f6cf6954SWhindmar Saksit if (g_Wait == Wait_ExplorerInstance)
550f6cf6954SWhindmar Saksit wprintf(L"INFO: Release ExplorerInstance\n");
551f6cf6954SWhindmar Saksit ULONG r = InterlockedDecrement(&m_rc);
552f6cf6954SWhindmar Saksit if (!r)
553f6cf6954SWhindmar Saksit PostMessage(m_hWnd, WM_CLOSE, 0, 0);
554f6cf6954SWhindmar Saksit return r;
555f6cf6954SWhindmar Saksit }
WaitExplorerInstance556f6cf6954SWhindmar Saksit void Wait()
557f6cf6954SWhindmar Saksit {
558f6cf6954SWhindmar Saksit SHSetInstanceExplorer(NULL);
559f6cf6954SWhindmar Saksit m_hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("STATIC"), NULL, WS_POPUP,
560f6cf6954SWhindmar Saksit 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
561f6cf6954SWhindmar Saksit BOOL loop = InterlockedDecrement(&m_rc) != 0;
562f6cf6954SWhindmar Saksit MSG msg;
563f6cf6954SWhindmar Saksit while (loop && (int)GetMessage(&msg, NULL, 0, 0) > 0)
564f6cf6954SWhindmar Saksit {
565f6cf6954SWhindmar Saksit if (msg.hwnd == m_hWnd && msg.message == WM_CLOSE)
566f6cf6954SWhindmar Saksit PostMessage(m_hWnd, WM_QUIT, 0, 0);
567f6cf6954SWhindmar Saksit DispatchMessage(&msg);
568f6cf6954SWhindmar Saksit }
569f6cf6954SWhindmar Saksit }
570f6cf6954SWhindmar Saksit } g_EI;
571f6cf6954SWhindmar Saksit
Wait()572f6cf6954SWhindmar Saksit static void Wait()
573f6cf6954SWhindmar Saksit {
574f6cf6954SWhindmar Saksit LPCWSTR nag = L"(Please use SHGetInstanceExplorer in your code instead)";
575f6cf6954SWhindmar Saksit switch (g_Wait)
576f6cf6954SWhindmar Saksit {
577f6cf6954SWhindmar Saksit case Wait_None:
578f6cf6954SWhindmar Saksit break;
579f6cf6954SWhindmar Saksit case Wait_Infinite:
580f6cf6954SWhindmar Saksit _putws(nag);
581f6cf6954SWhindmar Saksit while (true)
582f6cf6954SWhindmar Saksit Sleep(1000);
583f6cf6954SWhindmar Saksit break;
584f6cf6954SWhindmar Saksit case Wait_OpenWindows:
585f6cf6954SWhindmar Saksit _putws(nag);
586f6cf6954SWhindmar Saksit WaitWindows();
587f6cf6954SWhindmar Saksit break;
588f6cf6954SWhindmar Saksit case Wait_Input:
589f6cf6954SWhindmar Saksit wprintf(L"Press any key to continue... %s\n", nag);
590f6cf6954SWhindmar Saksit _getch();
591f6cf6954SWhindmar Saksit break;
592f6cf6954SWhindmar Saksit case Wait_ExplorerInstance:
593f6cf6954SWhindmar Saksit g_EI.Wait();
594f6cf6954SWhindmar Saksit break;
595f6cf6954SWhindmar Saksit }
596f6cf6954SWhindmar Saksit }
597cb885480SMark Jansen
598cb885480SMark Jansen CSimpleArray<HPROPSHEETPAGE> g_Pages;
cb_AddPage(HPROPSHEETPAGE page,LPARAM lParam)599cb885480SMark Jansen static BOOL CALLBACK cb_AddPage(HPROPSHEETPAGE page, LPARAM lParam)
600cb885480SMark Jansen {
601cb885480SMark Jansen g_Pages.Add(page);
602cb885480SMark Jansen if (lParam != (LPARAM)&g_Pages)
603cb885480SMark Jansen {
60458588b76STimo Kreuzer wprintf(L"Propsheet failed to pass lParam, got: 0x%Ix\n", lParam);
605cb885480SMark Jansen }
606cb885480SMark Jansen return TRUE;
607cb885480SMark Jansen }
608cb885480SMark Jansen
isCmdWithArg(int argc,WCHAR ** argv,int & n,PCWSTR check,PCWSTR & arg)609cb885480SMark Jansen static bool isCmdWithArg(int argc, WCHAR** argv, int& n, PCWSTR check, PCWSTR &arg)
610cb885480SMark Jansen {
611cb885480SMark Jansen arg = NULL;
612cb885480SMark Jansen size_t len = wcslen(check);
613cb885480SMark Jansen if (!_wcsnicmp(argv[n] + 1, check, len))
614cb885480SMark Jansen {
615cb885480SMark Jansen PCWSTR cmd = argv[n] + len + 1;
616cb885480SMark Jansen if (*cmd == ':' || *cmd == '=')
617cb885480SMark Jansen {
618cb885480SMark Jansen arg = cmd + 1;
619cb885480SMark Jansen return true;
620cb885480SMark Jansen }
621cb885480SMark Jansen if (n + 1 < argc)
622cb885480SMark Jansen {
623cb885480SMark Jansen arg = argv[n+1];
624cb885480SMark Jansen n++;
625cb885480SMark Jansen return true;
626cb885480SMark Jansen }
627cb885480SMark Jansen wprintf(L"Command %s has no required argument!\n", check);
628cb885480SMark Jansen return false;
629cb885480SMark Jansen }
630cb885480SMark Jansen return false;
631cb885480SMark Jansen }
632cb885480SMark Jansen
isCmd(int argc,WCHAR ** argv,int n,PCWSTR check)633cb885480SMark Jansen static bool isCmd(int argc, WCHAR** argv, int n, PCWSTR check)
634cb885480SMark Jansen {
635*e4930be4STimo Kreuzer return !_wcsicmp(argv[n] + 1, check);
636cb885480SMark Jansen }
637cb885480SMark Jansen
638cb885480SMark Jansen extern "C" // and another hack for gcc
wmain(int argc,WCHAR ** argv)639cb885480SMark Jansen int wmain(int argc, WCHAR **argv)
640cb885480SMark Jansen {
641f6cf6954SWhindmar Saksit INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_LINK_CLASS | ICC_STANDARD_CLASSES };
642f6cf6954SWhindmar Saksit InitCommonControlsEx(&icc);
643f6cf6954SWhindmar Saksit CoInitialize(NULL);
644f6cf6954SWhindmar Saksit SHSetInstanceExplorer(static_cast<IUnknown*>(&g_EI));
645f6cf6954SWhindmar Saksit
646cb885480SMark Jansen bool failArgs = false;
647cb885480SMark Jansen for (int n = 1; n < argc; ++n)
648cb885480SMark Jansen {
649cb885480SMark Jansen WCHAR* cmd = argv[n];
650cb885480SMark Jansen if (cmd[0] == '-' || cmd[0] == '/')
651cb885480SMark Jansen {
652cb885480SMark Jansen PCWSTR arg;
653f6cf6954SWhindmar Saksit if (isCmdWithArg(argc, argv, n, L"shgfi", arg))
654f6cf6954SWhindmar Saksit {
655f6cf6954SWhindmar Saksit failArgs = true;
656f6cf6954SWhindmar Saksit if (*arg)
657f6cf6954SWhindmar Saksit return SHGFI(arg);
658f6cf6954SWhindmar Saksit }
659f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"assocq"))
660f6cf6954SWhindmar Saksit {
661f6cf6954SWhindmar Saksit failArgs = true;
662f6cf6954SWhindmar Saksit if (argc - (n + 1) >= 6 && argc - (n + 1) <= 8)
663f6cf6954SWhindmar Saksit return AssocQ(argc - (n + 1), &argv[(n + 1)]);
664f6cf6954SWhindmar Saksit }
665f6cf6954SWhindmar Saksit else if (isCmdWithArg(argc, argv, n, L"shellexec", arg))
666f6cf6954SWhindmar Saksit {
667f6cf6954SWhindmar Saksit PIDLIST_ABSOLUTE pidl = NULL;
668f6cf6954SWhindmar Saksit HRESULT hr = SHParseDisplayName(arg, NULL, &pidl, 0, NULL);
669f6cf6954SWhindmar Saksit if (FAILED(hr))
670f6cf6954SWhindmar Saksit return ErrMsg(hr);
671f6cf6954SWhindmar Saksit SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_IDLIST | SEE_MASK_UNICODE };
672f6cf6954SWhindmar Saksit sei.lpIDList = pidl;
673f6cf6954SWhindmar Saksit sei.nShow = SW_SHOW;
674f6cf6954SWhindmar Saksit while (++n < argc)
675f6cf6954SWhindmar Saksit {
676f6cf6954SWhindmar Saksit if (argv[n][0] != '-' && argv[n][0] != '/')
677f6cf6954SWhindmar Saksit break;
678f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"INVOKE"))
679f6cf6954SWhindmar Saksit sei.fMask |= SEE_MASK_INVOKEIDLIST;
680f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"NOUI"))
681f6cf6954SWhindmar Saksit sei.fMask |= SEE_MASK_FLAG_NO_UI;
682f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"ASYNCOK"))
683f6cf6954SWhindmar Saksit sei.fMask |= SEE_MASK_ASYNCOK ;
684f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"NOASYNC"))
685f6cf6954SWhindmar Saksit sei.fMask |= SEE_MASK_NOASYNC;
686f6cf6954SWhindmar Saksit else
687f6cf6954SWhindmar Saksit wprintf(L"WARN: Ignoring switch %s\n", argv[n]);
688f6cf6954SWhindmar Saksit }
689f6cf6954SWhindmar Saksit if (n < argc && *argv[n++])
690f6cf6954SWhindmar Saksit {
691f6cf6954SWhindmar Saksit sei.lpVerb = argv[n - 1];
692f6cf6954SWhindmar Saksit }
693f6cf6954SWhindmar Saksit if (n < argc && *argv[n++])
694f6cf6954SWhindmar Saksit {
695f6cf6954SWhindmar Saksit sei.lpClass = argv[n - 1];
696f6cf6954SWhindmar Saksit sei.fMask |= SEE_MASK_CLASSNAME;
697f6cf6954SWhindmar Saksit }
698f6cf6954SWhindmar Saksit UINT succ = ShellExecuteExW(&sei), gle = GetLastError();
699f6cf6954SWhindmar Saksit SHFree(pidl);
700f6cf6954SWhindmar Saksit if (!succ)
701f6cf6954SWhindmar Saksit return ErrMsg(gle);
702f6cf6954SWhindmar Saksit Wait();
703f6cf6954SWhindmar Saksit return 0;
704f6cf6954SWhindmar Saksit }
705f6cf6954SWhindmar Saksit else if (isCmdWithArg(argc, argv, n, L"dumpmenu", arg))
706f6cf6954SWhindmar Saksit {
707f6cf6954SWhindmar Saksit HRESULT hr;
708f6cf6954SWhindmar Saksit CComPtr<IContextMenu> cm;
709f6cf6954SWhindmar Saksit if (CLSIDPrefix(arg, g_CLSID))
710f6cf6954SWhindmar Saksit {
711f6cf6954SWhindmar Saksit g_ShellExtInit = arg;
712f6cf6954SWhindmar Saksit hr = LoadAndInitialize(IID_PPV_ARG(IContextMenu, &cm));
713f6cf6954SWhindmar Saksit }
714f6cf6954SWhindmar Saksit else
715f6cf6954SWhindmar Saksit {
716f6cf6954SWhindmar Saksit CComPtr<IShellItem> si;
717f6cf6954SWhindmar Saksit hr = CreateShellItemFromParse(arg, &si);
718f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
719f6cf6954SWhindmar Saksit hr = si->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IContextMenu, &cm));
720f6cf6954SWhindmar Saksit }
721f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
722f6cf6954SWhindmar Saksit {
723f6cf6954SWhindmar Saksit UINT first = 10, last = 9000;
724f6cf6954SWhindmar Saksit UINT cmf = 0, nosub = 0, fakeinit = 0;
725f6cf6954SWhindmar Saksit while (++n < argc)
726f6cf6954SWhindmar Saksit {
727f6cf6954SWhindmar Saksit if (argv[n][0] != '-' && argv[n][0] != '/')
728f6cf6954SWhindmar Saksit break;
729f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"DEFAULTONLY"))
730f6cf6954SWhindmar Saksit cmf |= CMF_DEFAULTONLY;
731f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"NODEFAULT"))
732f6cf6954SWhindmar Saksit cmf |= CMF_NODEFAULT;
733f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"DONOTPICKDEFAULT"))
734f6cf6954SWhindmar Saksit cmf |= CMF_DONOTPICKDEFAULT;
735f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"EXTENDED") || isCmd(argc, argv, n, L"EXTENDEDVERBS"))
736f6cf6954SWhindmar Saksit cmf |= CMF_EXTENDEDVERBS;
737f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"SYNCCASCADEMENU"))
738f6cf6954SWhindmar Saksit cmf |= CMF_SYNCCASCADEMENU;
739f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"EXPLORE"))
740f6cf6954SWhindmar Saksit cmf |= CMF_EXPLORE;
741f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"VERBSONLY"))
742f6cf6954SWhindmar Saksit cmf |= CMF_VERBSONLY;
743f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"NOVERBS"))
744f6cf6954SWhindmar Saksit cmf |= CMF_NOVERBS;
745f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"DISABLEDVERBS"))
746f6cf6954SWhindmar Saksit cmf |= CMF_DISABLEDVERBS;
747f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"OPTIMIZEFORINVOKE"))
748f6cf6954SWhindmar Saksit cmf |= CMF_OPTIMIZEFORINVOKE;
749f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"CANRENAME"))
750f6cf6954SWhindmar Saksit cmf |= CMF_CANRENAME;
751f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"NOSUBMENU"))
752f6cf6954SWhindmar Saksit nosub++;
753f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"INITMENUPOPUP"))
754f6cf6954SWhindmar Saksit fakeinit++; // Tickle async submenus
755f6cf6954SWhindmar Saksit else
756f6cf6954SWhindmar Saksit wprintf(L"WARN: Ignoring switch %s\n", argv[n]);
757f6cf6954SWhindmar Saksit }
758f6cf6954SWhindmar Saksit HMENU hMenu = CreatePopupMenu();
759f6cf6954SWhindmar Saksit hr = cm->QueryContextMenu(hMenu, 0, first, last, cmf);
760f6cf6954SWhindmar Saksit if (SUCCEEDED(hr))
761f6cf6954SWhindmar Saksit {
762f6cf6954SWhindmar Saksit DumpMenu(hMenu, first, cm, fakeinit, nosub ? -1 : 0);
763f6cf6954SWhindmar Saksit }
764f6cf6954SWhindmar Saksit DestroyMenu(hMenu);
765f6cf6954SWhindmar Saksit }
766f6cf6954SWhindmar Saksit if (FAILED(hr))
767f6cf6954SWhindmar Saksit return ErrMsg(hr);
768f6cf6954SWhindmar Saksit return 0;
769f6cf6954SWhindmar Saksit }
770f6cf6954SWhindmar Saksit else if (isCmdWithArg(argc, argv, n, L"clsid", arg))
771cb885480SMark Jansen {
772cb885480SMark Jansen HRESULT hr = CLSIDFromString(arg, &g_CLSID);
773cb885480SMark Jansen if (!SUCCEEDED(hr))
774cb885480SMark Jansen {
775cb885480SMark Jansen wprintf(L"Failed to convert %s to CLSID\n", arg);
776cb885480SMark Jansen failArgs = true;
777cb885480SMark Jansen }
778cb885480SMark Jansen }
779cb885480SMark Jansen else if (isCmdWithArg(argc, argv, n, L"dll", arg))
780cb885480SMark Jansen {
781cb885480SMark Jansen g_DLL = arg;
782cb885480SMark Jansen }
783cb885480SMark Jansen else if (isCmdWithArg(argc, argv, n, L"IShellExtInit", arg))
784cb885480SMark Jansen {
785cb885480SMark Jansen g_ShellExtInit = arg;
786cb885480SMark Jansen }
787cb885480SMark Jansen else if (isCmd(argc, argv, n, L"IShellPropSheetExt"))
788cb885480SMark Jansen {
789cb885480SMark Jansen g_bIShellPropSheetExt = true;
790cb885480SMark Jansen }
791cb885480SMark Jansen else if (isCmdWithArg(argc, argv, n, L"IContextMenu", arg))
792cb885480SMark Jansen {
793cb885480SMark Jansen g_ContextMenu = arg;
794cb885480SMark Jansen }
795cb885480SMark Jansen else if (isCmd(argc, argv, n, L"infinite"))
796cb885480SMark Jansen {
797cb885480SMark Jansen g_Wait = Wait_Infinite;
798cb885480SMark Jansen }
799cb885480SMark Jansen else if (isCmd(argc, argv, n, L"openwindows"))
800cb885480SMark Jansen {
801cb885480SMark Jansen g_Wait = Wait_OpenWindows;
802cb885480SMark Jansen }
803cb885480SMark Jansen else if (isCmd(argc, argv, n, L"input"))
804cb885480SMark Jansen {
805cb885480SMark Jansen g_Wait = Wait_Input;
806cb885480SMark Jansen }
807f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"explorerinstance"))
808f6cf6954SWhindmar Saksit {
809f6cf6954SWhindmar Saksit g_Wait = Wait_ExplorerInstance;
810f6cf6954SWhindmar Saksit }
811f6cf6954SWhindmar Saksit else if (isCmd(argc, argv, n, L"nowait"))
812f6cf6954SWhindmar Saksit {
813f6cf6954SWhindmar Saksit g_Wait = Wait_None;
814f6cf6954SWhindmar Saksit }
815cb885480SMark Jansen else
816cb885480SMark Jansen {
817cb885480SMark Jansen wprintf(L"Unknown argument: %s\n", cmd);
818cb885480SMark Jansen failArgs = true;
819cb885480SMark Jansen }
820cb885480SMark Jansen }
821cb885480SMark Jansen }
822cb885480SMark Jansen
823cb885480SMark Jansen if (failArgs)
824cb885480SMark Jansen {
825cb885480SMark Jansen PrintHelp(NULL);
826cb885480SMark Jansen return E_INVALIDARG;
827cb885480SMark Jansen }
828cb885480SMark Jansen
829cb885480SMark Jansen CLSID EmptyCLSID = { 0 };
830cb885480SMark Jansen if (EmptyCLSID == g_CLSID)
831cb885480SMark Jansen {
832cb885480SMark Jansen PrintHelp(L"No CLSID specified");
833cb885480SMark Jansen return E_INVALIDARG;
834cb885480SMark Jansen }
835cb885480SMark Jansen
836cb885480SMark Jansen if (g_ShellExtInit.IsEmpty())
837cb885480SMark Jansen {
838cb885480SMark Jansen PrintHelp(L"No filename specified");
839cb885480SMark Jansen return E_INVALIDARG;
840cb885480SMark Jansen }
841cb885480SMark Jansen
842cb885480SMark Jansen HRESULT hr;
843cb885480SMark Jansen if (g_bIShellPropSheetExt)
844cb885480SMark Jansen {
845cb885480SMark Jansen CComPtr<IShellPropSheetExt> spSheetExt;
846cb885480SMark Jansen hr = LoadAndInitialize(IID_PPV_ARG(IShellPropSheetExt, &spSheetExt));
847cb885480SMark Jansen if (!SUCCEEDED(hr))
848cb885480SMark Jansen return hr;
849cb885480SMark Jansen
850cb885480SMark Jansen hr = spSheetExt->AddPages(cb_AddPage, (LPARAM)&g_Pages);
851cb885480SMark Jansen if (!SUCCEEDED(hr))
852cb885480SMark Jansen {
853cb885480SMark Jansen wprintf(L"IShellPropSheetExt->AddPages failed: 0x%x\n", hr);
854cb885480SMark Jansen return hr;
855cb885480SMark Jansen }
856cb885480SMark Jansen
857cb885480SMark Jansen USHORT ActivePage = HRESULT_CODE(hr);
858cb885480SMark Jansen PROPSHEETHEADERW psh = { 0 };
859cb885480SMark Jansen
860cb885480SMark Jansen psh.dwSize = sizeof(psh);
861cb885480SMark Jansen psh.dwFlags = PSH_PROPTITLE;
862cb885480SMark Jansen psh.pszCaption = L"shlextdbg";
863cb885480SMark Jansen psh.phpage = g_Pages.GetData();
864cb885480SMark Jansen psh.nPages = g_Pages.GetSize();
865cb885480SMark Jansen psh.nStartPage = ActivePage ? (ActivePage-1) : 0;
866cb885480SMark Jansen hr = PropertySheetW(&psh);
867cb885480SMark Jansen
868cb885480SMark Jansen wprintf(L"PropertySheetW returned: 0x%x\n", hr);
869cb885480SMark Jansen }
870cb885480SMark Jansen if (!g_ContextMenu.IsEmpty())
871cb885480SMark Jansen {
872cb885480SMark Jansen CComPtr<IContextMenu> spContextMenu;
873cb885480SMark Jansen hr = LoadAndInitialize(IID_PPV_ARG(IContextMenu, &spContextMenu));
874cb885480SMark Jansen if (!SUCCEEDED(hr))
875cb885480SMark Jansen return hr;
876cb885480SMark Jansen
877f6cf6954SWhindmar Saksit // FIXME: Must call QueryContextMenu before InvokeCommand?
878f6cf6954SWhindmar Saksit
879cb885480SMark Jansen CMINVOKECOMMANDINFO cm = { sizeof(cm), 0 };
880cb885480SMark Jansen cm.lpVerb = g_ContextMenu.GetString();
881cb885480SMark Jansen cm.nShow = SW_SHOW;
882cb885480SMark Jansen hr = spContextMenu->InvokeCommand(&cm);
883cb885480SMark Jansen
884cb885480SMark Jansen if (!SUCCEEDED(hr))
885cb885480SMark Jansen {
886cb885480SMark Jansen wprintf(L"IContextMenu->InvokeCommand failed: 0x%x\n", hr);
887cb885480SMark Jansen return hr;
888cb885480SMark Jansen }
889cb885480SMark Jansen wprintf(L"IContextMenu->InvokeCommand returned: 0x%x\n", hr);
890cb885480SMark Jansen }
891cb885480SMark Jansen
892f6cf6954SWhindmar Saksit Wait();
893cb885480SMark Jansen return 0;
894cb885480SMark Jansen }
895