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