[WSHOM.OCX] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / wshom.ocx / shell.c
index da575aa..49f1771 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#ifdef __REACTOS__
+#include <wchar.h>
+#endif
+
 #include "wshom_private.h"
+#include "wshom.h"
+
+#include "shellapi.h"
+#include "shlobj.h"
+#include "dispex.h"
 
-#include <shellapi.h>
-#include <shlobj.h>
-#include <dispex.h>
+#include "wine/debug.h"
+#include "wine/heap.h"
 
-#include <wine/unicode.h>
+WINE_DEFAULT_DEBUG_CHANNEL(wshom);
 
-static IWshShell3 WshShell3;
+typedef struct
+{
+    struct provideclassinfo classinfo;
+    IWshShell3 IWshShell3_iface;
+} WshShellImpl;
+static WshShellImpl WshShell3;
 
 typedef struct
 {
+    struct provideclassinfo classinfo;
     IWshCollection IWshCollection_iface;
     LONG ref;
 } WshCollection;
 
 typedef struct
 {
+    struct provideclassinfo classinfo;
     IWshShortcut IWshShortcut_iface;
     LONG ref;
 
@@ -43,10 +58,19 @@ typedef struct
 
 typedef struct
 {
+    struct provideclassinfo classinfo;
     IWshEnvironment IWshEnvironment_iface;
     LONG ref;
 } WshEnvironment;
 
+typedef struct
+{
+    struct provideclassinfo classinfo;
+    IWshExec IWshExec_iface;
+    LONG ref;
+    PROCESS_INFORMATION info;
+} WshExecImpl;
+
 static inline WshCollection *impl_from_IWshCollection( IWshCollection *iface )
 {
     return CONTAINING_RECORD(iface, WshCollection, IWshCollection_iface);
@@ -62,6 +86,266 @@ static inline WshEnvironment *impl_from_IWshEnvironment( IWshEnvironment *iface
     return CONTAINING_RECORD(iface, WshEnvironment, IWshEnvironment_iface);
 }
 
+static inline WshExecImpl *impl_from_IWshExec( IWshExec *iface )
+{
+    return CONTAINING_RECORD(iface, WshExecImpl, IWshExec_iface);
+}
+
+static HRESULT WINAPI WshExec_QueryInterface(IWshExec *iface, REFIID riid, void **obj)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
+
+    if (IsEqualGUID(riid, &IID_IDispatch) ||
+        IsEqualGUID(riid, &IID_IWshExec) ||
+        IsEqualGUID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+    }
+    else if (IsEqualIID(riid, &IID_IProvideClassInfo))
+    {
+        *obj = &This->classinfo.IProvideClassInfo_iface;
+    }
+    else {
+        FIXME("Unknown iface %s\n", debugstr_guid(riid));
+        *obj = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown *)*obj);
+    return S_OK;
+}
+
+static ULONG WINAPI WshExec_AddRef(IWshExec *iface)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    LONG ref = InterlockedIncrement(&This->ref);
+    TRACE("(%p) ref = %d\n", This, ref);
+    return ref;
+}
+
+static ULONG WINAPI WshExec_Release(IWshExec *iface)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    LONG ref = InterlockedDecrement(&This->ref);
+    TRACE("(%p) ref = %d\n", This, ref);
+
+    if (!ref) {
+        CloseHandle(This->info.hThread);
+        CloseHandle(This->info.hProcess);
+        heap_free(This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI WshExec_GetTypeInfoCount(IWshExec *iface, UINT *pctinfo)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    TRACE("(%p)->(%p)\n", This, pctinfo);
+    *pctinfo = 1;
+    return S_OK;
+}
+
+static HRESULT WINAPI WshExec_GetTypeInfo(IWshExec *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
+    return get_typeinfo(IWshExec_tid, ppTInfo);
+}
+
+static HRESULT WINAPI WshExec_GetIDsOfNames(IWshExec *iface, REFIID riid, LPOLESTR *rgszNames,
+        UINT cNames, LCID lcid, DISPID *rgDispId)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    ITypeInfo *typeinfo;
+    HRESULT hr;
+
+    TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
+
+    hr = get_typeinfo(IWshExec_tid, &typeinfo);
+    if(SUCCEEDED(hr))
+    {
+        hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
+        ITypeInfo_Release(typeinfo);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI WshExec_Invoke(IWshExec *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
+        WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    ITypeInfo *typeinfo;
+    HRESULT hr;
+
+    TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
+          lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+
+    hr = get_typeinfo(IWshExec_tid, &typeinfo);
+    if(SUCCEEDED(hr))
+    {
+        hr = ITypeInfo_Invoke(typeinfo, &This->IWshExec_iface, dispIdMember, wFlags,
+                pDispParams, pVarResult, pExcepInfo, puArgErr);
+        ITypeInfo_Release(typeinfo);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI WshExec_get_Status(IWshExec *iface, WshExecStatus *status)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    DWORD code;
+
+    TRACE("(%p)->(%p)\n", This, status);
+
+    if (!status)
+        return E_INVALIDARG;
+
+    if (!GetExitCodeProcess(This->info.hProcess, &code))
+        return HRESULT_FROM_WIN32(GetLastError());
+
+    switch (code)
+    {
+    case 0:
+        *status = WshFinished;
+        break;
+    case STILL_ACTIVE:
+        *status = WshRunning;
+        break;
+    default:
+        *status = WshFailed;
+    }
+
+    return S_OK;
+}
+
+static HRESULT WINAPI WshExec_get_StdIn(IWshExec *iface, ITextStream **stream)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    FIXME("(%p)->(%p): stub\n", This, stream);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI WshExec_get_StdOut(IWshExec *iface, ITextStream **stream)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    FIXME("(%p)->(%p): stub\n", This, stream);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI WshExec_get_StdErr(IWshExec *iface, ITextStream **stream)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    FIXME("(%p)->(%p): stub\n", This, stream);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI WshExec_get_ProcessID(IWshExec *iface, DWORD *pid)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    TRACE("(%p)->(%p)\n", This, pid);
+
+    if (!pid)
+        return E_INVALIDARG;
+
+    *pid = This->info.dwProcessId;
+    return S_OK;
+}
+
+static HRESULT WINAPI WshExec_get_ExitCode(IWshExec *iface, DWORD *code)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+
+    FIXME("(%p)->(%p): stub\n", This, code);
+
+    return E_NOTIMPL;
+}
+
+static BOOL CALLBACK enum_thread_wnd_proc(HWND hwnd, LPARAM lParam)
+{
+    INT *count = (INT*)lParam;
+
+    (*count)++;
+    PostMessageW(hwnd, WM_CLOSE, 0, 0);
+    /* try to send it to all windows, even if failed for some */
+    return TRUE;
+}
+
+static HRESULT WINAPI WshExec_Terminate(IWshExec *iface)
+{
+    WshExecImpl *This = impl_from_IWshExec(iface);
+    BOOL ret, kill = FALSE;
+    INT count = 0;
+
+    TRACE("(%p)\n", This);
+
+    ret = EnumThreadWindows(This->info.dwThreadId, enum_thread_wnd_proc, (LPARAM)&count);
+    if (ret && count) {
+        /* manual testing shows that it waits 2 seconds before forcing termination */
+        if (WaitForSingleObject(This->info.hProcess, 2000) != WAIT_OBJECT_0)
+            kill = TRUE;
+    }
+    else
+        kill = TRUE;
+
+    if (kill)
+        TerminateProcess(This->info.hProcess, 0);
+
+    return S_OK;
+}
+
+static const IWshExecVtbl WshExecVtbl = {
+    WshExec_QueryInterface,
+    WshExec_AddRef,
+    WshExec_Release,
+    WshExec_GetTypeInfoCount,
+    WshExec_GetTypeInfo,
+    WshExec_GetIDsOfNames,
+    WshExec_Invoke,
+    WshExec_get_Status,
+    WshExec_get_StdIn,
+    WshExec_get_StdOut,
+    WshExec_get_StdErr,
+    WshExec_get_ProcessID,
+    WshExec_get_ExitCode,
+    WshExec_Terminate
+};
+
+static HRESULT WshExec_create(BSTR command, IWshExec **ret)
+{
+    STARTUPINFOW si = {0};
+    WshExecImpl *This;
+
+    *ret = NULL;
+
+    This = heap_alloc(sizeof(*This));
+    if (!This)
+        return E_OUTOFMEMORY;
+
+    This->IWshExec_iface.lpVtbl = &WshExecVtbl;
+    This->ref = 1;
+
+    if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &This->info)) {
+        heap_free(This);
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    init_classinfo(&CLSID_WshExec, (IUnknown *)&This->IWshExec_iface, &This->classinfo);
+    *ret = &This->IWshExec_iface;
+    return S_OK;
+}
+
 static HRESULT WINAPI WshEnvironment_QueryInterface(IWshEnvironment *iface, REFIID riid, void **obj)
 {
     WshEnvironment *This = impl_from_IWshEnvironment(iface);
@@ -73,7 +357,12 @@ static HRESULT WINAPI WshEnvironment_QueryInterface(IWshEnvironment *iface, REFI
         IsEqualGUID(riid, &IID_IWshEnvironment))
     {
         *obj = iface;
-    }else {
+    }
+    else if (IsEqualIID(riid, &IID_IProvideClassInfo))
+    {
+        *obj = &This->classinfo.IProvideClassInfo_iface;
+    }
+    else {
         FIXME("Unknown iface %s\n", debugstr_guid(riid));
         *obj = NULL;
         return E_NOINTERFACE;
@@ -98,7 +387,7 @@ static ULONG WINAPI WshEnvironment_Release(IWshEnvironment *iface)
     TRACE("(%p) ref = %d\n", This, ref);
 
     if (!ref)
-        HeapFree(GetProcessHeap(), 0, This);
+        heap_free(This);
 
     return ref;
 }
@@ -169,14 +458,16 @@ static HRESULT WINAPI WshEnvironment_get_Item(IWshEnvironment *iface, BSTR name,
         return E_POINTER;
 
     len = GetEnvironmentVariableW(name, NULL, 0);
-    *value = SysAllocStringLen(NULL, len);
-    if (!*value)
-        return E_OUTOFMEMORY;
-
     if (len)
-        GetEnvironmentVariableW(name, *value, len+1);
+    {
+        *value = SysAllocStringLen(NULL, len - 1);
+        if (*value)
+            GetEnvironmentVariableW(name, *value, len);
+    }
+    else
+        *value = SysAllocStringLen(NULL, 0);
 
-    return S_OK;
+    return *value ? S_OK : E_OUTOFMEMORY;
 }
 
 static HRESULT WINAPI WshEnvironment_put_Item(IWshEnvironment *iface, BSTR name, BSTR value)
@@ -234,12 +525,13 @@ static HRESULT WshEnvironment_Create(IWshEnvironment **env)
 {
     WshEnvironment *This;
 
-    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    This = heap_alloc(sizeof(*This));
     if (!This) return E_OUTOFMEMORY;
 
     This->IWshEnvironment_iface.lpVtbl = &WshEnvironmentVtbl;
     This->ref = 1;
 
+    init_classinfo(&IID_IWshEnvironment, (IUnknown *)&This->IWshEnvironment_iface, &This->classinfo);
     *env = &This->IWshEnvironment_iface;
 
     return S_OK;
@@ -256,7 +548,12 @@ static HRESULT WINAPI WshCollection_QueryInterface(IWshCollection *iface, REFIID
         IsEqualGUID(riid, &IID_IWshCollection))
     {
         *ppv = iface;
-    }else {
+    }
+    else if (IsEqualIID(riid, &IID_IProvideClassInfo))
+    {
+        *ppv = &This->classinfo.IProvideClassInfo_iface;
+    }
+    else {
         FIXME("Unknown iface %s\n", debugstr_guid(riid));
         *ppv = NULL;
         return E_NOINTERFACE;
@@ -281,7 +578,7 @@ static ULONG WINAPI WshCollection_Release(IWshCollection *iface)
     TRACE("(%p) ref = %d\n", This, ref);
 
     if (!ref)
-        HeapFree(GetProcessHeap(), 0, This);
+        heap_free(This);
 
     return ref;
 }
@@ -362,11 +659,11 @@ static HRESULT WINAPI WshCollection_Item(IWshCollection *iface, VARIANT *index,
     }
 
     folder = V_BSTR(index);
-    if (!strcmpiW(folder, desktopW))
+    if (!wcsicmp(folder, desktopW))
         kind = CSIDL_DESKTOP;
-    else if (!strcmpiW(folder, allusersdesktopW))
+    else if (!wcsicmp(folder, allusersdesktopW))
         kind = CSIDL_COMMON_DESKTOPDIRECTORY;
-    else if (!strcmpiW(folder, allusersprogramsW))
+    else if (!wcsicmp(folder, allusersprogramsW))
         kind = CSIDL_COMMON_PROGRAMS;
     else
     {
@@ -405,7 +702,7 @@ static HRESULT WINAPI WshCollection_get_length(IWshCollection *iface, LONG *coun
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI WshCollection__NewEnum(IWshCollection *iface, IUnknown *Enum)
+static HRESULT WINAPI WshCollection__NewEnum(IWshCollection *iface, IUnknown **Enum)
 {
     WshCollection *This = impl_from_IWshCollection(iface);
     FIXME("(%p)->(%p): stub\n", This, Enum);
@@ -430,12 +727,13 @@ static HRESULT WshCollection_Create(IWshCollection **collection)
 {
     WshCollection *This;
 
-    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    This = heap_alloc(sizeof(*This));
     if (!This) return E_OUTOFMEMORY;
 
     This->IWshCollection_iface.lpVtbl = &WshCollectionVtbl;
     This->ref = 1;
 
+    init_classinfo(&IID_IWshCollection, (IUnknown *)&This->IWshCollection_iface, &This->classinfo);
     *collection = &This->IWshCollection_iface;
 
     return S_OK;
@@ -453,7 +751,12 @@ static HRESULT WINAPI WshShortcut_QueryInterface(IWshShortcut *iface, REFIID rii
         IsEqualGUID(riid, &IID_IWshShortcut))
     {
         *ppv = iface;
-    }else {
+    }
+    else if (IsEqualIID(riid, &IID_IProvideClassInfo))
+    {
+        *ppv = &This->classinfo.IProvideClassInfo_iface;
+    }
+    else {
         FIXME("Unknown iface %s\n", debugstr_guid(riid));
         *ppv = NULL;
         return E_NOINTERFACE;
@@ -481,7 +784,7 @@ static ULONG WINAPI WshShortcut_Release(IWshShortcut *iface)
     {
         SysFreeString(This->path_link);
         IShellLinkW_Release(This->link);
-        HeapFree(GetProcessHeap(), 0, This);
+        heap_free(This);
     }
 
     return ref;
@@ -562,7 +865,7 @@ static HRESULT WINAPI WshShortcut_get_Arguments(IWshShortcut *iface, BSTR *Argum
 
     *Arguments = NULL;
 
-    hr = IShellLinkW_GetArguments(This->link, buffW, sizeof(buffW)/sizeof(WCHAR));
+    hr = IShellLinkW_GetArguments(This->link, buffW, ARRAY_SIZE(buffW));
     if (FAILED(hr))
         return hr;
 
@@ -620,10 +923,10 @@ static HRESULT WINAPI WshShortcut_get_IconLocation(IWshShortcut *iface, BSTR *Ic
     if (!IconPath)
         return E_POINTER;
 
-    hr = IShellLinkW_GetIconLocation(This->link, buffW, sizeof(buffW)/sizeof(WCHAR), &icon);
+    hr = IShellLinkW_GetIconLocation(This->link, buffW, ARRAY_SIZE(buffW), &icon);
     if (FAILED(hr)) return hr;
 
-    sprintfW(pathW, fmtW, buffW, icon);
+    swprintf(pathW, fmtW, buffW, icon);
     *IconPath = SysAllocString(pathW);
     if (!*IconPath) return E_OUTOFMEMORY;
 
@@ -641,7 +944,7 @@ static HRESULT WINAPI WshShortcut_put_IconLocation(IWshShortcut *iface, BSTR Ico
     TRACE("(%p)->(%s)\n", This, debugstr_w(IconPath));
 
     /* scan for icon id */
-    ptr = strrchrW(IconPath, ',');
+    ptr = wcsrchr(IconPath, ',');
     if (!ptr)
     {
         WARN("icon index not found\n");
@@ -651,10 +954,10 @@ static HRESULT WINAPI WshShortcut_put_IconLocation(IWshShortcut *iface, BSTR Ico
     path = SysAllocStringLen(IconPath, ptr-IconPath);
 
     /* skip spaces if any */
-    while (isspaceW(*++ptr))
+    while (iswspace(*++ptr))
         ;
 
-    icon = atoiW(ptr);
+    icon = wcstol(ptr, NULL, 10);
 
     hr = IShellLinkW_SetIconLocation(This->link, path, icon);
     SysFreeString(path);
@@ -709,7 +1012,7 @@ static HRESULT WINAPI WshShortcut_get_WorkingDirectory(IWshShortcut *iface, BSTR
         return E_POINTER;
 
     *WorkingDirectory = NULL;
-    hr = IShellLinkW_GetWorkingDirectory(This->link, buffW, sizeof(buffW)/sizeof(WCHAR));
+    hr = IShellLinkW_GetWorkingDirectory(This->link, buffW, ARRAY_SIZE(buffW));
     if (FAILED(hr)) return hr;
 
     *WorkingDirectory = SysAllocString(buffW);
@@ -780,7 +1083,7 @@ static HRESULT WshShortcut_Create(const WCHAR *path, IDispatch **shortcut)
 
     *shortcut = NULL;
 
-    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    This = heap_alloc(sizeof(*This));
     if (!This) return E_OUTOFMEMORY;
 
     This->IWshShortcut_iface.lpVtbl = &WshShortcutVtbl;
@@ -790,7 +1093,7 @@ static HRESULT WshShortcut_Create(const WCHAR *path, IDispatch **shortcut)
             &IID_IShellLinkW, (void**)&This->link);
     if (FAILED(hr))
     {
-        HeapFree(GetProcessHeap(), 0, This);
+        heap_free(This);
         return hr;
     }
 
@@ -798,10 +1101,11 @@ static HRESULT WshShortcut_Create(const WCHAR *path, IDispatch **shortcut)
     if (!This->path_link)
     {
         IShellLinkW_Release(This->link);
-        HeapFree(GetProcessHeap(), 0, This);
+        heap_free(This);
         return E_OUTOFMEMORY;
     }
 
+    init_classinfo(&IID_IWshShortcut, (IUnknown *)&This->IWshShortcut_iface, &This->classinfo);
     *shortcut = (IDispatch*)&This->IWshShortcut_iface;
 
     return S_OK;
@@ -813,9 +1117,11 @@ static HRESULT WINAPI WshShell3_QueryInterface(IWshShell3 *iface, REFIID riid, v
 
     *ppv = NULL;
 
-    if(IsEqualGUID(riid, &IID_IUnknown)  ||
-       IsEqualGUID(riid, &IID_IDispatch) ||
-       IsEqualGUID(riid, &IID_IWshShell3))
+    if (IsEqualGUID(riid, &IID_IDispatch) ||
+        IsEqualGUID(riid, &IID_IWshShell3) ||
+        IsEqualGUID(riid, &IID_IWshShell2) ||
+        IsEqualGUID(riid, &IID_IWshShell) ||
+        IsEqualGUID(riid, &IID_IUnknown))
     {
         *ppv = iface;
     }
@@ -823,13 +1129,17 @@ static HRESULT WINAPI WshShell3_QueryInterface(IWshShell3 *iface, REFIID riid, v
     {
         return E_NOINTERFACE;
     }
+    else if (IsEqualIID(riid, &IID_IProvideClassInfo))
+    {
+        *ppv = &WshShell3.classinfo.IProvideClassInfo_iface;
+    }
     else
     {
-        FIXME("Unknown iface %s\n", debugstr_guid(riid));
+        WARN("unknown iface %s\n", debugstr_guid(riid));
         return E_NOINTERFACE;
     }
 
-    IWshShell3_AddRef(iface);
+    IUnknown_AddRef((IUnknown *)*ppv);
     return S_OK;
 }
 
@@ -888,7 +1198,7 @@ static HRESULT WINAPI WshShell3_Invoke(IWshShell3 *iface, DISPID dispIdMember, R
     hr = get_typeinfo(IWshShell3_tid, &typeinfo);
     if(SUCCEEDED(hr))
     {
-        hr = ITypeInfo_Invoke(typeinfo, &WshShell3, dispIdMember, wFlags,
+        hr = ITypeInfo_Invoke(typeinfo, &WshShell3.IWshShell3_iface, dispIdMember, wFlags,
                 pDispParams, pVarResult, pExcepInfo, puArgErr);
         ITypeInfo_Release(typeinfo);
     }
@@ -908,14 +1218,47 @@ static HRESULT WINAPI WshShell3_get_Environment(IWshShell3 *iface, VARIANT *type
     return WshEnvironment_Create(env);
 }
 
-static HRESULT WINAPI WshShell3_Run(IWshShell3 *iface, BSTR cmd, VARIANT *style, VARIANT *WaitOnReturn, int *exit_code)
+static inline BOOL is_optional_argument(const VARIANT *arg)
+{
+    return V_VT(arg) == VT_ERROR && V_ERROR(arg) == DISP_E_PARAMNOTFOUND;
+}
+
+static WCHAR *split_command( BSTR cmd, WCHAR **params )
+{
+    WCHAR *ret, *ptr;
+    BOOL in_quotes = FALSE;
+
+    if (!(ret = heap_alloc((lstrlenW(cmd) + 1) * sizeof(WCHAR)))) return NULL;
+    lstrcpyW( ret, cmd );
+
+    *params = NULL;
+    for (ptr = ret; *ptr; ptr++)
+    {
+        if (*ptr == '"') in_quotes = !in_quotes;
+        else if (*ptr == ' ' && !in_quotes)
+        {
+            *ptr = 0;
+            *params = ptr + 1;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static HRESULT WINAPI WshShell3_Run(IWshShell3 *iface, BSTR cmd, VARIANT *style, VARIANT *wait, DWORD *exit_code)
 {
     SHELLEXECUTEINFOW info;
     int waitforprocess;
-    VARIANT s, w;
+    WCHAR *file, *params;
+    VARIANT s;
     HRESULT hr;
+    BOOL ret;
+
+    TRACE("(%s %s %s %p)\n", debugstr_w(cmd), debugstr_variant(style), debugstr_variant(wait), exit_code);
 
-    TRACE("(%s %s %s %p)\n", debugstr_w(cmd), debugstr_variant(style), debugstr_variant(WaitOnReturn), exit_code);
+    if (!style || !wait || !exit_code)
+        return E_POINTER;
 
     VariantInit(&s);
     hr = VariantChangeType(&s, style, 0, VT_I4);
@@ -925,24 +1268,31 @@ static HRESULT WINAPI WshShell3_Run(IWshShell3 *iface, BSTR cmd, VARIANT *style,
         return hr;
     }
 
-    VariantInit(&w);
-    hr = VariantChangeType(&w, WaitOnReturn, 0, VT_I4);
-    if (FAILED(hr))
-    {
-        ERR("failed to convert wait argument, 0x%08x\n", hr);
-        return hr;
+    if (is_optional_argument(wait))
+        waitforprocess = 0;
+    else {
+        VARIANT w;
+
+        VariantInit(&w);
+        hr = VariantChangeType(&w, wait, 0, VT_I4);
+        if (FAILED(hr))
+            return hr;
+
+        waitforprocess = V_I4(&w);
     }
 
+    if (!(file = split_command(cmd, &params))) return E_OUTOFMEMORY;
+
     memset(&info, 0, sizeof(info));
     info.cbSize = sizeof(info);
-
-    waitforprocess = V_I4(&w);
-
     info.fMask = waitforprocess ? SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS : SEE_MASK_DEFAULT;
-    info.lpFile = cmd;
+    info.lpFile = file;
+    info.lpParameters = params;
     info.nShow = V_I4(&s);
 
-    if (!ShellExecuteExW(&info))
+    ret = ShellExecuteExW(&info);
+    heap_free( file );
+    if (!ret)
     {
         TRACE("ShellExecute failed, %d\n", GetLastError());
         return HRESULT_FROM_WIN32(GetLastError());
@@ -951,26 +1301,104 @@ static HRESULT WINAPI WshShell3_Run(IWshShell3 *iface, BSTR cmd, VARIANT *style,
     {
         if (waitforprocess)
         {
-            if (exit_code)
-            {
-                DWORD code;
-                GetExitCodeProcess(info.hProcess, &code);
-                *exit_code = code;
-            }
+            WaitForSingleObject(info.hProcess, INFINITE);
+            GetExitCodeProcess(info.hProcess, exit_code);
             CloseHandle(info.hProcess);
         }
         else
-            if (exit_code) *exit_code = 0;
+            *exit_code = 0;
 
         return S_OK;
     }
 }
 
-static HRESULT WINAPI WshShell3_Popup(IWshShell3 *iface, BSTR Text, VARIANT* SecondsToWait, VARIANT *Title, VARIANT *Type, int *button)
+struct popup_thread_param
 {
-    FIXME("(%s %s %s %s %p): stub\n", debugstr_w(Text), debugstr_variant(SecondsToWait),
-        debugstr_variant(Title), debugstr_variant(Type), button);
-    return E_NOTIMPL;
+    WCHAR *text;
+    VARIANT title;
+    VARIANT type;
+    INT button;
+};
+
+static DWORD WINAPI popup_thread_proc(void *arg)
+{
+    static const WCHAR defaulttitleW[] = {'W','i','n','d','o','w','s',' ','S','c','r','i','p','t',' ','H','o','s','t',0};
+    struct popup_thread_param *param = (struct popup_thread_param *)arg;
+
+    param->button = MessageBoxW(NULL, param->text, is_optional_argument(&param->title) ?
+            defaulttitleW : V_BSTR(&param->title), V_I4(&param->type));
+    return 0;
+}
+
+static HRESULT WINAPI WshShell3_Popup(IWshShell3 *iface, BSTR text, VARIANT *seconds_to_wait, VARIANT *title,
+        VARIANT *type, int *button)
+{
+    struct popup_thread_param param;
+    DWORD tid, status;
+    VARIANT timeout;
+    HANDLE hthread;
+    HRESULT hr;
+
+    TRACE("(%s %s %s %s %p)\n", debugstr_w(text), debugstr_variant(seconds_to_wait), debugstr_variant(title),
+        debugstr_variant(type), button);
+
+    if (!seconds_to_wait || !title || !type || !button)
+        return E_POINTER;
+
+    VariantInit(&timeout);
+    if (!is_optional_argument(seconds_to_wait))
+    {
+        hr = VariantChangeType(&timeout, seconds_to_wait, 0, VT_I4);
+        if (FAILED(hr))
+            return hr;
+    }
+#ifdef __REACTOS__
+    else
+    {
+        VariantChangeType(&timeout, &timeout, 0, VT_I4);
+    }
+#endif
+
+    VariantInit(&param.type);
+    if (!is_optional_argument(type))
+    {
+        hr = VariantChangeType(&param.type, type, 0, VT_I4);
+        if (FAILED(hr))
+            return hr;
+    }
+#ifdef __REACTOS__
+    else
+    {
+        VariantChangeType(&param.type, &param.type, 0, VT_I4);
+    }
+#endif
+
+    if (is_optional_argument(title))
+        param.title = *title;
+    else
+    {
+        VariantInit(&param.title);
+        hr = VariantChangeType(&param.title, title, 0, VT_BSTR);
+        if (FAILED(hr))
+            return hr;
+    }
+
+    param.text = text;
+    param.button = -1;
+    hthread = CreateThread(NULL, 0, popup_thread_proc, &param, 0, &tid);
+    status = MsgWaitForMultipleObjects(1, &hthread, FALSE, V_I4(&timeout) ? V_I4(&timeout) * 1000: INFINITE, 0);
+    if (status == WAIT_TIMEOUT)
+    {
+        PostThreadMessageW(tid, WM_QUIT, 0, 0);
+        MsgWaitForMultipleObjects(1, &hthread, FALSE, INFINITE, 0);
+        param.button = -1;
+    }
+    *button = param.button;
+
+    VariantClear(&param.title);
+    CloseHandle(hthread);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI WshShell3_CreateShortcut(IWshShell3 *iface, BSTR PathLink, IDispatch** Shortcut)
@@ -1001,16 +1429,289 @@ static HRESULT WINAPI WshShell3_ExpandEnvironmentStrings(IWshShell3 *iface, BSTR
     }
 }
 
-static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR Name, VARIANT* out_Value)
+static HKEY get_root_key(const WCHAR *path)
+{
+    static const struct {
+        const WCHAR full[20];
+        const WCHAR abbrev[5];
+        HKEY hkey;
+    } rootkeys[] = {
+        { {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0},     {'H','K','C','U',0}, HKEY_CURRENT_USER },
+        { {'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 },
+        { {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0},     {'H','K','C','R',0}, HKEY_CLASSES_ROOT },
+        { {'H','K','E','Y','_','U','S','E','R','S',0},                                                 {0}, HKEY_USERS },
+        { {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0},             {0}, HKEY_CURRENT_CONFIG }
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(rootkeys); i++) {
+        if (!wcsncmp(path, rootkeys[i].full, lstrlenW(rootkeys[i].full)))
+            return rootkeys[i].hkey;
+        if (rootkeys[i].abbrev[0] && !wcsncmp(path, rootkeys[i].abbrev, lstrlenW(rootkeys[i].abbrev)))
+            return rootkeys[i].hkey;
+    }
+
+    return NULL;
+}
+
+/* Caller is responsible to free 'subkey' if 'value' is not NULL */
+static HRESULT split_reg_path(const WCHAR *path, WCHAR **subkey, WCHAR **value)
 {
-    FIXME("(%s %p): stub\n", debugstr_w(Name), out_Value);
-    return E_NOTIMPL;
+    *value = NULL;
+
+    /* at least one separator should be present */
+    *subkey = wcschr(path, '\\');
+    if (!*subkey)
+        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+
+    /* default value or not */
+    if ((*subkey)[lstrlenW(*subkey)-1] == '\\') {
+        (*subkey)++;
+        *value = NULL;
+    }
+    else {
+        *value = wcsrchr(*subkey, '\\');
+        if (*value - *subkey > 1) {
+            unsigned int len = *value - *subkey - 1;
+            WCHAR *ret;
+
+            ret = heap_alloc((len + 1)*sizeof(WCHAR));
+            if (!ret)
+                return E_OUTOFMEMORY;
+
+            memcpy(ret, *subkey + 1, len*sizeof(WCHAR));
+            ret[len] = 0;
+            *subkey = ret;
+        }
+        (*value)++;
+    }
+
+    return S_OK;
 }
 
-static HRESULT WINAPI WshShell3_RegWrite(IWshShell3 *iface, BSTR Name, VARIANT *Value, VARIANT *Type)
+static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR name, VARIANT *value)
 {
-    FIXME("(%s %s %s): stub\n", debugstr_w(Name), debugstr_variant(Value), debugstr_variant(Type));
-    return E_NOTIMPL;
+    DWORD type, datalen, ret;
+    WCHAR *subkey, *val;
+    HRESULT hr;
+    HKEY root;
+
+    TRACE("(%s %p)\n", debugstr_w(name), value);
+
+    if (!name || !value)
+        return E_POINTER;
+
+    root = get_root_key(name);
+    if (!root)
+        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+
+    hr = split_reg_path(name, &subkey, &val);
+    if (FAILED(hr))
+        return hr;
+
+    type = REG_NONE;
+    datalen = 0;
+    ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, NULL, &datalen);
+    if (ret == ERROR_SUCCESS) {
+        void *data;
+
+        data = heap_alloc(datalen);
+        if (!data) {
+            hr = E_OUTOFMEMORY;
+            goto fail;
+        }
+
+        ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, data, &datalen);
+        if (ret) {
+            heap_free(data);
+            hr = HRESULT_FROM_WIN32(ret);
+            goto fail;
+        }
+
+        switch (type) {
+        case REG_SZ:
+        case REG_EXPAND_SZ:
+            V_VT(value) = VT_BSTR;
+            V_BSTR(value) = SysAllocString((WCHAR*)data);
+            if (!V_BSTR(value))
+                hr = E_OUTOFMEMORY;
+            break;
+        case REG_DWORD:
+            V_VT(value) = VT_I4;
+            V_I4(value) = *(DWORD*)data;
+            break;
+        case REG_BINARY:
+        {
+            BYTE *ptr = (BYTE*)data;
+            SAFEARRAYBOUND bound;
+            unsigned int i;
+            SAFEARRAY *sa;
+            VARIANT *v;
+
+            bound.lLbound = 0;
+            bound.cElements = datalen;
+            sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
+            if (!sa)
+                break;
+
+            hr = SafeArrayAccessData(sa, (void**)&v);
+            if (FAILED(hr)) {
+                SafeArrayDestroy(sa);
+                break;
+            }
+
+            for (i = 0; i < datalen; i++) {
+                V_VT(&v[i]) = VT_UI1;
+                V_UI1(&v[i]) = ptr[i];
+            }
+            SafeArrayUnaccessData(sa);
+
+            V_VT(value) = VT_ARRAY|VT_VARIANT;
+            V_ARRAY(value) = sa;
+            break;
+        }
+        case REG_MULTI_SZ:
+        {
+            WCHAR *ptr = (WCHAR*)data;
+            SAFEARRAYBOUND bound;
+            SAFEARRAY *sa;
+            VARIANT *v;
+
+            /* get element count first */
+            bound.lLbound = 0;
+            bound.cElements = 0;
+            while (*ptr) {
+                bound.cElements++;
+                ptr += lstrlenW(ptr)+1;
+            }
+
+            sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
+            if (!sa)
+                break;
+
+            hr = SafeArrayAccessData(sa, (void**)&v);
+            if (FAILED(hr)) {
+                SafeArrayDestroy(sa);
+                break;
+            }
+
+            ptr = (WCHAR*)data;
+            while (*ptr) {
+                V_VT(v) = VT_BSTR;
+                V_BSTR(v) = SysAllocString(ptr);
+                ptr += lstrlenW(ptr)+1;
+                v++;
+            }
+
+            SafeArrayUnaccessData(sa);
+            V_VT(value) = VT_ARRAY|VT_VARIANT;
+            V_ARRAY(value) = sa;
+            break;
+        }
+        default:
+            FIXME("value type %d not supported\n", type);
+            hr = E_FAIL;
+        };
+
+        heap_free(data);
+        if (FAILED(hr))
+            VariantInit(value);
+    }
+    else
+        hr = HRESULT_FROM_WIN32(ret);
+
+fail:
+    if (val)
+        heap_free(subkey);
+    return hr;
+}
+
+static HRESULT WINAPI WshShell3_RegWrite(IWshShell3 *iface, BSTR name, VARIANT *value, VARIANT *type)
+{
+    static const WCHAR regexpandszW[] = {'R','E','G','_','E','X','P','A','N','D','_','S','Z',0};
+    static const WCHAR regszW[] = {'R','E','G','_','S','Z',0};
+    static const WCHAR regdwordW[] = {'R','E','G','_','D','W','O','R','D',0};
+    static const WCHAR regbinaryW[] = {'R','E','G','_','B','I','N','A','R','Y',0};
+
+    DWORD regtype, data_len;
+    WCHAR *subkey, *val;
+    const BYTE *data;
+    HRESULT hr;
+    HKEY root;
+    VARIANT v;
+    LONG ret;
+
+    TRACE("(%s %s %s)\n", debugstr_w(name), debugstr_variant(value), debugstr_variant(type));
+
+    if (!name || !value || !type)
+        return E_POINTER;
+
+    root = get_root_key(name);
+    if (!root)
+        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+
+    /* value type */
+    if (is_optional_argument(type))
+        regtype = REG_SZ;
+    else {
+        if (V_VT(type) != VT_BSTR)
+            return E_INVALIDARG;
+
+        if (!wcscmp(V_BSTR(type), regszW))
+            regtype = REG_SZ;
+        else if (!wcscmp(V_BSTR(type), regdwordW))
+            regtype = REG_DWORD;
+        else if (!wcscmp(V_BSTR(type), regexpandszW))
+            regtype = REG_EXPAND_SZ;
+        else if (!wcscmp(V_BSTR(type), regbinaryW))
+            regtype = REG_BINARY;
+        else {
+            FIXME("unrecognized value type %s\n", debugstr_w(V_BSTR(type)));
+            return E_FAIL;
+        }
+    }
+
+    /* it's always a string or a DWORD */
+    VariantInit(&v);
+    switch (regtype)
+    {
+    case REG_SZ:
+    case REG_EXPAND_SZ:
+        hr = VariantChangeType(&v, value, 0, VT_BSTR);
+        if (hr == S_OK) {
+            data = (BYTE*)V_BSTR(&v);
+            data_len = SysStringByteLen(V_BSTR(&v)) + sizeof(WCHAR);
+        }
+        break;
+    case REG_DWORD:
+    case REG_BINARY:
+        hr = VariantChangeType(&v, value, 0, VT_I4);
+        data = (BYTE*)&V_I4(&v);
+        data_len = sizeof(DWORD);
+        break;
+    default:
+        FIXME("unexpected regtype %d\n", regtype);
+        return E_FAIL;
+    };
+
+    if (FAILED(hr)) {
+        FIXME("failed to convert value, regtype %d, 0x%08x\n", regtype, hr);
+        return hr;
+    }
+
+    hr = split_reg_path(name, &subkey, &val);
+    if (FAILED(hr))
+        goto fail;
+
+    ret = RegSetKeyValueW(root, subkey, val, regtype, data, data_len);
+    if (ret)
+        hr = HRESULT_FROM_WIN32(ret);
+
+fail:
+    VariantClear(&v);
+    if (val)
+        heap_free(subkey);
+    return hr;
 }
 
 static HRESULT WINAPI WshShell3_RegDelete(IWshShell3 *iface, BSTR Name)
@@ -1037,6 +1738,56 @@ static HRESULT WINAPI WshShell3_SendKeys(IWshShell3 *iface, BSTR Keys, VARIANT *
     return E_NOTIMPL;
 }
 
+static HRESULT WINAPI WshShell3_Exec(IWshShell3 *iface, BSTR command, IWshExec **ret)
+{
+    TRACE("(%s %p)\n", debugstr_w(command), ret);
+
+    if (!ret)
+        return E_POINTER;
+
+    if (!command)
+        return DISP_E_EXCEPTION;
+
+    return WshExec_create(command, ret);
+}
+
+static HRESULT WINAPI WshShell3_get_CurrentDirectory(IWshShell3 *iface, BSTR *dir)
+{
+    DWORD ret;
+
+    TRACE("(%p)\n", dir);
+
+    ret = GetCurrentDirectoryW(0, NULL);
+    if (!ret)
+        return HRESULT_FROM_WIN32(GetLastError());
+
+    *dir = SysAllocStringLen(NULL, ret-1);
+    if (!*dir)
+        return E_OUTOFMEMORY;
+
+    ret = GetCurrentDirectoryW(ret, *dir);
+    if (!ret) {
+        SysFreeString(*dir);
+        *dir = NULL;
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    return S_OK;
+}
+
+static HRESULT WINAPI WshShell3_put_CurrentDirectory(IWshShell3 *iface, BSTR dir)
+{
+    TRACE("(%s)\n", debugstr_w(dir));
+
+    if (!dir)
+        return E_INVALIDARG;
+
+    if (!SetCurrentDirectoryW(dir))
+        return HRESULT_FROM_WIN32(GetLastError());
+
+    return S_OK;
+}
+
 static const IWshShell3Vtbl WshShell3Vtbl = {
     WshShell3_QueryInterface,
     WshShell3_AddRef,
@@ -1056,14 +1807,17 @@ static const IWshShell3Vtbl WshShell3Vtbl = {
     WshShell3_RegDelete,
     WshShell3_LogEvent,
     WshShell3_AppActivate,
-    WshShell3_SendKeys
+    WshShell3_SendKeys,
+    WshShell3_Exec,
+    WshShell3_get_CurrentDirectory,
+    WshShell3_put_CurrentDirectory
 };
 
-static IWshShell3 WshShell3 = { &WshShell3Vtbl };
-
 HRESULT WINAPI WshShellFactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
 {
     TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv);
 
-    return IWshShell3_QueryInterface(&WshShell3, riid, ppv);
+    WshShell3.IWshShell3_iface.lpVtbl = &WshShell3Vtbl;
+    init_classinfo(&IID_IWshShell3, (IUnknown *)&WshShell3.IWshShell3_iface, &WshShell3.classinfo);
+    return IWshShell3_QueryInterface(&WshShell3.IWshShell3_iface, riid, ppv);
 }