xref: /reactos/dll/win32/wshom.ocx/shell.c (revision 9cfd8dd9)
1 /*
2  * Copyright 2011 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #ifdef __REACTOS__
20 #include <wchar.h>
21 #endif
22 
23 #include "wshom_private.h"
24 #include "wshom.h"
25 
26 #include "shellapi.h"
27 #include "shlobj.h"
28 #include "dispex.h"
29 
30 #include "wine/debug.h"
31 #include "wine/heap.h"
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(wshom);
34 
35 typedef struct
36 {
37     struct provideclassinfo classinfo;
38     IWshShell3 IWshShell3_iface;
39 } WshShellImpl;
40 static WshShellImpl WshShell3;
41 
42 typedef struct
43 {
44     struct provideclassinfo classinfo;
45     IWshCollection IWshCollection_iface;
46     LONG ref;
47 } WshCollection;
48 
49 typedef struct
50 {
51     struct provideclassinfo classinfo;
52     IWshShortcut IWshShortcut_iface;
53     LONG ref;
54 
55     IShellLinkW *link;
56     BSTR path_link;
57 } WshShortcut;
58 
59 typedef struct
60 {
61     struct provideclassinfo classinfo;
62     IWshEnvironment IWshEnvironment_iface;
63     LONG ref;
64 } WshEnvironment;
65 
66 typedef struct
67 {
68     struct provideclassinfo classinfo;
69     IWshExec IWshExec_iface;
70     LONG ref;
71     PROCESS_INFORMATION info;
72 } WshExecImpl;
73 
74 static inline WshCollection *impl_from_IWshCollection( IWshCollection *iface )
75 {
76     return CONTAINING_RECORD(iface, WshCollection, IWshCollection_iface);
77 }
78 
79 static inline WshShortcut *impl_from_IWshShortcut( IWshShortcut *iface )
80 {
81     return CONTAINING_RECORD(iface, WshShortcut, IWshShortcut_iface);
82 }
83 
84 static inline WshEnvironment *impl_from_IWshEnvironment( IWshEnvironment *iface )
85 {
86     return CONTAINING_RECORD(iface, WshEnvironment, IWshEnvironment_iface);
87 }
88 
89 static inline WshExecImpl *impl_from_IWshExec( IWshExec *iface )
90 {
91     return CONTAINING_RECORD(iface, WshExecImpl, IWshExec_iface);
92 }
93 
94 static HRESULT WINAPI WshExec_QueryInterface(IWshExec *iface, REFIID riid, void **obj)
95 {
96     WshExecImpl *This = impl_from_IWshExec(iface);
97 
98     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
99 
100     if (IsEqualGUID(riid, &IID_IDispatch) ||
101         IsEqualGUID(riid, &IID_IWshExec) ||
102         IsEqualGUID(riid, &IID_IUnknown))
103     {
104         *obj = iface;
105     }
106     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
107     {
108         *obj = &This->classinfo.IProvideClassInfo_iface;
109     }
110     else {
111         FIXME("Unknown iface %s\n", debugstr_guid(riid));
112         *obj = NULL;
113         return E_NOINTERFACE;
114     }
115 
116     IUnknown_AddRef((IUnknown *)*obj);
117     return S_OK;
118 }
119 
120 static ULONG WINAPI WshExec_AddRef(IWshExec *iface)
121 {
122     WshExecImpl *This = impl_from_IWshExec(iface);
123     LONG ref = InterlockedIncrement(&This->ref);
124     TRACE("(%p) ref = %d\n", This, ref);
125     return ref;
126 }
127 
128 static ULONG WINAPI WshExec_Release(IWshExec *iface)
129 {
130     WshExecImpl *This = impl_from_IWshExec(iface);
131     LONG ref = InterlockedDecrement(&This->ref);
132     TRACE("(%p) ref = %d\n", This, ref);
133 
134     if (!ref) {
135         CloseHandle(This->info.hThread);
136         CloseHandle(This->info.hProcess);
137         heap_free(This);
138     }
139 
140     return ref;
141 }
142 
143 static HRESULT WINAPI WshExec_GetTypeInfoCount(IWshExec *iface, UINT *pctinfo)
144 {
145     WshExecImpl *This = impl_from_IWshExec(iface);
146     TRACE("(%p)->(%p)\n", This, pctinfo);
147     *pctinfo = 1;
148     return S_OK;
149 }
150 
151 static HRESULT WINAPI WshExec_GetTypeInfo(IWshExec *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
152 {
153     WshExecImpl *This = impl_from_IWshExec(iface);
154     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
155     return get_typeinfo(IWshExec_tid, ppTInfo);
156 }
157 
158 static HRESULT WINAPI WshExec_GetIDsOfNames(IWshExec *iface, REFIID riid, LPOLESTR *rgszNames,
159         UINT cNames, LCID lcid, DISPID *rgDispId)
160 {
161     WshExecImpl *This = impl_from_IWshExec(iface);
162     ITypeInfo *typeinfo;
163     HRESULT hr;
164 
165     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
166 
167     hr = get_typeinfo(IWshExec_tid, &typeinfo);
168     if(SUCCEEDED(hr))
169     {
170         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
171         ITypeInfo_Release(typeinfo);
172     }
173 
174     return hr;
175 }
176 
177 static HRESULT WINAPI WshExec_Invoke(IWshExec *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
178         WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
179 {
180     WshExecImpl *This = impl_from_IWshExec(iface);
181     ITypeInfo *typeinfo;
182     HRESULT hr;
183 
184     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
185           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
186 
187     hr = get_typeinfo(IWshExec_tid, &typeinfo);
188     if(SUCCEEDED(hr))
189     {
190         hr = ITypeInfo_Invoke(typeinfo, &This->IWshExec_iface, dispIdMember, wFlags,
191                 pDispParams, pVarResult, pExcepInfo, puArgErr);
192         ITypeInfo_Release(typeinfo);
193     }
194 
195     return hr;
196 }
197 
198 static HRESULT WINAPI WshExec_get_Status(IWshExec *iface, WshExecStatus *status)
199 {
200     WshExecImpl *This = impl_from_IWshExec(iface);
201     DWORD code;
202 
203     TRACE("(%p)->(%p)\n", This, status);
204 
205     if (!status)
206         return E_INVALIDARG;
207 
208     if (!GetExitCodeProcess(This->info.hProcess, &code))
209         return HRESULT_FROM_WIN32(GetLastError());
210 
211     switch (code)
212     {
213     case 0:
214         *status = WshFinished;
215         break;
216     case STILL_ACTIVE:
217         *status = WshRunning;
218         break;
219     default:
220         *status = WshFailed;
221     }
222 
223     return S_OK;
224 }
225 
226 static HRESULT WINAPI WshExec_get_StdIn(IWshExec *iface, ITextStream **stream)
227 {
228     WshExecImpl *This = impl_from_IWshExec(iface);
229 
230     FIXME("(%p)->(%p): stub\n", This, stream);
231 
232     return E_NOTIMPL;
233 }
234 
235 static HRESULT WINAPI WshExec_get_StdOut(IWshExec *iface, ITextStream **stream)
236 {
237     WshExecImpl *This = impl_from_IWshExec(iface);
238 
239     FIXME("(%p)->(%p): stub\n", This, stream);
240 
241     return E_NOTIMPL;
242 }
243 
244 static HRESULT WINAPI WshExec_get_StdErr(IWshExec *iface, ITextStream **stream)
245 {
246     WshExecImpl *This = impl_from_IWshExec(iface);
247 
248     FIXME("(%p)->(%p): stub\n", This, stream);
249 
250     return E_NOTIMPL;
251 }
252 
253 static HRESULT WINAPI WshExec_get_ProcessID(IWshExec *iface, DWORD *pid)
254 {
255     WshExecImpl *This = impl_from_IWshExec(iface);
256 
257     TRACE("(%p)->(%p)\n", This, pid);
258 
259     if (!pid)
260         return E_INVALIDARG;
261 
262     *pid = This->info.dwProcessId;
263     return S_OK;
264 }
265 
266 static HRESULT WINAPI WshExec_get_ExitCode(IWshExec *iface, DWORD *code)
267 {
268     WshExecImpl *This = impl_from_IWshExec(iface);
269 
270     FIXME("(%p)->(%p): stub\n", This, code);
271 
272     return E_NOTIMPL;
273 }
274 
275 static BOOL CALLBACK enum_thread_wnd_proc(HWND hwnd, LPARAM lParam)
276 {
277     INT *count = (INT*)lParam;
278 
279     (*count)++;
280     PostMessageW(hwnd, WM_CLOSE, 0, 0);
281     /* try to send it to all windows, even if failed for some */
282     return TRUE;
283 }
284 
285 static HRESULT WINAPI WshExec_Terminate(IWshExec *iface)
286 {
287     WshExecImpl *This = impl_from_IWshExec(iface);
288     BOOL ret, kill = FALSE;
289     INT count = 0;
290 
291     TRACE("(%p)\n", This);
292 
293     ret = EnumThreadWindows(This->info.dwThreadId, enum_thread_wnd_proc, (LPARAM)&count);
294     if (ret && count) {
295         /* manual testing shows that it waits 2 seconds before forcing termination */
296         if (WaitForSingleObject(This->info.hProcess, 2000) != WAIT_OBJECT_0)
297             kill = TRUE;
298     }
299     else
300         kill = TRUE;
301 
302     if (kill)
303         TerminateProcess(This->info.hProcess, 0);
304 
305     return S_OK;
306 }
307 
308 static const IWshExecVtbl WshExecVtbl = {
309     WshExec_QueryInterface,
310     WshExec_AddRef,
311     WshExec_Release,
312     WshExec_GetTypeInfoCount,
313     WshExec_GetTypeInfo,
314     WshExec_GetIDsOfNames,
315     WshExec_Invoke,
316     WshExec_get_Status,
317     WshExec_get_StdIn,
318     WshExec_get_StdOut,
319     WshExec_get_StdErr,
320     WshExec_get_ProcessID,
321     WshExec_get_ExitCode,
322     WshExec_Terminate
323 };
324 
325 static HRESULT WshExec_create(BSTR command, IWshExec **ret)
326 {
327     STARTUPINFOW si = {0};
328     WshExecImpl *This;
329 
330     *ret = NULL;
331 
332     This = heap_alloc(sizeof(*This));
333     if (!This)
334         return E_OUTOFMEMORY;
335 
336     This->IWshExec_iface.lpVtbl = &WshExecVtbl;
337     This->ref = 1;
338 
339     if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &This->info)) {
340         heap_free(This);
341         return HRESULT_FROM_WIN32(GetLastError());
342     }
343 
344     init_classinfo(&CLSID_WshExec, (IUnknown *)&This->IWshExec_iface, &This->classinfo);
345     *ret = &This->IWshExec_iface;
346     return S_OK;
347 }
348 
349 static HRESULT WINAPI WshEnvironment_QueryInterface(IWshEnvironment *iface, REFIID riid, void **obj)
350 {
351     WshEnvironment *This = impl_from_IWshEnvironment(iface);
352 
353     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
354 
355     if (IsEqualGUID(riid, &IID_IUnknown)  ||
356         IsEqualGUID(riid, &IID_IDispatch) ||
357         IsEqualGUID(riid, &IID_IWshEnvironment))
358     {
359         *obj = iface;
360     }
361     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
362     {
363         *obj = &This->classinfo.IProvideClassInfo_iface;
364     }
365     else {
366         FIXME("Unknown iface %s\n", debugstr_guid(riid));
367         *obj = NULL;
368         return E_NOINTERFACE;
369     }
370 
371     IUnknown_AddRef((IUnknown*)*obj);
372     return S_OK;
373 }
374 
375 static ULONG WINAPI WshEnvironment_AddRef(IWshEnvironment *iface)
376 {
377     WshEnvironment *This = impl_from_IWshEnvironment(iface);
378     LONG ref = InterlockedIncrement(&This->ref);
379     TRACE("(%p) ref = %d\n", This, ref);
380     return ref;
381 }
382 
383 static ULONG WINAPI WshEnvironment_Release(IWshEnvironment *iface)
384 {
385     WshEnvironment *This = impl_from_IWshEnvironment(iface);
386     LONG ref = InterlockedDecrement(&This->ref);
387     TRACE("(%p) ref = %d\n", This, ref);
388 
389     if (!ref)
390         heap_free(This);
391 
392     return ref;
393 }
394 
395 static HRESULT WINAPI WshEnvironment_GetTypeInfoCount(IWshEnvironment *iface, UINT *pctinfo)
396 {
397     WshEnvironment *This = impl_from_IWshEnvironment(iface);
398     TRACE("(%p)->(%p)\n", This, pctinfo);
399     *pctinfo = 1;
400     return S_OK;
401 }
402 
403 static HRESULT WINAPI WshEnvironment_GetTypeInfo(IWshEnvironment *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
404 {
405     WshEnvironment *This = impl_from_IWshEnvironment(iface);
406     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
407     return get_typeinfo(IWshEnvironment_tid, ppTInfo);
408 }
409 
410 static HRESULT WINAPI WshEnvironment_GetIDsOfNames(IWshEnvironment *iface, REFIID riid, LPOLESTR *rgszNames,
411         UINT cNames, LCID lcid, DISPID *rgDispId)
412 {
413     WshEnvironment *This = impl_from_IWshEnvironment(iface);
414     ITypeInfo *typeinfo;
415     HRESULT hr;
416 
417     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
418 
419     hr = get_typeinfo(IWshEnvironment_tid, &typeinfo);
420     if(SUCCEEDED(hr))
421     {
422         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
423         ITypeInfo_Release(typeinfo);
424     }
425 
426     return hr;
427 }
428 
429 static HRESULT WINAPI WshEnvironment_Invoke(IWshEnvironment *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
430         WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
431 {
432     WshEnvironment *This = impl_from_IWshEnvironment(iface);
433     ITypeInfo *typeinfo;
434     HRESULT hr;
435 
436     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
437           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
438 
439     hr = get_typeinfo(IWshEnvironment_tid, &typeinfo);
440     if(SUCCEEDED(hr))
441     {
442         hr = ITypeInfo_Invoke(typeinfo, &This->IWshEnvironment_iface, dispIdMember, wFlags,
443                 pDispParams, pVarResult, pExcepInfo, puArgErr);
444         ITypeInfo_Release(typeinfo);
445     }
446 
447     return hr;
448 }
449 
450 static HRESULT WINAPI WshEnvironment_get_Item(IWshEnvironment *iface, BSTR name, BSTR *value)
451 {
452     WshEnvironment *This = impl_from_IWshEnvironment(iface);
453     DWORD len;
454 
455     TRACE("(%p)->(%s %p)\n", This, debugstr_w(name), value);
456 
457     if (!value)
458         return E_POINTER;
459 
460     len = GetEnvironmentVariableW(name, NULL, 0);
461     if (len)
462     {
463         *value = SysAllocStringLen(NULL, len - 1);
464         if (*value)
465             GetEnvironmentVariableW(name, *value, len);
466     }
467     else
468         *value = SysAllocStringLen(NULL, 0);
469 
470     return *value ? S_OK : E_OUTOFMEMORY;
471 }
472 
473 static HRESULT WINAPI WshEnvironment_put_Item(IWshEnvironment *iface, BSTR name, BSTR value)
474 {
475     WshEnvironment *This = impl_from_IWshEnvironment(iface);
476     FIXME("(%p)->(%s %s): stub\n", This, debugstr_w(name), debugstr_w(value));
477     return E_NOTIMPL;
478 }
479 
480 static HRESULT WINAPI WshEnvironment_Count(IWshEnvironment *iface, LONG *count)
481 {
482     WshEnvironment *This = impl_from_IWshEnvironment(iface);
483     FIXME("(%p)->(%p): stub\n", This, count);
484     return E_NOTIMPL;
485 }
486 
487 static HRESULT WINAPI WshEnvironment_get_length(IWshEnvironment *iface, LONG *len)
488 {
489     WshEnvironment *This = impl_from_IWshEnvironment(iface);
490     FIXME("(%p)->(%p): stub\n", This, len);
491     return E_NOTIMPL;
492 }
493 
494 static HRESULT WINAPI WshEnvironment__NewEnum(IWshEnvironment *iface, IUnknown **penum)
495 {
496     WshEnvironment *This = impl_from_IWshEnvironment(iface);
497     FIXME("(%p)->(%p): stub\n", This, penum);
498     return E_NOTIMPL;
499 }
500 
501 static HRESULT WINAPI WshEnvironment_Remove(IWshEnvironment *iface, BSTR name)
502 {
503     WshEnvironment *This = impl_from_IWshEnvironment(iface);
504     FIXME("(%p)->(%s): stub\n", This, debugstr_w(name));
505     return E_NOTIMPL;
506 }
507 
508 static const IWshEnvironmentVtbl WshEnvironmentVtbl = {
509     WshEnvironment_QueryInterface,
510     WshEnvironment_AddRef,
511     WshEnvironment_Release,
512     WshEnvironment_GetTypeInfoCount,
513     WshEnvironment_GetTypeInfo,
514     WshEnvironment_GetIDsOfNames,
515     WshEnvironment_Invoke,
516     WshEnvironment_get_Item,
517     WshEnvironment_put_Item,
518     WshEnvironment_Count,
519     WshEnvironment_get_length,
520     WshEnvironment__NewEnum,
521     WshEnvironment_Remove
522 };
523 
524 static HRESULT WshEnvironment_Create(IWshEnvironment **env)
525 {
526     WshEnvironment *This;
527 
528     This = heap_alloc(sizeof(*This));
529     if (!This) return E_OUTOFMEMORY;
530 
531     This->IWshEnvironment_iface.lpVtbl = &WshEnvironmentVtbl;
532     This->ref = 1;
533 
534     init_classinfo(&IID_IWshEnvironment, (IUnknown *)&This->IWshEnvironment_iface, &This->classinfo);
535     *env = &This->IWshEnvironment_iface;
536 
537     return S_OK;
538 }
539 
540 static HRESULT WINAPI WshCollection_QueryInterface(IWshCollection *iface, REFIID riid, void **ppv)
541 {
542     WshCollection *This = impl_from_IWshCollection(iface);
543 
544     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
545 
546     if (IsEqualGUID(riid, &IID_IUnknown)  ||
547         IsEqualGUID(riid, &IID_IDispatch) ||
548         IsEqualGUID(riid, &IID_IWshCollection))
549     {
550         *ppv = iface;
551     }
552     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
553     {
554         *ppv = &This->classinfo.IProvideClassInfo_iface;
555     }
556     else {
557         FIXME("Unknown iface %s\n", debugstr_guid(riid));
558         *ppv = NULL;
559         return E_NOINTERFACE;
560     }
561 
562     IUnknown_AddRef((IUnknown*)*ppv);
563     return S_OK;
564 }
565 
566 static ULONG WINAPI WshCollection_AddRef(IWshCollection *iface)
567 {
568     WshCollection *This = impl_from_IWshCollection(iface);
569     LONG ref = InterlockedIncrement(&This->ref);
570     TRACE("(%p) ref = %d\n", This, ref);
571     return ref;
572 }
573 
574 static ULONG WINAPI WshCollection_Release(IWshCollection *iface)
575 {
576     WshCollection *This = impl_from_IWshCollection(iface);
577     LONG ref = InterlockedDecrement(&This->ref);
578     TRACE("(%p) ref = %d\n", This, ref);
579 
580     if (!ref)
581         heap_free(This);
582 
583     return ref;
584 }
585 
586 static HRESULT WINAPI WshCollection_GetTypeInfoCount(IWshCollection *iface, UINT *pctinfo)
587 {
588     WshCollection *This = impl_from_IWshCollection(iface);
589     TRACE("(%p)->(%p)\n", This, pctinfo);
590     *pctinfo = 1;
591     return S_OK;
592 }
593 
594 static HRESULT WINAPI WshCollection_GetTypeInfo(IWshCollection *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
595 {
596     WshCollection *This = impl_from_IWshCollection(iface);
597     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
598     return get_typeinfo(IWshCollection_tid, ppTInfo);
599 }
600 
601 static HRESULT WINAPI WshCollection_GetIDsOfNames(IWshCollection *iface, REFIID riid, LPOLESTR *rgszNames,
602         UINT cNames, LCID lcid, DISPID *rgDispId)
603 {
604     WshCollection *This = impl_from_IWshCollection(iface);
605     ITypeInfo *typeinfo;
606     HRESULT hr;
607 
608     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
609 
610     hr = get_typeinfo(IWshCollection_tid, &typeinfo);
611     if(SUCCEEDED(hr))
612     {
613         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
614         ITypeInfo_Release(typeinfo);
615     }
616 
617     return hr;
618 }
619 
620 static HRESULT WINAPI WshCollection_Invoke(IWshCollection *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
621         WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
622 {
623     WshCollection *This = impl_from_IWshCollection(iface);
624     ITypeInfo *typeinfo;
625     HRESULT hr;
626 
627     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
628           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
629 
630     hr = get_typeinfo(IWshCollection_tid, &typeinfo);
631     if(SUCCEEDED(hr))
632     {
633         hr = ITypeInfo_Invoke(typeinfo, &This->IWshCollection_iface, dispIdMember, wFlags,
634                 pDispParams, pVarResult, pExcepInfo, puArgErr);
635         ITypeInfo_Release(typeinfo);
636     }
637 
638     return hr;
639 }
640 
641 static HRESULT WINAPI WshCollection_Item(IWshCollection *iface, VARIANT *index, VARIANT *value)
642 {
643     WshCollection *This = impl_from_IWshCollection(iface);
644     static const WCHAR allusersdesktopW[] = {'A','l','l','U','s','e','r','s','D','e','s','k','t','o','p',0};
645     static const WCHAR allusersprogramsW[] = {'A','l','l','U','s','e','r','s','P','r','o','g','r','a','m','s',0};
646     static const WCHAR desktopW[] = {'D','e','s','k','t','o','p',0};
647     PIDLIST_ABSOLUTE pidl;
648     WCHAR pathW[MAX_PATH];
649     int kind = 0;
650     BSTR folder;
651     HRESULT hr;
652 
653     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(index), value);
654 
655     if (V_VT(index) != VT_BSTR)
656     {
657         FIXME("only BSTR index supported, got %d\n", V_VT(index));
658         return E_NOTIMPL;
659     }
660 
661     folder = V_BSTR(index);
662     if (!wcsicmp(folder, desktopW))
663         kind = CSIDL_DESKTOP;
664     else if (!wcsicmp(folder, allusersdesktopW))
665         kind = CSIDL_COMMON_DESKTOPDIRECTORY;
666     else if (!wcsicmp(folder, allusersprogramsW))
667         kind = CSIDL_COMMON_PROGRAMS;
668     else
669     {
670         FIXME("folder kind %s not supported\n", debugstr_w(folder));
671         return E_NOTIMPL;
672     }
673 
674     hr = SHGetSpecialFolderLocation(NULL, kind, &pidl);
675     if (hr != S_OK) return hr;
676 
677     if (SHGetPathFromIDListW(pidl, pathW))
678     {
679         V_VT(value) = VT_BSTR;
680         V_BSTR(value) = SysAllocString(pathW);
681         hr = V_BSTR(value) ? S_OK : E_OUTOFMEMORY;
682     }
683     else
684         hr = E_FAIL;
685 
686     CoTaskMemFree(pidl);
687 
688     return hr;
689 }
690 
691 static HRESULT WINAPI WshCollection_Count(IWshCollection *iface, LONG *count)
692 {
693     WshCollection *This = impl_from_IWshCollection(iface);
694     FIXME("(%p)->(%p): stub\n", This, count);
695     return E_NOTIMPL;
696 }
697 
698 static HRESULT WINAPI WshCollection_get_length(IWshCollection *iface, LONG *count)
699 {
700     WshCollection *This = impl_from_IWshCollection(iface);
701     FIXME("(%p)->(%p): stub\n", This, count);
702     return E_NOTIMPL;
703 }
704 
705 static HRESULT WINAPI WshCollection__NewEnum(IWshCollection *iface, IUnknown **Enum)
706 {
707     WshCollection *This = impl_from_IWshCollection(iface);
708     FIXME("(%p)->(%p): stub\n", This, Enum);
709     return E_NOTIMPL;
710 }
711 
712 static const IWshCollectionVtbl WshCollectionVtbl = {
713     WshCollection_QueryInterface,
714     WshCollection_AddRef,
715     WshCollection_Release,
716     WshCollection_GetTypeInfoCount,
717     WshCollection_GetTypeInfo,
718     WshCollection_GetIDsOfNames,
719     WshCollection_Invoke,
720     WshCollection_Item,
721     WshCollection_Count,
722     WshCollection_get_length,
723     WshCollection__NewEnum
724 };
725 
726 static HRESULT WshCollection_Create(IWshCollection **collection)
727 {
728     WshCollection *This;
729 
730     This = heap_alloc(sizeof(*This));
731     if (!This) return E_OUTOFMEMORY;
732 
733     This->IWshCollection_iface.lpVtbl = &WshCollectionVtbl;
734     This->ref = 1;
735 
736     init_classinfo(&IID_IWshCollection, (IUnknown *)&This->IWshCollection_iface, &This->classinfo);
737     *collection = &This->IWshCollection_iface;
738 
739     return S_OK;
740 }
741 
742 /* IWshShortcut */
743 static HRESULT WINAPI WshShortcut_QueryInterface(IWshShortcut *iface, REFIID riid, void **ppv)
744 {
745     WshShortcut *This = impl_from_IWshShortcut(iface);
746 
747     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
748 
749     if (IsEqualGUID(riid, &IID_IUnknown)  ||
750         IsEqualGUID(riid, &IID_IDispatch) ||
751         IsEqualGUID(riid, &IID_IWshShortcut))
752     {
753         *ppv = iface;
754     }
755     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
756     {
757         *ppv = &This->classinfo.IProvideClassInfo_iface;
758     }
759     else {
760         FIXME("Unknown iface %s\n", debugstr_guid(riid));
761         *ppv = NULL;
762         return E_NOINTERFACE;
763     }
764 
765     IUnknown_AddRef((IUnknown*)*ppv);
766     return S_OK;
767 }
768 
769 static ULONG WINAPI WshShortcut_AddRef(IWshShortcut *iface)
770 {
771     WshShortcut *This = impl_from_IWshShortcut(iface);
772     LONG ref = InterlockedIncrement(&This->ref);
773     TRACE("(%p) ref = %d\n", This, ref);
774     return ref;
775 }
776 
777 static ULONG WINAPI WshShortcut_Release(IWshShortcut *iface)
778 {
779     WshShortcut *This = impl_from_IWshShortcut(iface);
780     LONG ref = InterlockedDecrement(&This->ref);
781     TRACE("(%p) ref = %d\n", This, ref);
782 
783     if (!ref)
784     {
785         SysFreeString(This->path_link);
786         IShellLinkW_Release(This->link);
787         heap_free(This);
788     }
789 
790     return ref;
791 }
792 
793 static HRESULT WINAPI WshShortcut_GetTypeInfoCount(IWshShortcut *iface, UINT *pctinfo)
794 {
795     WshShortcut *This = impl_from_IWshShortcut(iface);
796     TRACE("(%p)->(%p)\n", This, pctinfo);
797     *pctinfo = 1;
798     return S_OK;
799 }
800 
801 static HRESULT WINAPI WshShortcut_GetTypeInfo(IWshShortcut *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
802 {
803     WshShortcut *This = impl_from_IWshShortcut(iface);
804     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
805     return get_typeinfo(IWshShortcut_tid, ppTInfo);
806 }
807 
808 static HRESULT WINAPI WshShortcut_GetIDsOfNames(IWshShortcut *iface, REFIID riid, LPOLESTR *rgszNames,
809         UINT cNames, LCID lcid, DISPID *rgDispId)
810 {
811     WshShortcut *This = impl_from_IWshShortcut(iface);
812     ITypeInfo *typeinfo;
813     HRESULT hr;
814 
815     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
816 
817     hr = get_typeinfo(IWshShortcut_tid, &typeinfo);
818     if(SUCCEEDED(hr))
819     {
820         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
821         ITypeInfo_Release(typeinfo);
822     }
823 
824     return hr;
825 }
826 
827 static HRESULT WINAPI WshShortcut_Invoke(IWshShortcut *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
828         WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
829 {
830     WshShortcut *This = impl_from_IWshShortcut(iface);
831     ITypeInfo *typeinfo;
832     HRESULT hr;
833 
834     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
835           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
836 
837     hr = get_typeinfo(IWshShortcut_tid, &typeinfo);
838     if(SUCCEEDED(hr))
839     {
840         hr = ITypeInfo_Invoke(typeinfo, &This->IWshShortcut_iface, dispIdMember, wFlags,
841                 pDispParams, pVarResult, pExcepInfo, puArgErr);
842         ITypeInfo_Release(typeinfo);
843     }
844 
845     return hr;
846 }
847 
848 static HRESULT WINAPI WshShortcut_get_FullName(IWshShortcut *iface, BSTR *name)
849 {
850     WshShortcut *This = impl_from_IWshShortcut(iface);
851     FIXME("(%p)->(%p): stub\n", This, name);
852     return E_NOTIMPL;
853 }
854 
855 static HRESULT WINAPI WshShortcut_get_Arguments(IWshShortcut *iface, BSTR *Arguments)
856 {
857     WshShortcut *This = impl_from_IWshShortcut(iface);
858     WCHAR buffW[INFOTIPSIZE];
859     HRESULT hr;
860 
861     TRACE("(%p)->(%p)\n", This, Arguments);
862 
863     if (!Arguments)
864         return E_POINTER;
865 
866     *Arguments = NULL;
867 
868     hr = IShellLinkW_GetArguments(This->link, buffW, ARRAY_SIZE(buffW));
869     if (FAILED(hr))
870         return hr;
871 
872     *Arguments = SysAllocString(buffW);
873     return *Arguments ? S_OK : E_OUTOFMEMORY;
874 }
875 
876 static HRESULT WINAPI WshShortcut_put_Arguments(IWshShortcut *iface, BSTR Arguments)
877 {
878     WshShortcut *This = impl_from_IWshShortcut(iface);
879 
880     TRACE("(%p)->(%s)\n", This, debugstr_w(Arguments));
881 
882     return IShellLinkW_SetArguments(This->link, Arguments);
883 }
884 
885 static HRESULT WINAPI WshShortcut_get_Description(IWshShortcut *iface, BSTR *Description)
886 {
887     WshShortcut *This = impl_from_IWshShortcut(iface);
888     FIXME("(%p)->(%p): stub\n", This, Description);
889     return E_NOTIMPL;
890 }
891 
892 static HRESULT WINAPI WshShortcut_put_Description(IWshShortcut *iface, BSTR Description)
893 {
894     WshShortcut *This = impl_from_IWshShortcut(iface);
895     TRACE("(%p)->(%s)\n", This, debugstr_w(Description));
896     return IShellLinkW_SetDescription(This->link, Description);
897 }
898 
899 static HRESULT WINAPI WshShortcut_get_Hotkey(IWshShortcut *iface, BSTR *Hotkey)
900 {
901     WshShortcut *This = impl_from_IWshShortcut(iface);
902     FIXME("(%p)->(%p): stub\n", This, Hotkey);
903     return E_NOTIMPL;
904 }
905 
906 static HRESULT WINAPI WshShortcut_put_Hotkey(IWshShortcut *iface, BSTR Hotkey)
907 {
908     WshShortcut *This = impl_from_IWshShortcut(iface);
909     FIXME("(%p)->(%s): stub\n", This, debugstr_w(Hotkey));
910     return E_NOTIMPL;
911 }
912 
913 static HRESULT WINAPI WshShortcut_get_IconLocation(IWshShortcut *iface, BSTR *IconPath)
914 {
915     static const WCHAR fmtW[] = {'%','s',',',' ','%','d',0};
916     WshShortcut *This = impl_from_IWshShortcut(iface);
917     WCHAR buffW[MAX_PATH], pathW[MAX_PATH];
918     INT icon = 0;
919     HRESULT hr;
920 
921     TRACE("(%p)->(%p)\n", This, IconPath);
922 
923     if (!IconPath)
924         return E_POINTER;
925 
926     hr = IShellLinkW_GetIconLocation(This->link, buffW, ARRAY_SIZE(buffW), &icon);
927     if (FAILED(hr)) return hr;
928 
929     swprintf(pathW, fmtW, buffW, icon);
930     *IconPath = SysAllocString(pathW);
931     if (!*IconPath) return E_OUTOFMEMORY;
932 
933     return S_OK;
934 }
935 
936 static HRESULT WINAPI WshShortcut_put_IconLocation(IWshShortcut *iface, BSTR IconPath)
937 {
938     WshShortcut *This = impl_from_IWshShortcut(iface);
939     HRESULT hr;
940     WCHAR *ptr;
941     BSTR path;
942     INT icon;
943 
944     TRACE("(%p)->(%s)\n", This, debugstr_w(IconPath));
945 
946     /* scan for icon id */
947     ptr = wcsrchr(IconPath, ',');
948     if (!ptr)
949     {
950         WARN("icon index not found\n");
951         return E_FAIL;
952     }
953 
954     path = SysAllocStringLen(IconPath, ptr-IconPath);
955 
956     /* skip spaces if any */
957     while (iswspace(*++ptr))
958         ;
959 
960     icon = wcstol(ptr, NULL, 10);
961 
962     hr = IShellLinkW_SetIconLocation(This->link, path, icon);
963     SysFreeString(path);
964 
965     return hr;
966 }
967 
968 static HRESULT WINAPI WshShortcut_put_RelativePath(IWshShortcut *iface, BSTR rhs)
969 {
970     WshShortcut *This = impl_from_IWshShortcut(iface);
971     FIXME("(%p)->(%s): stub\n", This, debugstr_w(rhs));
972     return E_NOTIMPL;
973 }
974 
975 static HRESULT WINAPI WshShortcut_get_TargetPath(IWshShortcut *iface, BSTR *Path)
976 {
977     WshShortcut *This = impl_from_IWshShortcut(iface);
978     FIXME("(%p)->(%p): stub\n", This, Path);
979     return E_NOTIMPL;
980 }
981 
982 static HRESULT WINAPI WshShortcut_put_TargetPath(IWshShortcut *iface, BSTR Path)
983 {
984     WshShortcut *This = impl_from_IWshShortcut(iface);
985     TRACE("(%p)->(%s)\n", This, debugstr_w(Path));
986     return IShellLinkW_SetPath(This->link, Path);
987 }
988 
989 static HRESULT WINAPI WshShortcut_get_WindowStyle(IWshShortcut *iface, int *ShowCmd)
990 {
991     WshShortcut *This = impl_from_IWshShortcut(iface);
992     TRACE("(%p)->(%p)\n", This, ShowCmd);
993     return IShellLinkW_GetShowCmd(This->link, ShowCmd);
994 }
995 
996 static HRESULT WINAPI WshShortcut_put_WindowStyle(IWshShortcut *iface, int ShowCmd)
997 {
998     WshShortcut *This = impl_from_IWshShortcut(iface);
999     TRACE("(%p)->(%d)\n", This, ShowCmd);
1000     return IShellLinkW_SetShowCmd(This->link, ShowCmd);
1001 }
1002 
1003 static HRESULT WINAPI WshShortcut_get_WorkingDirectory(IWshShortcut *iface, BSTR *WorkingDirectory)
1004 {
1005     WshShortcut *This = impl_from_IWshShortcut(iface);
1006     WCHAR buffW[MAX_PATH];
1007     HRESULT hr;
1008 
1009     TRACE("(%p)->(%p)\n", This, WorkingDirectory);
1010 
1011     if (!WorkingDirectory)
1012         return E_POINTER;
1013 
1014     *WorkingDirectory = NULL;
1015     hr = IShellLinkW_GetWorkingDirectory(This->link, buffW, ARRAY_SIZE(buffW));
1016     if (FAILED(hr)) return hr;
1017 
1018     *WorkingDirectory = SysAllocString(buffW);
1019     return *WorkingDirectory ? S_OK : E_OUTOFMEMORY;
1020 }
1021 
1022 static HRESULT WINAPI WshShortcut_put_WorkingDirectory(IWshShortcut *iface, BSTR WorkingDirectory)
1023 {
1024     WshShortcut *This = impl_from_IWshShortcut(iface);
1025     TRACE("(%p)->(%s)\n", This, debugstr_w(WorkingDirectory));
1026     return IShellLinkW_SetWorkingDirectory(This->link, WorkingDirectory);
1027 }
1028 
1029 static HRESULT WINAPI WshShortcut_Load(IWshShortcut *iface, BSTR PathLink)
1030 {
1031     WshShortcut *This = impl_from_IWshShortcut(iface);
1032     FIXME("(%p)->(%s): stub\n", This, debugstr_w(PathLink));
1033     return E_NOTIMPL;
1034 }
1035 
1036 static HRESULT WINAPI WshShortcut_Save(IWshShortcut *iface)
1037 {
1038     WshShortcut *This = impl_from_IWshShortcut(iface);
1039     IPersistFile *file;
1040     HRESULT hr;
1041 
1042     TRACE("(%p)\n", This);
1043 
1044     IShellLinkW_QueryInterface(This->link, &IID_IPersistFile, (void**)&file);
1045     hr = IPersistFile_Save(file, This->path_link, TRUE);
1046     IPersistFile_Release(file);
1047 
1048     return hr;
1049 }
1050 
1051 static const IWshShortcutVtbl WshShortcutVtbl = {
1052     WshShortcut_QueryInterface,
1053     WshShortcut_AddRef,
1054     WshShortcut_Release,
1055     WshShortcut_GetTypeInfoCount,
1056     WshShortcut_GetTypeInfo,
1057     WshShortcut_GetIDsOfNames,
1058     WshShortcut_Invoke,
1059     WshShortcut_get_FullName,
1060     WshShortcut_get_Arguments,
1061     WshShortcut_put_Arguments,
1062     WshShortcut_get_Description,
1063     WshShortcut_put_Description,
1064     WshShortcut_get_Hotkey,
1065     WshShortcut_put_Hotkey,
1066     WshShortcut_get_IconLocation,
1067     WshShortcut_put_IconLocation,
1068     WshShortcut_put_RelativePath,
1069     WshShortcut_get_TargetPath,
1070     WshShortcut_put_TargetPath,
1071     WshShortcut_get_WindowStyle,
1072     WshShortcut_put_WindowStyle,
1073     WshShortcut_get_WorkingDirectory,
1074     WshShortcut_put_WorkingDirectory,
1075     WshShortcut_Load,
1076     WshShortcut_Save
1077 };
1078 
1079 static HRESULT WshShortcut_Create(const WCHAR *path, IDispatch **shortcut)
1080 {
1081     WshShortcut *This;
1082     HRESULT hr;
1083 
1084     *shortcut = NULL;
1085 
1086     This = heap_alloc(sizeof(*This));
1087     if (!This) return E_OUTOFMEMORY;
1088 
1089     This->IWshShortcut_iface.lpVtbl = &WshShortcutVtbl;
1090     This->ref = 1;
1091 
1092     hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1093             &IID_IShellLinkW, (void**)&This->link);
1094     if (FAILED(hr))
1095     {
1096         heap_free(This);
1097         return hr;
1098     }
1099 
1100     This->path_link = SysAllocString(path);
1101     if (!This->path_link)
1102     {
1103         IShellLinkW_Release(This->link);
1104         heap_free(This);
1105         return E_OUTOFMEMORY;
1106     }
1107 
1108     init_classinfo(&IID_IWshShortcut, (IUnknown *)&This->IWshShortcut_iface, &This->classinfo);
1109     *shortcut = (IDispatch*)&This->IWshShortcut_iface;
1110 
1111     return S_OK;
1112 }
1113 
1114 static HRESULT WINAPI WshShell3_QueryInterface(IWshShell3 *iface, REFIID riid, void **ppv)
1115 {
1116     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1117 
1118     *ppv = NULL;
1119 
1120     if (IsEqualGUID(riid, &IID_IDispatch) ||
1121         IsEqualGUID(riid, &IID_IWshShell3) ||
1122         IsEqualGUID(riid, &IID_IWshShell2) ||
1123         IsEqualGUID(riid, &IID_IWshShell) ||
1124         IsEqualGUID(riid, &IID_IUnknown))
1125     {
1126         *ppv = iface;
1127     }
1128     else if (IsEqualGUID(riid, &IID_IDispatchEx))
1129     {
1130         return E_NOINTERFACE;
1131     }
1132     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
1133     {
1134         *ppv = &WshShell3.classinfo.IProvideClassInfo_iface;
1135     }
1136     else
1137     {
1138         WARN("unknown iface %s\n", debugstr_guid(riid));
1139         return E_NOINTERFACE;
1140     }
1141 
1142     IUnknown_AddRef((IUnknown *)*ppv);
1143     return S_OK;
1144 }
1145 
1146 static ULONG WINAPI WshShell3_AddRef(IWshShell3 *iface)
1147 {
1148     TRACE("()\n");
1149     return 2;
1150 }
1151 
1152 static ULONG WINAPI WshShell3_Release(IWshShell3 *iface)
1153 {
1154     TRACE("()\n");
1155     return 2;
1156 }
1157 
1158 static HRESULT WINAPI WshShell3_GetTypeInfoCount(IWshShell3 *iface, UINT *pctinfo)
1159 {
1160     TRACE("(%p)\n", pctinfo);
1161     *pctinfo = 1;
1162     return S_OK;
1163 }
1164 
1165 static HRESULT WINAPI WshShell3_GetTypeInfo(IWshShell3 *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1166 {
1167     TRACE("(%u %u %p)\n", iTInfo, lcid, ppTInfo);
1168     return get_typeinfo(IWshShell3_tid, ppTInfo);
1169 }
1170 
1171 static HRESULT WINAPI WshShell3_GetIDsOfNames(IWshShell3 *iface, REFIID riid, LPOLESTR *rgszNames,
1172         UINT cNames, LCID lcid, DISPID *rgDispId)
1173 {
1174     ITypeInfo *typeinfo;
1175     HRESULT hr;
1176 
1177     TRACE("(%s %p %u %u %p)\n", debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
1178 
1179     hr = get_typeinfo(IWshShell3_tid, &typeinfo);
1180     if(SUCCEEDED(hr))
1181     {
1182         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1183         ITypeInfo_Release(typeinfo);
1184     }
1185 
1186     return hr;
1187 }
1188 
1189 static HRESULT WINAPI WshShell3_Invoke(IWshShell3 *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
1190         WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1191 {
1192     ITypeInfo *typeinfo;
1193     HRESULT hr;
1194 
1195     TRACE("(%d %s %d %d %p %p %p %p)\n", dispIdMember, debugstr_guid(riid),
1196           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1197 
1198     hr = get_typeinfo(IWshShell3_tid, &typeinfo);
1199     if(SUCCEEDED(hr))
1200     {
1201         hr = ITypeInfo_Invoke(typeinfo, &WshShell3.IWshShell3_iface, dispIdMember, wFlags,
1202                 pDispParams, pVarResult, pExcepInfo, puArgErr);
1203         ITypeInfo_Release(typeinfo);
1204     }
1205 
1206     return hr;
1207 }
1208 
1209 static HRESULT WINAPI WshShell3_get_SpecialFolders(IWshShell3 *iface, IWshCollection **folders)
1210 {
1211     TRACE("(%p)\n", folders);
1212     return WshCollection_Create(folders);
1213 }
1214 
1215 static HRESULT WINAPI WshShell3_get_Environment(IWshShell3 *iface, VARIANT *type, IWshEnvironment **env)
1216 {
1217     FIXME("(%s %p): semi-stub\n", debugstr_variant(type), env);
1218     return WshEnvironment_Create(env);
1219 }
1220 
1221 static inline BOOL is_optional_argument(const VARIANT *arg)
1222 {
1223     return V_VT(arg) == VT_ERROR && V_ERROR(arg) == DISP_E_PARAMNOTFOUND;
1224 }
1225 
1226 static WCHAR *split_command( BSTR cmd, WCHAR **params )
1227 {
1228     WCHAR *ret, *ptr;
1229     BOOL in_quotes = FALSE;
1230 
1231     if (!(ret = heap_alloc((lstrlenW(cmd) + 1) * sizeof(WCHAR)))) return NULL;
1232     lstrcpyW( ret, cmd );
1233 
1234     *params = NULL;
1235     for (ptr = ret; *ptr; ptr++)
1236     {
1237         if (*ptr == '"') in_quotes = !in_quotes;
1238         else if (*ptr == ' ' && !in_quotes)
1239         {
1240             *ptr = 0;
1241             *params = ptr + 1;
1242             break;
1243         }
1244     }
1245 
1246     return ret;
1247 }
1248 
1249 static HRESULT WINAPI WshShell3_Run(IWshShell3 *iface, BSTR cmd, VARIANT *style, VARIANT *wait, DWORD *exit_code)
1250 {
1251     SHELLEXECUTEINFOW info;
1252     int waitforprocess;
1253     WCHAR *file, *params;
1254     VARIANT s;
1255     HRESULT hr;
1256     BOOL ret;
1257 
1258     TRACE("(%s %s %s %p)\n", debugstr_w(cmd), debugstr_variant(style), debugstr_variant(wait), exit_code);
1259 
1260     if (!style || !wait || !exit_code)
1261         return E_POINTER;
1262 
1263     VariantInit(&s);
1264 #ifdef __REACTOS__
1265     if (is_optional_argument(style))
1266     {
1267         V_VT(&s) = VT_I4;
1268         V_I4(&s) = SW_SHOW;
1269         hr = S_OK;
1270     }
1271     else
1272 #endif
1273     hr = VariantChangeType(&s, style, 0, VT_I4);
1274     if (FAILED(hr))
1275     {
1276         ERR("failed to convert style argument, 0x%08x\n", hr);
1277         return hr;
1278     }
1279 
1280     if (is_optional_argument(wait))
1281         waitforprocess = 0;
1282     else {
1283         VARIANT w;
1284 
1285         VariantInit(&w);
1286         hr = VariantChangeType(&w, wait, 0, VT_I4);
1287         if (FAILED(hr))
1288             return hr;
1289 
1290         waitforprocess = V_I4(&w);
1291     }
1292 
1293     if (!(file = split_command(cmd, &params))) return E_OUTOFMEMORY;
1294 
1295     memset(&info, 0, sizeof(info));
1296     info.cbSize = sizeof(info);
1297     info.fMask = waitforprocess ? SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS : SEE_MASK_DEFAULT;
1298     info.lpFile = file;
1299     info.lpParameters = params;
1300     info.nShow = V_I4(&s);
1301 
1302     ret = ShellExecuteExW(&info);
1303     heap_free( file );
1304     if (!ret)
1305     {
1306         TRACE("ShellExecute failed, %d\n", GetLastError());
1307         return HRESULT_FROM_WIN32(GetLastError());
1308     }
1309     else
1310     {
1311         if (waitforprocess)
1312         {
1313             WaitForSingleObject(info.hProcess, INFINITE);
1314             GetExitCodeProcess(info.hProcess, exit_code);
1315             CloseHandle(info.hProcess);
1316         }
1317         else
1318             *exit_code = 0;
1319 
1320         return S_OK;
1321     }
1322 }
1323 
1324 struct popup_thread_param
1325 {
1326     WCHAR *text;
1327     VARIANT title;
1328     VARIANT type;
1329     INT button;
1330 };
1331 
1332 static DWORD WINAPI popup_thread_proc(void *arg)
1333 {
1334     static const WCHAR defaulttitleW[] = {'W','i','n','d','o','w','s',' ','S','c','r','i','p','t',' ','H','o','s','t',0};
1335     struct popup_thread_param *param = (struct popup_thread_param *)arg;
1336 
1337     param->button = MessageBoxW(NULL, param->text, is_optional_argument(&param->title) ?
1338             defaulttitleW : V_BSTR(&param->title), V_I4(&param->type));
1339     return 0;
1340 }
1341 
1342 static HRESULT WINAPI WshShell3_Popup(IWshShell3 *iface, BSTR text, VARIANT *seconds_to_wait, VARIANT *title,
1343         VARIANT *type, int *button)
1344 {
1345     struct popup_thread_param param;
1346     DWORD tid, status;
1347     VARIANT timeout;
1348     HANDLE hthread;
1349     HRESULT hr;
1350 
1351     TRACE("(%s %s %s %s %p)\n", debugstr_w(text), debugstr_variant(seconds_to_wait), debugstr_variant(title),
1352         debugstr_variant(type), button);
1353 
1354     if (!seconds_to_wait || !title || !type || !button)
1355         return E_POINTER;
1356 
1357     VariantInit(&timeout);
1358     if (!is_optional_argument(seconds_to_wait))
1359     {
1360         hr = VariantChangeType(&timeout, seconds_to_wait, 0, VT_I4);
1361         if (FAILED(hr))
1362             return hr;
1363     }
1364 #ifdef __REACTOS__
1365     else
1366     {
1367         VariantChangeType(&timeout, &timeout, 0, VT_I4);
1368     }
1369 #endif
1370 
1371     VariantInit(&param.type);
1372     if (!is_optional_argument(type))
1373     {
1374         hr = VariantChangeType(&param.type, type, 0, VT_I4);
1375         if (FAILED(hr))
1376             return hr;
1377     }
1378 #ifdef __REACTOS__
1379     else
1380     {
1381         VariantChangeType(&param.type, &param.type, 0, VT_I4);
1382     }
1383 #endif
1384 
1385     if (is_optional_argument(title))
1386         param.title = *title;
1387     else
1388     {
1389         VariantInit(&param.title);
1390         hr = VariantChangeType(&param.title, title, 0, VT_BSTR);
1391         if (FAILED(hr))
1392             return hr;
1393     }
1394 
1395     param.text = text;
1396     param.button = -1;
1397     hthread = CreateThread(NULL, 0, popup_thread_proc, &param, 0, &tid);
1398     status = MsgWaitForMultipleObjects(1, &hthread, FALSE, V_I4(&timeout) ? V_I4(&timeout) * 1000: INFINITE, 0);
1399     if (status == WAIT_TIMEOUT)
1400     {
1401         PostThreadMessageW(tid, WM_QUIT, 0, 0);
1402         MsgWaitForMultipleObjects(1, &hthread, FALSE, INFINITE, 0);
1403         param.button = -1;
1404     }
1405     *button = param.button;
1406 
1407     VariantClear(&param.title);
1408     CloseHandle(hthread);
1409 
1410     return S_OK;
1411 }
1412 
1413 static HRESULT WINAPI WshShell3_CreateShortcut(IWshShell3 *iface, BSTR PathLink, IDispatch** Shortcut)
1414 {
1415     TRACE("(%s %p)\n", debugstr_w(PathLink), Shortcut);
1416     return WshShortcut_Create(PathLink, Shortcut);
1417 }
1418 
1419 static HRESULT WINAPI WshShell3_ExpandEnvironmentStrings(IWshShell3 *iface, BSTR Src, BSTR* Dst)
1420 {
1421     DWORD ret;
1422 
1423     TRACE("(%s %p)\n", debugstr_w(Src), Dst);
1424 
1425     if (!Src || !Dst) return E_POINTER;
1426 
1427     ret = ExpandEnvironmentStringsW(Src, NULL, 0);
1428     *Dst = SysAllocStringLen(NULL, ret);
1429     if (!*Dst) return E_OUTOFMEMORY;
1430 
1431     if (ExpandEnvironmentStringsW(Src, *Dst, ret))
1432         return S_OK;
1433     else
1434     {
1435         SysFreeString(*Dst);
1436         *Dst = NULL;
1437         return HRESULT_FROM_WIN32(GetLastError());
1438     }
1439 }
1440 
1441 static HKEY get_root_key(const WCHAR *path)
1442 {
1443     static const struct {
1444         const WCHAR full[20];
1445         const WCHAR abbrev[5];
1446         HKEY hkey;
1447     } rootkeys[] = {
1448         { {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0},     {'H','K','C','U',0}, HKEY_CURRENT_USER },
1449         { {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',0}, {'H','K','L','M',0}, HKEY_LOCAL_MACHINE },
1450         { {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0},     {'H','K','C','R',0}, HKEY_CLASSES_ROOT },
1451         { {'H','K','E','Y','_','U','S','E','R','S',0},                                                 {0}, HKEY_USERS },
1452         { {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0},             {0}, HKEY_CURRENT_CONFIG }
1453     };
1454     int i;
1455 
1456     for (i = 0; i < ARRAY_SIZE(rootkeys); i++) {
1457         if (!wcsncmp(path, rootkeys[i].full, lstrlenW(rootkeys[i].full)))
1458             return rootkeys[i].hkey;
1459         if (rootkeys[i].abbrev[0] && !wcsncmp(path, rootkeys[i].abbrev, lstrlenW(rootkeys[i].abbrev)))
1460             return rootkeys[i].hkey;
1461     }
1462 
1463     return NULL;
1464 }
1465 
1466 /* Caller is responsible to free 'subkey' if 'value' is not NULL */
1467 static HRESULT split_reg_path(const WCHAR *path, WCHAR **subkey, WCHAR **value)
1468 {
1469     *value = NULL;
1470 
1471     /* at least one separator should be present */
1472     *subkey = wcschr(path, '\\');
1473     if (!*subkey)
1474         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1475 
1476     /* default value or not */
1477     if ((*subkey)[lstrlenW(*subkey)-1] == '\\') {
1478         (*subkey)++;
1479         *value = NULL;
1480     }
1481     else {
1482         *value = wcsrchr(*subkey, '\\');
1483         if (*value - *subkey > 1) {
1484             unsigned int len = *value - *subkey - 1;
1485             WCHAR *ret;
1486 
1487             ret = heap_alloc((len + 1)*sizeof(WCHAR));
1488             if (!ret)
1489                 return E_OUTOFMEMORY;
1490 
1491             memcpy(ret, *subkey + 1, len*sizeof(WCHAR));
1492             ret[len] = 0;
1493             *subkey = ret;
1494         }
1495         (*value)++;
1496     }
1497 
1498     return S_OK;
1499 }
1500 
1501 static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR name, VARIANT *value)
1502 {
1503     DWORD type, datalen, ret;
1504     WCHAR *subkey, *val;
1505     HRESULT hr;
1506     HKEY root;
1507 
1508     TRACE("(%s %p)\n", debugstr_w(name), value);
1509 
1510     if (!name || !value)
1511         return E_POINTER;
1512 
1513     root = get_root_key(name);
1514     if (!root)
1515         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1516 
1517     hr = split_reg_path(name, &subkey, &val);
1518     if (FAILED(hr))
1519         return hr;
1520 
1521     type = REG_NONE;
1522     datalen = 0;
1523     ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, NULL, &datalen);
1524     if (ret == ERROR_SUCCESS) {
1525         void *data;
1526 
1527         data = heap_alloc(datalen);
1528         if (!data) {
1529             hr = E_OUTOFMEMORY;
1530             goto fail;
1531         }
1532 
1533         ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, data, &datalen);
1534         if (ret) {
1535             heap_free(data);
1536             hr = HRESULT_FROM_WIN32(ret);
1537             goto fail;
1538         }
1539 
1540         switch (type) {
1541         case REG_SZ:
1542         case REG_EXPAND_SZ:
1543             V_VT(value) = VT_BSTR;
1544             V_BSTR(value) = SysAllocString((WCHAR*)data);
1545             if (!V_BSTR(value))
1546                 hr = E_OUTOFMEMORY;
1547             break;
1548         case REG_DWORD:
1549             V_VT(value) = VT_I4;
1550             V_I4(value) = *(DWORD*)data;
1551             break;
1552         case REG_BINARY:
1553         {
1554             BYTE *ptr = (BYTE*)data;
1555             SAFEARRAYBOUND bound;
1556             unsigned int i;
1557             SAFEARRAY *sa;
1558             VARIANT *v;
1559 
1560             bound.lLbound = 0;
1561             bound.cElements = datalen;
1562             sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
1563             if (!sa)
1564                 break;
1565 
1566             hr = SafeArrayAccessData(sa, (void**)&v);
1567             if (FAILED(hr)) {
1568                 SafeArrayDestroy(sa);
1569                 break;
1570             }
1571 
1572             for (i = 0; i < datalen; i++) {
1573                 V_VT(&v[i]) = VT_UI1;
1574                 V_UI1(&v[i]) = ptr[i];
1575             }
1576             SafeArrayUnaccessData(sa);
1577 
1578             V_VT(value) = VT_ARRAY|VT_VARIANT;
1579             V_ARRAY(value) = sa;
1580             break;
1581         }
1582         case REG_MULTI_SZ:
1583         {
1584             WCHAR *ptr = (WCHAR*)data;
1585             SAFEARRAYBOUND bound;
1586             SAFEARRAY *sa;
1587             VARIANT *v;
1588 
1589             /* get element count first */
1590             bound.lLbound = 0;
1591             bound.cElements = 0;
1592             while (*ptr) {
1593                 bound.cElements++;
1594                 ptr += lstrlenW(ptr)+1;
1595             }
1596 
1597             sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
1598             if (!sa)
1599                 break;
1600 
1601             hr = SafeArrayAccessData(sa, (void**)&v);
1602             if (FAILED(hr)) {
1603                 SafeArrayDestroy(sa);
1604                 break;
1605             }
1606 
1607             ptr = (WCHAR*)data;
1608             while (*ptr) {
1609                 V_VT(v) = VT_BSTR;
1610                 V_BSTR(v) = SysAllocString(ptr);
1611                 ptr += lstrlenW(ptr)+1;
1612                 v++;
1613             }
1614 
1615             SafeArrayUnaccessData(sa);
1616             V_VT(value) = VT_ARRAY|VT_VARIANT;
1617             V_ARRAY(value) = sa;
1618             break;
1619         }
1620         default:
1621             FIXME("value type %d not supported\n", type);
1622             hr = E_FAIL;
1623         };
1624 
1625         heap_free(data);
1626         if (FAILED(hr))
1627             VariantInit(value);
1628     }
1629     else
1630         hr = HRESULT_FROM_WIN32(ret);
1631 
1632 fail:
1633     if (val)
1634         heap_free(subkey);
1635     return hr;
1636 }
1637 
1638 static HRESULT WINAPI WshShell3_RegWrite(IWshShell3 *iface, BSTR name, VARIANT *value, VARIANT *type)
1639 {
1640     static const WCHAR regexpandszW[] = {'R','E','G','_','E','X','P','A','N','D','_','S','Z',0};
1641     static const WCHAR regszW[] = {'R','E','G','_','S','Z',0};
1642     static const WCHAR regdwordW[] = {'R','E','G','_','D','W','O','R','D',0};
1643     static const WCHAR regbinaryW[] = {'R','E','G','_','B','I','N','A','R','Y',0};
1644 
1645     DWORD regtype, data_len;
1646     WCHAR *subkey, *val;
1647     const BYTE *data;
1648     HRESULT hr;
1649     HKEY root;
1650     VARIANT v;
1651     LONG ret;
1652 
1653     TRACE("(%s %s %s)\n", debugstr_w(name), debugstr_variant(value), debugstr_variant(type));
1654 
1655     if (!name || !value || !type)
1656         return E_POINTER;
1657 
1658     root = get_root_key(name);
1659     if (!root)
1660         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1661 
1662     /* value type */
1663     if (is_optional_argument(type))
1664         regtype = REG_SZ;
1665     else {
1666         if (V_VT(type) != VT_BSTR)
1667             return E_INVALIDARG;
1668 
1669         if (!wcscmp(V_BSTR(type), regszW))
1670             regtype = REG_SZ;
1671         else if (!wcscmp(V_BSTR(type), regdwordW))
1672             regtype = REG_DWORD;
1673         else if (!wcscmp(V_BSTR(type), regexpandszW))
1674             regtype = REG_EXPAND_SZ;
1675         else if (!wcscmp(V_BSTR(type), regbinaryW))
1676             regtype = REG_BINARY;
1677         else {
1678             FIXME("unrecognized value type %s\n", debugstr_w(V_BSTR(type)));
1679             return E_FAIL;
1680         }
1681     }
1682 
1683     /* it's always a string or a DWORD */
1684     VariantInit(&v);
1685     switch (regtype)
1686     {
1687     case REG_SZ:
1688     case REG_EXPAND_SZ:
1689         hr = VariantChangeType(&v, value, 0, VT_BSTR);
1690         if (hr == S_OK) {
1691             data = (BYTE*)V_BSTR(&v);
1692             data_len = SysStringByteLen(V_BSTR(&v)) + sizeof(WCHAR);
1693         }
1694         break;
1695     case REG_DWORD:
1696     case REG_BINARY:
1697         hr = VariantChangeType(&v, value, 0, VT_I4);
1698         data = (BYTE*)&V_I4(&v);
1699         data_len = sizeof(DWORD);
1700         break;
1701     default:
1702         FIXME("unexpected regtype %d\n", regtype);
1703         return E_FAIL;
1704     };
1705 
1706     if (FAILED(hr)) {
1707         FIXME("failed to convert value, regtype %d, 0x%08x\n", regtype, hr);
1708         return hr;
1709     }
1710 
1711     hr = split_reg_path(name, &subkey, &val);
1712     if (FAILED(hr))
1713         goto fail;
1714 
1715     ret = RegSetKeyValueW(root, subkey, val, regtype, data, data_len);
1716     if (ret)
1717         hr = HRESULT_FROM_WIN32(ret);
1718 
1719 fail:
1720     VariantClear(&v);
1721     if (val)
1722         heap_free(subkey);
1723     return hr;
1724 }
1725 
1726 static HRESULT WINAPI WshShell3_RegDelete(IWshShell3 *iface, BSTR Name)
1727 {
1728     FIXME("(%s): stub\n", debugstr_w(Name));
1729     return E_NOTIMPL;
1730 }
1731 
1732 static HRESULT WINAPI WshShell3_LogEvent(IWshShell3 *iface, VARIANT *Type, BSTR Message, BSTR Target, VARIANT_BOOL *out_Success)
1733 {
1734     FIXME("(%s %s %s %p): stub\n", debugstr_variant(Type), debugstr_w(Message), debugstr_w(Target), out_Success);
1735     return E_NOTIMPL;
1736 }
1737 
1738 static HRESULT WINAPI WshShell3_AppActivate(IWshShell3 *iface, VARIANT *App, VARIANT *Wait, VARIANT_BOOL *out_Success)
1739 {
1740     FIXME("(%s %s %p): stub\n", debugstr_variant(App), debugstr_variant(Wait), out_Success);
1741     return E_NOTIMPL;
1742 }
1743 
1744 static HRESULT WINAPI WshShell3_SendKeys(IWshShell3 *iface, BSTR Keys, VARIANT *Wait)
1745 {
1746     FIXME("(%s %p): stub\n", debugstr_w(Keys), Wait);
1747     return E_NOTIMPL;
1748 }
1749 
1750 static HRESULT WINAPI WshShell3_Exec(IWshShell3 *iface, BSTR command, IWshExec **ret)
1751 {
1752     TRACE("(%s %p)\n", debugstr_w(command), ret);
1753 
1754     if (!ret)
1755         return E_POINTER;
1756 
1757     if (!command)
1758         return DISP_E_EXCEPTION;
1759 
1760     return WshExec_create(command, ret);
1761 }
1762 
1763 static HRESULT WINAPI WshShell3_get_CurrentDirectory(IWshShell3 *iface, BSTR *dir)
1764 {
1765     DWORD ret;
1766 
1767     TRACE("(%p)\n", dir);
1768 
1769     ret = GetCurrentDirectoryW(0, NULL);
1770     if (!ret)
1771         return HRESULT_FROM_WIN32(GetLastError());
1772 
1773     *dir = SysAllocStringLen(NULL, ret-1);
1774     if (!*dir)
1775         return E_OUTOFMEMORY;
1776 
1777     ret = GetCurrentDirectoryW(ret, *dir);
1778     if (!ret) {
1779         SysFreeString(*dir);
1780         *dir = NULL;
1781         return HRESULT_FROM_WIN32(GetLastError());
1782     }
1783 
1784     return S_OK;
1785 }
1786 
1787 static HRESULT WINAPI WshShell3_put_CurrentDirectory(IWshShell3 *iface, BSTR dir)
1788 {
1789     TRACE("(%s)\n", debugstr_w(dir));
1790 
1791     if (!dir)
1792         return E_INVALIDARG;
1793 
1794     if (!SetCurrentDirectoryW(dir))
1795         return HRESULT_FROM_WIN32(GetLastError());
1796 
1797     return S_OK;
1798 }
1799 
1800 static const IWshShell3Vtbl WshShell3Vtbl = {
1801     WshShell3_QueryInterface,
1802     WshShell3_AddRef,
1803     WshShell3_Release,
1804     WshShell3_GetTypeInfoCount,
1805     WshShell3_GetTypeInfo,
1806     WshShell3_GetIDsOfNames,
1807     WshShell3_Invoke,
1808     WshShell3_get_SpecialFolders,
1809     WshShell3_get_Environment,
1810     WshShell3_Run,
1811     WshShell3_Popup,
1812     WshShell3_CreateShortcut,
1813     WshShell3_ExpandEnvironmentStrings,
1814     WshShell3_RegRead,
1815     WshShell3_RegWrite,
1816     WshShell3_RegDelete,
1817     WshShell3_LogEvent,
1818     WshShell3_AppActivate,
1819     WshShell3_SendKeys,
1820     WshShell3_Exec,
1821     WshShell3_get_CurrentDirectory,
1822     WshShell3_put_CurrentDirectory
1823 };
1824 
1825 HRESULT WINAPI WshShellFactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
1826 {
1827     TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv);
1828 
1829     WshShell3.IWshShell3_iface.lpVtbl = &WshShell3Vtbl;
1830     init_classinfo(&IID_IWshShell3, (IUnknown *)&WshShell3.IWshShell3_iface, &WshShell3.classinfo);
1831     return IWshShell3_QueryInterface(&WshShell3.IWshShell3_iface, riid, ppv);
1832 }
1833