ReactOS  0.4.14-dev-614-gbfd8a84
shlextdbg.cpp
Go to the documentation of this file.
1 /*
2  * PROJECT: shlextdbg
3  * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE: Shell extension debug utility
5  * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include <windows.h>
9 #include <shlobj.h>
10 #include <atlbase.h> // thanks gcc
11 #include <atlcom.h> // thanks gcc
12 #include <atlstr.h>
13 #include <atlsimpcoll.h>
14 #include <conio.h>
15 #include <shellutils.h>
16 
18 {
23 };
24 
25 CLSID g_CLSID = { 0 };
28 bool g_bIShellPropSheetExt = false;
31 
33 {
35  if (!SUCCEEDED(hr))
36  {
37  wprintf(L"Failed to create pidl from '%s': 0x%x\n", FileName, hr);
38  return hr;
39  }
40 
41  CComPtr<IShellFolder> shellFolder;
42  PCUITEMID_CHILD childs;
43  hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &childs);
44  if (!SUCCEEDED(hr))
45  {
46  wprintf(L"Failed to bind to parent: 0x%x\n", hr);
47  return hr;
48  }
49  hr = shellFolder->GetUIObjectOf(NULL, 1, &childs, IID_IDataObject, NULL, (PVOID*)&dataObject);
50  if (!SUCCEEDED(hr))
51  {
52  wprintf(L"Failed to query IDataObject: 0x%x\n", hr);
53  }
54  return hr;
55 }
56 
58 {
59  CComPtr<IShellExtInit> spShellExtInit;
60  HRESULT hr;
61  if (g_DLL.IsEmpty())
62  {
63  hr = CoCreateInstance(g_CLSID, NULL, CLSCTX_ALL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
64  if (!SUCCEEDED(hr))
65  {
66  WCHAR Buffer[100];
68  wprintf(L"Failed to Create %s:IShellExtInit: 0x%x\n", Buffer, hr);
69  return hr;
70  }
71  }
72  else
73  {
74  typedef HRESULT (STDAPICALLTYPE *tDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
76  if (!mod)
77  {
78  wprintf(L"Failed to Load %s:(0x%x)\n", g_DLL.GetString(), GetLastError());
79  return E_FAIL;
80  }
81  tDllGetClassObject DllGet = (tDllGetClassObject)GetProcAddress(mod, "DllGetClassObject");
82  if (!DllGet)
83  {
84  wprintf(L"%s does not export DllGetClassObject\n", g_DLL.GetString());
85  return E_FAIL;
86  }
87  CComPtr<IClassFactory> spClassFactory;
88  hr = DllGet(g_CLSID, IID_PPV_ARG(IClassFactory, &spClassFactory));
89  if (!SUCCEEDED(hr))
90  {
91  wprintf(L"Failed to create IClassFactory: 0x%x\n", hr);
92  return hr;
93  }
94  hr = spClassFactory->CreateInstance(NULL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
95  if (!SUCCEEDED(hr))
96  {
97  wprintf(L"Failed to Request IShellExtInit from IClassFactory: 0x%x\n", hr);
98  return hr;
99  }
100  }
101 
102  CComPtr<IDataObject> spDataObject;
104  hr = CreateIDataObject(pidl, spDataObject, g_ShellExtInit.GetString());
105  if (!SUCCEEDED(hr))
106  return hr;
107 
108  hr = spShellExtInit->Initialize(pidl, spDataObject, NULL);
109  if (!SUCCEEDED(hr))
110  {
111  wprintf(L"IShellExtInit->Initialize failed: 0x%x\n", hr);
112  return hr;
113  }
114  hr = spShellExtInit->QueryInterface(riid, ppv);
115  if (!SUCCEEDED(hr))
116  {
117  WCHAR Buffer[100];
119  wprintf(L"Failed to query %s from IShellExtInit: 0x%x\n", Buffer, hr);
120  }
121  return hr;
122 }
123 
124 
128 {
129  if (hwnd != g_ConsoleWindow)
130  {
131  DWORD pid = 0;
133  if (pid == GetCurrentProcessId())
134  {
135  g_Windows.Add(hwnd);
136  }
137  }
138  return TRUE;
139 }
140 
142 {
143  /* Give the windows some time to spawn */
144  Sleep(2000);
146  while (true)
147  {
148  g_Windows.RemoveAll();
150  if (g_Windows.GetSize() == 0)
151  break;
152  Sleep(500);
153  }
154  wprintf(L"All windows closed (ignoring console window)\n");
155 }
156 
157 
158 
161 {
162  g_Pages.Add(page);
163  if (lParam != (LPARAM)&g_Pages)
164  {
165  wprintf(L"Propsheet failed to pass lParam, got: 0x%Ix\n", lParam);
166  }
167  return TRUE;
168 }
169 
170 static bool isCmdWithArg(int argc, WCHAR** argv, int& n, PCWSTR check, PCWSTR &arg)
171 {
172  arg = NULL;
173  size_t len = wcslen(check);
174  if (!_wcsnicmp(argv[n] + 1, check, len))
175  {
176  PCWSTR cmd = argv[n] + len + 1;
177  if (*cmd == ':' || *cmd == '=')
178  {
179  arg = cmd + 1;
180  return true;
181  }
182  if (n + 1 < argc)
183  {
184  arg = argv[n+1];
185  n++;
186  return true;
187  }
188  wprintf(L"Command %s has no required argument!\n", check);
189  return false;
190  }
191  return false;
192 }
193 
194 static bool isCmd(int argc, WCHAR** argv, int n, PCWSTR check)
195 {
196  return !wcsicmp(argv[n] + 1, check);
197 }
198 
199 static void PrintHelp(PCWSTR ExtraLine)
200 {
201  if (ExtraLine)
202  wprintf(L"%s\n", ExtraLine);
203 
204  wprintf(L"shlextdbg /clsid={clsid} [/dll=dllname] /IShellExtInit=filename |shlextype| |waitoptions|\n");
205  wprintf(L" {clsid}: The CLSID or ProgID of the object to create\n");
206  wprintf(L" dll: Optional dllname to create the object from, instead of CoCreateInstance\n");
207  wprintf(L" filename: The filename to pass to IShellExtInit->Initialze\n");
208  wprintf(L" shlextype: The type of shell extention to run:\n");
209  wprintf(L" /IShellPropSheetExt to create a property sheet\n");
210  wprintf(L" /IContextMenu=verb to activate the specified verb\n");
211  wprintf(L" waitoptions: Specify how to wait:\n");
212  wprintf(L" /infinite: Keep on waiting infinitely\n");
213  wprintf(L" /openwindows: Wait for all windows from the current application to close\n");
214  wprintf(L" /input: Wait for input\n");
215  wprintf(L"\n");
216 }
217 
218 /*
219 Examples:
220 
221 /clsid={513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8} /IShellExtInit=C:\RosBE\Uninstall.exe /IShellPropSheetExt
222 /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows
223 /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows /dll=R:\build\dev\devenv\dll\shellext\zipfldr\Debug\zipfldr.dll
224 
225 */
226 extern "C" // and another hack for gcc
227 int wmain(int argc, WCHAR **argv)
228 {
229  bool failArgs = false;
230  for (int n = 1; n < argc; ++n)
231  {
232  WCHAR* cmd = argv[n];
233  if (cmd[0] == '-' || cmd[0] == '/')
234  {
235  PCWSTR arg;
236  if (isCmdWithArg(argc, argv, n, L"clsid", arg))
237  {
239  if (!SUCCEEDED(hr))
240  {
241  wprintf(L"Failed to convert %s to CLSID\n", arg);
242  failArgs = true;
243  }
244  }
245  else if (isCmdWithArg(argc, argv, n, L"dll", arg))
246  {
247  g_DLL = arg;
248  }
249  else if (isCmdWithArg(argc, argv, n, L"IShellExtInit", arg))
250  {
252  }
253  else if (isCmd(argc, argv, n, L"IShellPropSheetExt"))
254  {
255  g_bIShellPropSheetExt = true;
256  }
257  else if (isCmdWithArg(argc, argv, n, L"IContextMenu", arg))
258  {
259  g_ContextMenu = arg;
260  }
261  else if (isCmd(argc, argv, n, L"infinite"))
262  {
264  }
265  else if (isCmd(argc, argv, n, L"openwindows"))
266  {
268  }
269  else if (isCmd(argc, argv, n, L"input"))
270  {
271  g_Wait = Wait_Input;
272  }
273  else
274  {
275  wprintf(L"Unknown argument: %s\n", cmd);
276  failArgs = true;
277  }
278  }
279  }
280 
281  if (failArgs)
282  {
283  PrintHelp(NULL);
284  return E_INVALIDARG;
285  }
286 
287 
288  CLSID EmptyCLSID = { 0 };
289  if (EmptyCLSID == g_CLSID)
290  {
291  PrintHelp(L"No CLSID specified");
292  return E_INVALIDARG;
293  }
294 
295  if (g_ShellExtInit.IsEmpty())
296  {
297  PrintHelp(L"No filename specified");
298  return E_INVALIDARG;
299  }
300 
302  InitCommonControlsEx(&icc);
304 
305  HRESULT hr;
307  {
308  CComPtr<IShellPropSheetExt> spSheetExt;
310  if (!SUCCEEDED(hr))
311  return hr;
312 
313  hr = spSheetExt->AddPages(cb_AddPage, (LPARAM)&g_Pages);
314  if (!SUCCEEDED(hr))
315  {
316  wprintf(L"IShellPropSheetExt->AddPages failed: 0x%x\n", hr);
317  return hr;
318  }
319 
320  USHORT ActivePage = HRESULT_CODE(hr);
321  PROPSHEETHEADERW psh = { 0 };
322 
323  psh.dwSize = sizeof(psh);
324  psh.dwFlags = PSH_PROPTITLE;
325  psh.pszCaption = L"shlextdbg";
326  psh.phpage = g_Pages.GetData();
327  psh.nPages = g_Pages.GetSize();
328  psh.nStartPage = ActivePage ? (ActivePage-1) : 0;
329  hr = PropertySheetW(&psh);
330 
331  wprintf(L"PropertySheetW returned: 0x%x\n", hr);
332  }
333  if (!g_ContextMenu.IsEmpty())
334  {
335  CComPtr<IContextMenu> spContextMenu;
336  hr = LoadAndInitialize(IID_PPV_ARG(IContextMenu, &spContextMenu));
337  if (!SUCCEEDED(hr))
338  return hr;
339 
340  CMINVOKECOMMANDINFO cm = { sizeof(cm), 0 };
341  cm.lpVerb = g_ContextMenu.GetString();
342  cm.nShow = SW_SHOW;
343  hr = spContextMenu->InvokeCommand(&cm);
344 
345  if (!SUCCEEDED(hr))
346  {
347  wprintf(L"IContextMenu->InvokeCommand failed: 0x%x\n", hr);
348  return hr;
349  }
350  wprintf(L"IContextMenu->InvokeCommand returned: 0x%x\n", hr);
351  }
352 
353  switch (g_Wait)
354  {
355  case Wait_None:
356  break;
357  case Wait_Infinite:
358  while (true) {
359  Sleep(1000);
360  }
361  break;
362  case Wait_OpenWindows:
363  WaitWindows();
364  break;
365  case Wait_Input:
366  wprintf(L"Press any key to continue...\n");
367  _getch();
368  break;
369 
370  }
371  return 0;
372 }
const DOCKBAR PVOID HWND HWND * hwnd
Definition: tooldock.h:22
#define HRESULT
Definition: msvc.h:7
static int argc
Definition: ServiceArgs.c:12
const uint16_t * PCWSTR
Definition: typedefs.h:55
_In_ ULONG_PTR _In_ ULONG _Out_ ULONG_PTR * pid
Definition: winddi.h:3835
#define REFIID
Definition: guiddef.h:118
#define TRUE
Definition: types.h:120
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:790
HRESULT hr
Definition: shlfolder.c:183
CLSID g_CLSID
Definition: shlextdbg.cpp:25
Definition: ftp_var.h:139
REFIID riid
Definition: precomp.h:44
#define REFCLSID
Definition: guiddef.h:117
static bool isCmd(int argc, WCHAR **argv, int n, PCWSTR check)
Definition: shlextdbg.cpp:194
static bool isCmdWithArg(int argc, WCHAR **argv, int &n, PCWSTR check, PCWSTR &arg)
Definition: shlextdbg.cpp:170
#define _countof(array)
Definition: fontsub.cpp:30
#define CALLBACK
Definition: compat.h:27
_Check_return_ _CRTIMP int __cdecl _wcsnicmp(_In_reads_or_z_(_MaxCount) const wchar_t *_Str1, _In_reads_or_z_(_MaxCount) const wchar_t *_Str2, _In_ size_t _MaxCount)
REFIID LPVOID * ppv
Definition: atlbase.h:39
GLdouble n
Definition: glext.h:7729
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
Definition: shlextdbg.cpp:127
const ITEMID_CHILD UNALIGNED * PCUITEMID_CHILD
Definition: shtypes.idl:70
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
const GUID IID_IDataObject
static void PrintHelp(PCWSTR ExtraLine)
Definition: shlextdbg.cpp:199
void * arg
Definition: msvc.h:10
#define wprintf(...)
Definition: whoami.c:18
#define argv
Definition: mplay32.c:18
#define IID_PPV_ARG(Itype, ppType)
#define E_FAIL
Definition: ddrawi.h:102
CSimpleArray< HWND > g_Windows
Definition: shlextdbg.cpp:125
bool g_bIShellPropSheetExt
Definition: shlextdbg.cpp:28
HRESULT LoadAndInitialize(REFIID riid, LPVOID *ppv)
Definition: shlextdbg.cpp:57
LPCWSTR pszCaption
Definition: prsht.h:283
unsigned int BOOL
Definition: ntddk_ex.h:94
struct _PSP * HPROPSHEETPAGE
Definition: mstask.idl:90
CStringW g_ShellExtInit
Definition: shlextdbg.cpp:27
DWORD WINAPI GetWindowThreadProcessId(HWND, PDWORD)
#define E_INVALIDARG
Definition: ddrawi.h:101
#define LoadLibraryW(x)
Definition: compat.h:412
smooth NULL
Definition: ftsmooth.c:416
void WaitWindows()
Definition: shlextdbg.cpp:141
LONG_PTR LPARAM
Definition: windef.h:208
Definition: module.h:566
Definition: bufpool.h:45
INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
Definition: propsheet.c:2905
#define SW_SHOW
Definition: winuser.h:769
WaitType
Definition: shlextdbg.cpp:17
HWND g_ConsoleWindow
Definition: shlextdbg.cpp:126
static BOOL CALLBACK cb_AddPage(HPROPSHEETPAGE page, LPARAM lParam)
Definition: shlextdbg.cpp:160
CSimpleArray< HPROPSHEETPAGE > g_Pages
Definition: shlextdbg.cpp:159
HRESULT CreateIDataObject(CComHeapPtr< ITEMIDLIST > &pidl, CComPtr< IDataObject > &dataObject, PCWSTR FileName)
Definition: shlextdbg.cpp:32
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define PSH_PROPTITLE
Definition: prsht.h:40
LONG HRESULT
Definition: typedefs.h:77
INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
Definition: compobj.c:2434
PCXSTR GetString() const
Definition: atlsimpstr.h:361
CStringW g_DLL
Definition: shlextdbg.cpp:26
BOOL WINAPI EnumWindows(_In_ WNDENUMPROC, _In_ LPARAM)
unsigned long DWORD
Definition: ntddk_ex.h:95
DWORD dwSize
Definition: prsht.h:275
void check(CONTEXT *pContext)
Definition: NtContinue.c:61
int wmain(int argc, WCHAR **argv)
Definition: shlextdbg.cpp:227
DWORD dwFlags
Definition: prsht.h:276
static const WCHAR L[]
Definition: oid.c:1250
BOOL WINAPI InitCommonControlsEx(const INITCOMMONCONTROLSEX *lpInitCtrls)
Definition: commctrl.c:893
#define STDAPICALLTYPE
Definition: guid.c:3
GLenum GLsizei len
Definition: glext.h:6722
#define wcsicmp
Definition: string.h:1152
HPROPSHEETPAGE * phpage
Definition: prsht.h:291
HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID iid, LPVOID *ppv)
Definition: compobj.c:3325
bool IsEmpty() const
Definition: atlsimpstr.h:379
WaitType g_Wait
Definition: shlextdbg.cpp:30
HRESULT WINAPI SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCITEMIDLIST *ppidlLast)
Definition: pidl.c:1323
unsigned short USHORT
Definition: pedump.c:61
CStringA g_ContextMenu
Definition: shlextdbg.cpp:29
HRESULT WINAPI SHParseDisplayName(LPCWSTR pszName, IBindCtx *pbc, LPITEMIDLIST *ppidl, SFGAOF sfgaoIn, SFGAOF *psfgaoOut)
Definition: pidl.c:1367
HRESULT WINAPI CoInitialize(LPVOID lpReserved)
Definition: compobj.c:1964
#define HRESULT_CODE(hr)
Definition: winerror.h:76
HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id)
Definition: compobj.c:2338
#define GetProcAddress(x, y)
Definition: compat.h:418
#define ICC_STANDARD_CLASSES
Definition: commctrl.h:73
int _getch()
Definition: getch.c:16
LPARAM lParam
Definition: combotst.c:139
size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
#define ICC_LINK_CLASS
Definition: commctrl.h:74
DWORD WINAPI GetCurrentProcessId(VOID)
Definition: proc.c:1158
#define SUCCEEDED(hr)
Definition: intsafe.h:57
HWND WINAPI DECLSPEC_HOTPATCH GetConsoleWindow(VOID)
Definition: console.c:2730
static int mod
Definition: i386-dis.c:1273
UINT nStartPage
Definition: prsht.h:286