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