ReactOS  0.4.13-dev-100-gc8611ae
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  // hack for gcc:
16 #define DbgPrint(...) printf(__VA_ARGS__)
17 #include <shellutils.h>
18 
20 {
25 };
26 
27 CLSID g_CLSID = { 0 };
30 bool g_bIShellPropSheetExt = false;
33 
35 {
37  if (!SUCCEEDED(hr))
38  {
39  wprintf(L"Failed to create pidl from '%s': 0x%x\n", FileName, hr);
40  return hr;
41  }
42 
43  CComPtr<IShellFolder> shellFolder;
44  PCUITEMID_CHILD childs;
45  hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &childs);
46  if (!SUCCEEDED(hr))
47  {
48  wprintf(L"Failed to bind to parent: 0x%x\n", hr);
49  return hr;
50  }
51  hr = shellFolder->GetUIObjectOf(NULL, 1, &childs, IID_IDataObject, NULL, (PVOID*)&dataObject);
52  if (!SUCCEEDED(hr))
53  {
54  wprintf(L"Failed to query IDataObject: 0x%x\n", hr);
55  }
56  return hr;
57 }
58 
60 {
61  CComPtr<IShellExtInit> spShellExtInit;
62  HRESULT hr;
63  if (g_DLL.IsEmpty())
64  {
65  hr = CoCreateInstance(g_CLSID, NULL, CLSCTX_ALL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
66  if (!SUCCEEDED(hr))
67  {
68  WCHAR Buffer[100];
70  wprintf(L"Failed to Create %s:IShellExtInit: 0x%x\n", Buffer, hr);
71  return hr;
72  }
73  }
74  else
75  {
76  typedef HRESULT (STDAPICALLTYPE *tDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
78  if (!mod)
79  {
80  wprintf(L"Failed to Load %s:(0x%x)\n", g_DLL.GetString(), GetLastError());
81  return E_FAIL;
82  }
83  tDllGetClassObject DllGet = (tDllGetClassObject)GetProcAddress(mod, "DllGetClassObject");
84  if (!DllGet)
85  {
86  wprintf(L"%s does not export DllGetClassObject\n", g_DLL.GetString());
87  return E_FAIL;
88  }
89  CComPtr<IClassFactory> spClassFactory;
90  hr = DllGet(g_CLSID, IID_PPV_ARG(IClassFactory, &spClassFactory));
91  if (!SUCCEEDED(hr))
92  {
93  wprintf(L"Failed to create IClassFactory: 0x%x\n", hr);
94  return hr;
95  }
96  hr = spClassFactory->CreateInstance(NULL, IID_PPV_ARG(IShellExtInit, &spShellExtInit));
97  if (!SUCCEEDED(hr))
98  {
99  wprintf(L"Failed to Request IShellExtInit from IClassFactory: 0x%x\n", hr);
100  return hr;
101  }
102  }
103 
104  CComPtr<IDataObject> spDataObject;
106  hr = CreateIDataObject(pidl, spDataObject, g_ShellExtInit.GetString());
107  if (!SUCCEEDED(hr))
108  return hr;
109 
110  hr = spShellExtInit->Initialize(pidl, spDataObject, NULL);
111  if (!SUCCEEDED(hr))
112  {
113  wprintf(L"IShellExtInit->Initialize failed: 0x%x\n", hr);
114  return hr;
115  }
116  hr = spShellExtInit->QueryInterface(riid, ppv);
117  if (!SUCCEEDED(hr))
118  {
119  WCHAR Buffer[100];
121  wprintf(L"Failed to query %s from IShellExtInit: 0x%x\n", Buffer, hr);
122  }
123  return hr;
124 }
125 
126 
130 {
131  if (hwnd != g_ConsoleWindow)
132  {
133  DWORD pid = 0;
135  if (pid == GetCurrentProcessId())
136  {
137  g_Windows.Add(hwnd);
138  }
139  }
140  return TRUE;
141 }
142 
144 {
145  /* Give the windows some time to spawn */
146  Sleep(2000);
148  while (true)
149  {
150  g_Windows.RemoveAll();
152  if (g_Windows.GetSize() == 0)
153  break;
154  Sleep(500);
155  }
156  wprintf(L"All windows closed (ignoring console window)\n");
157 }
158 
159 
160 
163 {
164  g_Pages.Add(page);
165  if (lParam != (LPARAM)&g_Pages)
166  {
167  wprintf(L"Propsheet failed to pass lParam, got: 0x%x\n", lParam);
168  }
169  return TRUE;
170 }
171 
172 static bool isCmdWithArg(int argc, WCHAR** argv, int& n, PCWSTR check, PCWSTR &arg)
173 {
174  arg = NULL;
175  size_t len = wcslen(check);
176  if (!_wcsnicmp(argv[n] + 1, check, len))
177  {
178  PCWSTR cmd = argv[n] + len + 1;
179  if (*cmd == ':' || *cmd == '=')
180  {
181  arg = cmd + 1;
182  return true;
183  }
184  if (n + 1 < argc)
185  {
186  arg = argv[n+1];
187  n++;
188  return true;
189  }
190  wprintf(L"Command %s has no required argument!\n", check);
191  return false;
192  }
193  return false;
194 }
195 
196 static bool isCmd(int argc, WCHAR** argv, int n, PCWSTR check)
197 {
198  return !wcsicmp(argv[n] + 1, check);
199 }
200 
201 static void PrintHelp(PCWSTR ExtraLine)
202 {
203  if (ExtraLine)
204  wprintf(L"%s\n", ExtraLine);
205 
206  wprintf(L"shlextdbg /clsid={clsid} [/dll=dllname] /IShellExtInit=filename |shlextype| |waitoptions|\n");
207  wprintf(L" {clsid}: The CLSID or ProgID of the object to create\n");
208  wprintf(L" dll: Optional dllname to create the object from, instead of CoCreateInstance\n");
209  wprintf(L" filename: The filename to pass to IShellExtInit->Initialze\n");
210  wprintf(L" shlextype: The type of shell extention to run:\n");
211  wprintf(L" /IShellPropSheetExt to create a property sheet\n");
212  wprintf(L" /IContextMenu=verb to activate the specified verb\n");
213  wprintf(L" waitoptions: Specify how to wait:\n");
214  wprintf(L" /infinite: Keep on waiting infinitely\n");
215  wprintf(L" /openwindows: Wait for all windows from the current application to close\n");
216  wprintf(L" /input: Wait for input\n");
217  wprintf(L"\n");
218 }
219 
220 /*
221 Examples:
222 
223 /clsid={513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8} /IShellExtInit=C:\RosBE\Uninstall.exe /IShellPropSheetExt
224 /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows
225 /clsid=CompressedFolder /IShellExtInit=e:\test.zip /IContextMenu=extract /openwindows /dll=R:\build\dev\devenv\dll\shellext\zipfldr\Debug\zipfldr.dll
226 
227 */
228 extern "C" // and another hack for gcc
229 int wmain(int argc, WCHAR **argv)
230 {
231  bool failArgs = false;
232  for (int n = 1; n < argc; ++n)
233  {
234  WCHAR* cmd = argv[n];
235  if (cmd[0] == '-' || cmd[0] == '/')
236  {
237  PCWSTR arg;
238  if (isCmdWithArg(argc, argv, n, L"clsid", arg))
239  {
241  if (!SUCCEEDED(hr))
242  {
243  wprintf(L"Failed to convert %s to CLSID\n", arg);
244  failArgs = true;
245  }
246  }
247  else if (isCmdWithArg(argc, argv, n, L"dll", arg))
248  {
249  g_DLL = arg;
250  }
251  else if (isCmdWithArg(argc, argv, n, L"IShellExtInit", arg))
252  {
254  }
255  else if (isCmd(argc, argv, n, L"IShellPropSheetExt"))
256  {
257  g_bIShellPropSheetExt = true;
258  }
259  else if (isCmdWithArg(argc, argv, n, L"IContextMenu", arg))
260  {
261  g_ContextMenu = arg;
262  }
263  else if (isCmd(argc, argv, n, L"infinite"))
264  {
266  }
267  else if (isCmd(argc, argv, n, L"openwindows"))
268  {
270  }
271  else if (isCmd(argc, argv, n, L"input"))
272  {
273  g_Wait = Wait_Input;
274  }
275  else
276  {
277  wprintf(L"Unknown argument: %s\n", cmd);
278  failArgs = true;
279  }
280  }
281  }
282 
283  if (failArgs)
284  {
285  PrintHelp(NULL);
286  return E_INVALIDARG;
287  }
288 
289 
290  CLSID EmptyCLSID = { 0 };
291  if (EmptyCLSID == g_CLSID)
292  {
293  PrintHelp(L"No CLSID specified");
294  return E_INVALIDARG;
295  }
296 
297  if (g_ShellExtInit.IsEmpty())
298  {
299  PrintHelp(L"No filename specified");
300  return E_INVALIDARG;
301  }
302 
304  InitCommonControlsEx(&icc);
306 
307  HRESULT hr;
309  {
310  CComPtr<IShellPropSheetExt> spSheetExt;
312  if (!SUCCEEDED(hr))
313  return hr;
314 
315  hr = spSheetExt->AddPages(cb_AddPage, (LPARAM)&g_Pages);
316  if (!SUCCEEDED(hr))
317  {
318  wprintf(L"IShellPropSheetExt->AddPages failed: 0x%x\n", hr);
319  return hr;
320  }
321 
322  USHORT ActivePage = HRESULT_CODE(hr);
323  PROPSHEETHEADERW psh = { 0 };
324 
325  psh.dwSize = sizeof(psh);
326  psh.dwFlags = PSH_PROPTITLE;
327  psh.pszCaption = L"shlextdbg";
328  psh.phpage = g_Pages.GetData();
329  psh.nPages = g_Pages.GetSize();
330  psh.nStartPage = ActivePage ? (ActivePage-1) : 0;
331  hr = PropertySheetW(&psh);
332 
333  wprintf(L"PropertySheetW returned: 0x%x\n", hr);
334  }
335  if (!g_ContextMenu.IsEmpty())
336  {
337  CComPtr<IContextMenu> spContextMenu;
338  hr = LoadAndInitialize(IID_PPV_ARG(IContextMenu, &spContextMenu));
339  if (!SUCCEEDED(hr))
340  return hr;
341 
342  CMINVOKECOMMANDINFO cm = { sizeof(cm), 0 };
343  cm.lpVerb = g_ContextMenu.GetString();
344  cm.nShow = SW_SHOW;
345  hr = spContextMenu->InvokeCommand(&cm);
346 
347  if (!SUCCEEDED(hr))
348  {
349  wprintf(L"IContextMenu->InvokeCommand failed: 0x%x\n", hr);
350  return hr;
351  }
352  wprintf(L"IContextMenu->InvokeCommand returned: 0x%x\n", hr);
353  }
354 
355  switch (g_Wait)
356  {
357  case Wait_None:
358  break;
359  case Wait_Infinite:
360  while (true) {
361  Sleep(1000);
362  }
363  break;
364  case Wait_OpenWindows:
365  WaitWindows();
366  break;
367  case Wait_Input:
368  wprintf(L"Press any key to continue...\n");
369  _getch();
370  break;
371 
372  }
373  return 0;
374 }
const DOCKBAR PVOID HWND HWND * hwnd
Definition: tooldock.h:22
#define HRESULT
Definition: msvc.h:9
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:113
#define TRUE
Definition: types.h:120
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:736
HRESULT hr
Definition: shlfolder.c:183
CLSID g_CLSID
Definition: shlextdbg.cpp:27
Definition: ftp_var.h:139
REFIID riid
Definition: precomp.h:44
#define REFCLSID
Definition: guiddef.h:112
static bool isCmd(int argc, WCHAR **argv, int n, PCWSTR check)
Definition: shlextdbg.cpp:196
static bool isCmdWithArg(int argc, WCHAR **argv, int &n, PCWSTR check, PCWSTR &arg)
Definition: shlextdbg.cpp:172
#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:129
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:201
void * arg
Definition: msvc.h:12
#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:127
bool g_bIShellPropSheetExt
Definition: shlextdbg.cpp:30
HRESULT LoadAndInitialize(REFIID riid, LPVOID *ppv)
Definition: shlextdbg.cpp:59
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:29
DWORD WINAPI GetWindowThreadProcessId(HWND, PDWORD)
#define E_INVALIDARG
Definition: ddrawi.h:101
#define LoadLibraryW(x)
Definition: compat.h:404
smooth NULL
Definition: ftsmooth.c:416
void WaitWindows()
Definition: shlextdbg.cpp:143
LONG_PTR LPARAM
Definition: windef.h:208
Definition: module.h:566
Definition: bufpool.h:45
INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
Definition: propsheet.c:2896
#define SW_SHOW
Definition: winuser.h:769
WaitType
Definition: shlextdbg.cpp:19
HWND g_ConsoleWindow
Definition: shlextdbg.cpp:128
static BOOL CALLBACK cb_AddPage(HPROPSHEETPAGE page, LPARAM lParam)
Definition: shlextdbg.cpp:162
CSimpleArray< HPROPSHEETPAGE > g_Pages
Definition: shlextdbg.cpp:161
HRESULT CreateIDataObject(CComHeapPtr< ITEMIDLIST > &pidl, CComPtr< IDataObject > &dataObject, PCWSTR FileName)
Definition: shlextdbg.cpp:34
__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:2343
PCXSTR GetString() const
Definition: atlsimpstr.h:361
CStringW g_DLL
Definition: shlextdbg.cpp:28
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:229
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:3234
bool IsEmpty() const
Definition: atlsimpstr.h:379
WaitType g_Wait
Definition: shlextdbg.cpp:32
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:31
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:1897
#define HRESULT_CODE(hr)
Definition: winerror.h:76
HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id)
Definition: compobj.c:2247
#define GetProcAddress(x, y)
Definition: compat.h:410
#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:1188
#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