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