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