diff --git a/dlls/wshom.ocx/Makefile.in b/dlls/wshom.ocx/Makefile.in
index 891812ea7a7dc8bfb086b1fcaaa38b5665c19492..18e1b96210bec03f45ef8a452192f51e5e0e6889 100644
--- a/dlls/wshom.ocx/Makefile.in
+++ b/dlls/wshom.ocx/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = wshom.ocx
-IMPORTS   = uuid oleaut32 ole32 shell32
+IMPORTS   = uuid oleaut32 ole32 shell32 advapi32
 
 C_SRCS = \
 	shell.c \
diff --git a/dlls/wshom.ocx/shell.c b/dlls/wshom.ocx/shell.c
index 9bc39cbd9f7c8e383a6ee126ec49509dca277017..02849676470675a5ad087ac8581c2a6b48b85bd2 100644
--- a/dlls/wshom.ocx/shell.c
+++ b/dlls/wshom.ocx/shell.c
@@ -1035,12 +1035,46 @@ static HKEY get_root_key(const WCHAR *path)
     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)
+{
+    *value = NULL;
+
+    /* at least one separator should be present */
+    *subkey = strchrW(path, '\\');
+    if (!*subkey)
+        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+
+    /* default value or not */
+    if ((*subkey)[strlenW(*subkey)-1] == '\\') {
+        (*subkey)++;
+        *value = NULL;
+    }
+    else {
+        *value = strrchrW(*subkey, '\\');
+        if (*value - *subkey > 1) {
+            unsigned int len = *value - *subkey - 1;
+            WCHAR *ret;
+
+            ret = HeapAlloc(GetProcessHeap(), 0, (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_RegRead(IWshShell3 *iface, BSTR name, VARIANT *value)
 {
     DWORD type, datalen, ret;
-    WCHAR *keyname, *val;
-    HRESULT hr = S_OK;
-    BSTR subkey = NULL;
+    WCHAR *subkey, *val;
+    HRESULT hr;
     HKEY root;
 
     TRACE("(%s %p)\n", debugstr_w(name), value);
@@ -1052,28 +1086,13 @@ static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR name, VARIANT *v
     if (!root)
         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
 
-    /* at least one separator should be present */
-    keyname = strchrW(name, '\\');
-    if (!keyname)
-        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-
-    /* default value or not */
-    if (keyname[strlenW(keyname)-1] == '\\') {
-        keyname++;
-        val = NULL;
-    }
-    else {
-        val = strrchrW(keyname, '\\');
-        if (val - keyname > 1) {
-            subkey = SysAllocStringLen(keyname+1, val-keyname-1);
-            keyname = subkey;
-        }
-        val++;
-    }
+    hr = split_reg_path(name, &subkey, &val);
+    if (FAILED(hr))
+        return hr;
 
     type = REG_NONE;
     datalen = 0;
-    ret = RegGetValueW(root, keyname, val, RRF_RT_ANY, &type, NULL, &datalen);
+    ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, NULL, &datalen);
     if (ret == ERROR_SUCCESS) {
         void *data;
 
@@ -1083,7 +1102,7 @@ static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR name, VARIANT *v
             goto fail;
         }
 
-        ret = RegGetValueW(root, keyname, val, RRF_RT_ANY, &type, data, &datalen);
+        ret = RegGetValueW(root, subkey, val, RRF_RT_ANY, &type, data, &datalen);
         if (ret) {
             HeapFree(GetProcessHeap(), 0, data);
             hr = HRESULT_FROM_WIN32(ret);
@@ -1183,14 +1202,97 @@ static HRESULT WINAPI WshShell3_RegRead(IWshShell3 *iface, BSTR name, VARIANT *v
         hr = HRESULT_FROM_WIN32(ret);
 
 fail:
-    SysFreeString(subkey);
+    if (val)
+        HeapFree(GetProcessHeap(), 0, subkey);
     return hr;
 }
 
-static HRESULT WINAPI WshShell3_RegWrite(IWshShell3 *iface, BSTR Name, VARIANT *Value, VARIANT *Type)
+static HRESULT WINAPI WshShell3_RegWrite(IWshShell3 *iface, BSTR name, VARIANT *value, VARIANT *type)
 {
-    FIXME("(%s %s %s): stub\n", debugstr_w(Name), debugstr_variant(Value), debugstr_variant(Type));
-    return E_NOTIMPL;
+    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 (!strcmpW(V_BSTR(type), regszW))
+            regtype = REG_SZ;
+        else if (!strcmpW(V_BSTR(type), regdwordW))
+            regtype = REG_DWORD;
+        else if (!strcmpW(V_BSTR(type), regexpandszW))
+            regtype = REG_EXPAND_SZ;
+        else if (!strcmpW(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)
+        HeapFree(GetProcessHeap(), 0, subkey);
+    return hr;
 }
 
 static HRESULT WINAPI WshShell3_RegDelete(IWshShell3 *iface, BSTR Name)
diff --git a/dlls/wshom.ocx/tests/wshom.c b/dlls/wshom.ocx/tests/wshom.c
index 1521b2b900424e967dbf8d1c55db58cacf012408..a836f106d1cd599a86accdfa8a433b2ac46424f4 100644
--- a/dlls/wshom.ocx/tests/wshom.c
+++ b/dlls/wshom.ocx/tests/wshom.c
@@ -240,10 +240,10 @@ static void test_registry(void)
     static const WCHAR brokenW[] = {'H','K','E','Y','_','b','r','o','k','e','n','_','k','e','y',0};
     static const WCHAR broken2W[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','a',0};
     WCHAR pathW[MAX_PATH];
+    DWORD dwvalue, type;
     VARIANT value, v;
     IWshShell3 *sh3;
     VARTYPE vartype;
-    DWORD dwvalue;
     LONG bound;
     HRESULT hr;
     BSTR name;
@@ -255,6 +255,7 @@ static void test_registry(void)
             &IID_IWshShell3, (void**)&sh3);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
+    /* RegRead() */
     V_VT(&value) = VT_I2;
     hr = IWshShell3_RegRead(sh3, NULL, &value);
     ok(hr == E_POINTER, "got 0x%08x\n", hr);
@@ -388,6 +389,84 @@ static void test_registry(void)
     VariantClear(&value);
     SysFreeString(name);
 
+    delete_key(root);
+
+    /* RegWrite() */
+    ret = RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &root);
+    ok(ret == 0, "got %d\n", ret);
+
+    hr = IWshShell3_RegWrite(sh3, NULL, NULL, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    lstrcpyW(pathW, keypathW);
+    lstrcatW(pathW, regszW);
+    name = SysAllocString(pathW);
+
+    hr = IWshShell3_RegWrite(sh3, name, NULL, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    VariantInit(&value);
+    hr = IWshShell3_RegWrite(sh3, name, &value, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    hr = IWshShell3_RegWrite(sh3, name, &value, &value);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+
+    /* type is optional */
+    V_VT(&v) = VT_ERROR;
+    V_ERROR(&v) = DISP_E_PARAMNOTFOUND;
+    hr = IWshShell3_RegWrite(sh3, name, &value, &v);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    /* default type is REG_SZ */
+    V_VT(&value) = VT_I4;
+    V_I4(&value) = 12;
+    hr = IWshShell3_RegWrite(sh3, name, &value, &v);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    type = REG_NONE;
+    ret = RegGetValueA(root, NULL, "regsz", RRF_RT_ANY, &type, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    ok(type == REG_SZ, "got %d\n", type);
+
+    ret = RegDeleteValueA(root, "regsz");
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    V_VT(&value) = VT_BSTR;
+    V_BSTR(&value) = SysAllocString(regszW);
+    hr = IWshShell3_RegWrite(sh3, name, &value, &v);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    VariantClear(&value);
+
+    type = REG_NONE;
+    ret = RegGetValueA(root, NULL, "regsz", RRF_RT_ANY, &type, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    ok(type == REG_SZ, "got %d\n", type);
+
+    ret = RegDeleteValueA(root, "regsz");
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    V_VT(&value) = VT_R4;
+    V_R4(&value) = 1.2;
+    hr = IWshShell3_RegWrite(sh3, name, &value, &v);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    VariantClear(&value);
+
+    type = REG_NONE;
+    ret = RegGetValueA(root, NULL, "regsz", RRF_RT_ANY, &type, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    ok(type == REG_SZ, "got %d\n", type);
+
+    ret = RegDeleteValueA(root, "regsz");
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    V_VT(&value) = VT_R4;
+    V_R4(&value) = 1.2;
+    V_VT(&v) = VT_I2;
+    V_I2(&v) = 1;
+    hr = IWshShell3_RegWrite(sh3, name, &value, &v);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+    VariantClear(&value);
+
+    SysFreeString(name);
+
     delete_key(root);
     IWshShell3_Release(sh3);
 }