Autosyncing with Wine HEAD
authorThe Wine Synchronizer <winesync@svn.reactos.org>
Fri, 4 Apr 2008 16:43:16 +0000 (16:43 +0000)
committerThe Wine Synchronizer <winesync@svn.reactos.org>
Fri, 4 Apr 2008 16:43:16 +0000 (16:43 +0000)
svn path=/trunk/; revision=32862

71 files changed:
rostests/winetests/advpack/advpack.c
rostests/winetests/advpack/advpack.rbuild
rostests/winetests/advpack/files.c
rostests/winetests/advpack/install.c
rostests/winetests/cabinet/cabinet.rbuild
rostests/winetests/cabinet/extract.c
rostests/winetests/cabinet/fdi.c
rostests/winetests/comcat/comcat.rbuild
rostests/winetests/comctl32/comboex.c
rostests/winetests/comctl32/comctl32.rbuild
rostests/winetests/comctl32/datetime.c
rostests/winetests/comctl32/dpa.c
rostests/winetests/comctl32/header.c
rostests/winetests/comctl32/listview.c
rostests/winetests/comctl32/monthcal.c
rostests/winetests/comctl32/progress.c
rostests/winetests/comctl32/rebar.c
rostests/winetests/comctl32/toolbar.c
rostests/winetests/comctl32/tooltips.c
rostests/winetests/comctl32/treeview.c
rostests/winetests/comdlg32/comdlg32.rbuild
rostests/winetests/comdlg32/printdlg.c
rostests/winetests/directory.rbuild
rostests/winetests/hlink/hlink.c
rostests/winetests/hlink/hlink.rbuild
rostests/winetests/imm32/imm32.c [new file with mode: 0644]
rostests/winetests/imm32/imm32.rbuild [new file with mode: 0644]
rostests/winetests/imm32/testlist.c [new file with mode: 0644]
rostests/winetests/lz32/lz32.rbuild
rostests/winetests/lz32/lzexpand_main.c
rostests/winetests/mapi32/mapi32.rbuild
rostests/winetests/mapi32/prop.c
rostests/winetests/mlang/mlang.rbuild
rostests/winetests/msi/automation.c
rostests/winetests/msi/db.c
rostests/winetests/msi/format.c
rostests/winetests/msi/install.c
rostests/winetests/msi/msi.c
rostests/winetests/msi/msi.rbuild
rostests/winetests/msi/package.c
rostests/winetests/msi/record.c
rostests/winetests/msi/source.c
rostests/winetests/msi/suminfo.c
rostests/winetests/netapi32/access.c
rostests/winetests/netapi32/netapi32.rbuild
rostests/winetests/netapi32/wksta.c
rostests/winetests/odbccp32/odbccp32.rbuild
rostests/winetests/riched20/editor.c
rostests/winetests/riched20/riched20.rbuild
rostests/winetests/riched32/editor.c [new file with mode: 0644]
rostests/winetests/riched32/riched32.rbuild [new file with mode: 0644]
rostests/winetests/riched32/testlist.c [new file with mode: 0644]
rostests/winetests/rsaenh/rsaenh.c
rostests/winetests/rsaenh/rsaenh.rbuild
rostests/winetests/shlwapi/generated.c
rostests/winetests/shlwapi/istream.c [new file with mode: 0644]
rostests/winetests/shlwapi/path.c
rostests/winetests/shlwapi/shlwapi.rbuild
rostests/winetests/shlwapi/string.c
rostests/winetests/shlwapi/testlist.c
rostests/winetests/shlwapi/url.c
rostests/winetests/urlmon/generated.c
rostests/winetests/urlmon/misc.c
rostests/winetests/urlmon/protocol.c
rostests/winetests/urlmon/stream.c
rostests/winetests/urlmon/url.c
rostests/winetests/urlmon/urlmon.rbuild
rostests/winetests/uxtheme/system.c
rostests/winetests/uxtheme/uxtheme.rbuild
rostests/winetests/version/info.c
rostests/winetests/version/version.rbuild

index b09a9c6..b90df2d 100644 (file)
@@ -35,6 +35,7 @@
 #define REG_VAL_EXISTS(key, value)   !RegQueryValueEx(key, value, NULL, NULL, NULL, NULL)
 #define OPEN_GUID_KEY() !RegOpenKey(HKEY_LOCAL_MACHINE, GUID_KEY, &guid)
 
+static HMODULE hAdvPack;
 static HRESULT (WINAPI *pCloseINFEngine)(HINF);
 static HRESULT (WINAPI *pDelNode)(LPCSTR,DWORD);
 static HRESULT (WINAPI *pGetVersionFromFile)(LPCSTR,LPDWORD,LPDWORD,BOOL);
@@ -66,7 +67,7 @@ static void get_progfiles_dir(void)
 
 static BOOL init_function_pointers(void)
 {
-    HMODULE hAdvPack = LoadLibraryA("advpack.dll");
+    hAdvPack = LoadLibraryA("advpack.dll");
 
     if (!hAdvPack)
         return FALSE;
@@ -521,7 +522,7 @@ static void setperusersecvalues_test(void)
     /* set initial values */
     lstrcpy(peruser.szGUID, "guid");
     hr = pSetPerUserSecValues(&peruser);
-    ok(hr == S_OK, "Expected S_OK, got %d\n", hr);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
     ok(OPEN_GUID_KEY(), "Expected guid key to exist\n");
     ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n");
     ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n");
@@ -538,7 +539,7 @@ static void setperusersecvalues_test(void)
     /* raise the version, but bRollback is FALSE, so vals not saved */
     lstrcpy(peruser.szVersion, "2,1,1,1");
     hr = pSetPerUserSecValues(&peruser);
-    ok(hr == S_OK, "Expected S_OK, got %d\n", hr);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
     ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n");
     ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n");
     ok(check_reg_str(guid, "Locale", "locale"), "Expected locale\n");
@@ -555,7 +556,7 @@ static void setperusersecvalues_test(void)
     peruser.bRollback = TRUE;
     lstrcpy(peruser.szVersion, "3,1,1,1");
     hr = pSetPerUserSecValues(&peruser);
-    ok(hr == S_OK, "Expected S_OK, got %d\n", hr);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
     ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n");
     ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n");
     ok(check_reg_str(guid, "Locale", "locale"), "Expected locale\n");
@@ -594,4 +595,6 @@ START_TEST(advpack)
     setperusersecvalues_test();
     translateinfstring_test();
     translateinfstringex_test();
+
+    FreeLibrary(hAdvPack);
 }
index baabd33..09e93fd 100644 (file)
@@ -1,17 +1,18 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="advpack_winetest" type="win32cui" installbase="bin" installname="advpack_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="advpack_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>advpack.c</file>
+       <file>files.c</file>
+       <file>install.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>cabinet</library>
-       <library>user32</library>
        <library>advapi32</library>
        <library>kernel32</library>
        <library>ntdll</library>
-       <file>advpack.c</file>
-       <file>files.c</file>
-       <file>install.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index 71c1741..e109a88 100644 (file)
@@ -560,4 +560,6 @@ START_TEST(files)
     test_AdvInstallFile();
 
     delete_test_files();
+
+    FreeLibrary(hAdvPack);
 }
index 6075963..564ecda 100644 (file)
@@ -23,6 +23,7 @@
 #include <advpub.h>
 #include "wine/test.h"
 
+static HMODULE hAdvPack;
 /* function pointers */
 static HRESULT (WINAPI *pRunSetupCommand)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, HANDLE*, DWORD, LPVOID);
 static HRESULT (WINAPI *pLaunchINFSection)(HWND, HINSTANCE, LPSTR, INT);
@@ -32,7 +33,7 @@ static char CURR_DIR[MAX_PATH];
 
 static BOOL init_function_pointers(void)
 {
-    HMODULE hAdvPack = LoadLibraryA("advpack.dll");
+    hAdvPack = LoadLibraryA("advpack.dll");
     if (!hAdvPack)
         return FALSE;
 
@@ -273,4 +274,6 @@ START_TEST(install)
     test_RunSetupCommand();
     test_LaunchINFSection();
     test_LaunchINFSectionEx();
+
+    FreeLibrary(hAdvPack);
 }
index c119392..dcac40f 100644 (file)
@@ -1,15 +1,16 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="cabinet_winetest" type="win32cui" installbase="bin" installname="cabinet_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="cabinet_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>extract.c</file>
+       <file>fdi.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>cabinet</library>
-       <library>user32</library>
        <library>kernel32</library>
        <library>ntdll</library>
-       <file>extract.c</file>
-       <file>fdi.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index 2cf5154..156df17 100644 (file)
@@ -28,7 +28,7 @@
 #define MEDIA_SIZE          999999999
 #define FOLDER_THRESHOLD    900000
 
-/* The following defintions were copied from dlls/cabinet/cabinet.h
+/* The following definitions were copied from dlls/cabinet/cabinet.h
  * because they are undocumented in windows.
  */
 
index 7c10b3d..18745f2 100644 (file)
@@ -92,10 +92,6 @@ static void test_FDICreate(void)
     HFDI hfdi;
     ERF erf;
 
-    erf.erfOper = 0xcafefeed;
-    erf.erfType = 0xdeadbabe;
-    erf.fError = 0xdecaface;
-
     /* native crashes if pfnalloc is NULL */
 
     /* FDICreate does not crash with a NULL pfnfree,
@@ -104,6 +100,9 @@ static void test_FDICreate(void)
     if (0)
     {
         SetLastError(0xdeadbeef);
+        erf.erfOper = 0xcafefeed;
+        erf.erfType = 0xdeadbabe;
+        erf.fError = 0xdecaface;
         hfdi = FDICreate(fdi_alloc, NULL, fdi_open, fdi_read,
                          fdi_write, fdi_close, fdi_seek,
                          cpuUNKNOWN, &erf);
@@ -118,99 +117,121 @@ static void test_FDICreate(void)
     }
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, NULL, fdi_read,
                      fdi_write, fdi_close, fdi_seek,
                      cpuUNKNOWN, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, NULL,
                      fdi_write, fdi_close, fdi_seek,
                      cpuUNKNOWN, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
                      NULL, fdi_close, fdi_seek,
                      cpuUNKNOWN, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
                      fdi_write, NULL, fdi_seek,
                      cpuUNKNOWN, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
                      fdi_write, fdi_close, NULL,
                      cpuUNKNOWN, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
                      fdi_write, fdi_close, fdi_seek,
                      cpuUNKNOWN, NULL);
-    ok(hfdi != NULL, "Expected non-NULL context\n");
+    /* XP sets hfdi to a non-NULL value, but Vista sets it to NULL! */
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    /* NULL is passed to FDICreate instead of &erf, so don't retest the erf member values. */
 
     FDIDestroy(hfdi);
 
     /* bad cpu type */
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
                      fdi_write, fdi_close, fdi_seek,
                      0xcafebabe, &erf);
     ok(hfdi != NULL, "Expected non-NULL context\n");
     ok(GetLastError() == 0xdeadbeef,
        "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
-    ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
-    ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
+    ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
+    ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
+    ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
 
     FDIDestroy(hfdi);
 
     /* pfnalloc fails */
     SetLastError(0xdeadbeef);
+    erf.erfOper = 0xcafefeed;
+    erf.erfType = 0xdeadbabe;
+    erf.fError = 0xdecaface;
     hfdi = FDICreate(fdi_alloc_bad, fdi_free, fdi_open, fdi_read,
                      fdi_write, fdi_close, fdi_seek,
                      cpuUNKNOWN, &erf);
index 7fe8a38..dc42865 100644 (file)
@@ -1,15 +1,17 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="comcat_winetest" type="win32cui" installbase="bin" installname="comcat_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="comcat_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>comcat.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>ole32</library>
        <library>advapi32</library>
        <library>kernel32</library>
        <library>uuid</library>
        <library>ntdll</library>
-       <file>comcat.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index f3f46b3..ad58643 100644 (file)
@@ -187,10 +187,23 @@ static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, L
     return 0L;
 }
 
-static void init(void) {
+static int init(void)
+{
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
     WNDCLASSA wc;
-
-    InitCommonControls();
+    INITCOMMONCONTROLSEX iccex;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return 0;
+    }
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC  = ICC_USEREX_CLASSES;
+    pInitCommonControlsEx(&iccex);
 
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.cbClsExtra = 0;
@@ -209,7 +222,7 @@ static void init(void) {
     assert(hComboExParentWnd != NULL);
 
     hMainHinst = GetModuleHandleA(NULL);
-
+    return 1;
 }
 
 static void cleanup(void)
@@ -227,7 +240,8 @@ static void cleanup(void)
 
 START_TEST(comboex)
 {
-    init();
+    if (!init())
+        return;
 
     test_comboboxex();
 
index f7ed7ae..307b6dc 100644 (file)
@@ -5,14 +5,6 @@
        <include base="comctl32_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
-       <library>wine</library>
-       <library>comctl32</library>
-       <library>ole32</library>
-       <library>user32</library>
-       <library>gdi32</library>
-       <library>advapi32</library>
-       <library>kernel32</library>
-       <library>ntdll</library>
        <file>comboex.c</file>
        <file>datetime.c</file>
        <file>dpa.c</file>
        <file>updown.c</file>
        <file>rsrc.rc</file>
        <file>testlist.c</file>
+       <library>wine</library>
+       <library>comctl32</library>
+       <library>ole32</library>
+       <library>user32</library>
+       <library>gdi32</library>
+       <library>advapi32</library>
+       <library>kernel32</library>
+       <library>ntdll</library>
 </module>
 </group>
index 956f16e..b194cc1 100644 (file)
@@ -112,6 +112,7 @@ static const struct message test_dtm_set_range_swap_min_max_seq[] = {
 
 static const struct message test_dtm_set_and_get_system_time_seq[] = {
     { DTM_SETSYSTEMTIME, sent|wparam, 0x00000001 },
+    { 0x0090, sent|optional }, /* Vista */
     { WM_DESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
     { WM_NCDESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
     { DTM_SETSYSTEMTIME, sent|wparam, 0x00000001 },
@@ -125,6 +126,7 @@ static const struct message test_dtm_set_and_get_system_time_seq[] = {
 };
 
 static const struct message destroy_window_seq[] = {
+    { 0x0090, sent|optional }, /* Vista */
     { WM_DESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
     { WM_NCDESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
     { 0 }
@@ -191,6 +193,8 @@ static HWND create_datetime_control(DWORD style, DWORD exstyle)
 
 static void test_dtm_set_format(HWND hWndDateTime)
 {
+    CHAR txt[256];
+    SYSTEMTIME systime;
     LRESULT r;
 
     r = SendMessage(hWndDateTime, DTM_SETFORMAT, 0, (LPARAM)NULL);
@@ -201,6 +205,17 @@ static void test_dtm_set_format(HWND hWndDateTime)
     expect(1, r);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_format_seq, "test_dtm_set_format", FALSE);
+
+    r = SendMessage(hWndDateTime, DTM_SETFORMAT, 0,
+                   (LPARAM)"'hh' hh");
+    expect(1, r);
+    ZeroMemory(&systime, sizeof(systime));
+    systime.wYear = 2000;
+    systime.wMonth = systime.wDay = 1;
+    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, 0, (LPARAM)&systime);
+    expect(1, r);
+    GetWindowText(hWndDateTime, txt, 256);
+    todo_wine ok(strcmp(txt, "hh 12") == 0, "String mismatch (\"%s\" vs \"hh 12\")\n", txt);
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 }
 
@@ -311,7 +326,7 @@ static void test_dtm_set_and_get_range(HWND hWndDateTime)
 
     /* initialize st[0] to lowest possible value */
     fill_systime_struct(&st[0], 1601, 1, 0, 1, 0, 0, 0, 0);
-    /* intialize st[1] to all invalid numbers */
+    /* initialize st[1] to all invalid numbers */
     fill_systime_struct(&st[1], 0, 0, 7, 0, 24, 60, 60, 1000);
 
     r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
@@ -560,7 +575,21 @@ static void test_datetime_control(void)
 
 START_TEST(datetime)
 {
-    InitCommonControls();
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+    INITCOMMONCONTROLSEX iccex;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return;
+    }
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC  = ICC_DATE_CLASSES;
+    pInitCommonControlsEx(&iccex);
+
     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
 
     test_datetime_control();
index f1ca8f2..b2ea3d8 100644 (file)
@@ -219,7 +219,7 @@ static void test_dpa(void)
 
     /* Set item with out of bound index */
     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
-    /* Fill the greated gap */
+    /* Fill the created gap */
     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
     rc=CheckDPA(dpa, 0x56, &dw);
     ok(rc, "dw=0x%x\n", dw);
@@ -454,6 +454,4 @@ START_TEST(dpa)
         test_dpa();
     else
         trace("skipping tests\n");
-
-    FreeLibrary(hcomctl32);
 }
index fa75d49..6f8b462 100644 (file)
@@ -1156,7 +1156,7 @@ static void test_hdm_index_messages(HWND hParent)
 
     ok_sequence(sequences, HEADER_SEQ_INDEX, orderArray_seq, "set_get_orderArray sequence testing", FALSE);
 
-    /* check if the array order is set correctly and the size of the array is corret. */
+    /* check if the array order is set correctly and the size of the array is correct. */
     expect(2, iSize);
     expect(lpiarray[0], lpiarrayReceived[0]);
     expect(lpiarray[1], lpiarrayReceived[1]);
@@ -1480,10 +1480,24 @@ static LRESULT CALLBACK HeaderTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
     return 0L;
 }
 
-static void init(void) {
+static int init(void)
+{
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
     WNDCLASSA wc;
+    INITCOMMONCONTROLSEX iccex;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return 0;
+    }
 
-    InitCommonControls();
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC  = ICC_USEREX_CLASSES;
+    pInitCommonControlsEx(&iccex);
 
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.cbClsExtra = 0;
@@ -1503,13 +1517,15 @@ static void init(void) {
       NULL, NULL, GetModuleHandleA(NULL), 0);
     assert(hHeaderParentWnd != NULL);
     ShowWindow(hHeaderParentWnd, SW_SHOW);
+    return 1;
 }
 
 START_TEST(header)
 {
     HWND parent_hwnd;
 
-    init();
+    if (!init())
+        return;
 
     test_header_control();
     test_header_order();
index 30a81a8..ad82579 100644 (file)
@@ -539,6 +539,44 @@ static void test_checkboxes(void)
     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
     ok(item.state == 0x1aaa, "state %x\n", item.state);
 
+    /* Toggle checkbox tests (bug 9934) */
+    memset (&item, 0xcc, sizeof(item));
+    item.mask = LVIF_STATE;
+    item.iItem = 3;
+    item.iSubItem = 0;
+    item.state = LVIS_FOCUSED;
+    item.stateMask = LVIS_FOCUSED;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
+    expect(1, r);
+
+    item.iItem = 3;
+    item.mask = LVIF_STATE;
+    item.stateMask = 0xffff;
+    r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+    ok(item.state == 0x1aab, "state %x\n", item.state);
+
+    r = SendMessage(hwnd, WM_KEYDOWN, VK_SPACE, 0);
+    expect(0, r);
+    r = SendMessage(hwnd, WM_KEYUP, VK_SPACE, 0);
+    expect(0, r);
+
+    item.iItem = 3;
+    item.mask = LVIF_STATE;
+    item.stateMask = 0xffff;
+    r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+    ok(item.state == 0x2aab, "state %x\n", item.state);
+
+    r = SendMessage(hwnd, WM_KEYDOWN, VK_SPACE, 0);
+    expect(0, r);
+    r = SendMessage(hwnd, WM_KEYUP, VK_SPACE, 0);
+    expect(0, r);
+
+    item.iItem = 3;
+    item.mask = LVIF_STATE;
+    item.stateMask = 0xffff;
+    r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+    ok(item.state == 0x1aab, "state %x\n", item.state);
+
     DestroyWindow(hwnd);
 }
 
@@ -547,7 +585,7 @@ static void insert_column(HWND hwnd, int idx)
     LVCOLUMN column;
     DWORD rc;
 
-    memset(&column, 0xaa, sizeof(column));
+    memset(&column, 0xcc, sizeof(column));
     column.mask = LVCF_SUBITEM;
     column.iSubItem = idx;
 
@@ -562,7 +600,7 @@ static void insert_item(HWND hwnd, int idx)
     LVITEMA item;
     DWORD rc;
 
-    memset(&item, 0xaa, sizeof (item));
+    memset(&item, 0xcc, sizeof (item));
     item.mask = LVIF_TEXT;
     item.iItem = idx;
     item.iSubItem = 0;
@@ -593,7 +631,7 @@ static void test_items(void)
     insert_column(hwnd, 1);
 
     /* Insert an item with just a param */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_PARAM;
     item.iItem = 0;
     item.iSubItem = 0;
@@ -602,7 +640,7 @@ static void test_items(void)
     ok(r == 0, "ret %d\n", r);
 
     /* Test getting of the param */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_PARAM;
     item.iItem = 0;
     item.iSubItem = 0;
@@ -611,7 +649,7 @@ static void test_items(void)
     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
 
     /* Set up a subitem */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_TEXT;
     item.iItem = 0;
     item.iSubItem = 1;
@@ -620,7 +658,7 @@ static void test_items(void)
     ok(r != 0, "ret %d\n", r);
 
     /* Query param from subitem: returns main item param */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_PARAM;
     item.iItem = 0;
     item.iSubItem = 1;
@@ -629,7 +667,7 @@ static void test_items(void)
     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
 
     /* Set up param on first subitem: no effect */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_PARAM;
     item.iItem = 0;
     item.iSubItem = 1;
@@ -638,7 +676,7 @@ static void test_items(void)
     ok(r == 0, "ret %d\n", r);
 
     /* Query param from subitem again: should still return main item param */
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_PARAM;
     item.iItem = 0;
     item.iSubItem = 1;
@@ -647,7 +685,7 @@ static void test_items(void)
     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
 
     /**** Some tests of state highlighting ****/
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_STATE;
     item.iItem = 0;
     item.iSubItem = 0;
@@ -660,7 +698,7 @@ static void test_items(void)
     r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
     ok(r != 0, "ret %d\n", r);
 
-    memset (&item, 0xaa, sizeof (item));
+    memset (&item, 0xcc, sizeof (item));
     item.mask = LVIF_STATE;
     item.iItem = 0;
     item.iSubItem = 0;
@@ -687,7 +725,7 @@ static void test_columns(void)
     ok(hwnd != NULL, "failed to create listview window\n");
 
     /* Add a column with no mask */
-    memset(&column, 0xaa, sizeof(column));
+    memset(&column, 0xcc, sizeof(column));
     column.mask = 0;
     rc = ListView_InsertColumn(hwnd, 0, &column);
     ok(rc==0, "Inserting column with no mask failed with %d\n", rc);
@@ -1040,7 +1078,20 @@ static void test_item_position(void)
 
 START_TEST(listview)
 {
-    InitCommonControls();
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (pInitCommonControlsEx)
+    {
+        INITCOMMONCONTROLSEX iccex;
+        iccex.dwSize = sizeof(iccex);
+        iccex.dwICC  = ICC_LISTVIEW_CLASSES;
+        pInitCommonControlsEx(&iccex);
+    }
+    else
+        InitCommonControls();
 
     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
 
index d670004..7f3b4e5 100644 (file)
@@ -281,6 +281,7 @@ static const struct message destroy_monthcal_parent_msgs_seq[] = {
 
 /* expected message sequence for child*/
 static const struct message destroy_monthcal_child_msgs_seq[] = {
+    { 0x0090, sent|optional }, /* Vista */
     { WM_SHOWWINDOW, sent|wparam|lparam, 0, 0},
     { WM_WINDOWPOSCHANGING, sent|wparam, 0},
     { WM_WINDOWPOSCHANGED, sent|wparam, 0},
@@ -290,6 +291,7 @@ static const struct message destroy_monthcal_child_msgs_seq[] = {
 };
 
 static const struct message destroy_monthcal_multi_sel_style_seq[] = {
+    { 0x0090, sent|optional }, /* Vista */
     { WM_DESTROY, sent|wparam|lparam, 0, 0},
     { WM_NCDESTROY, sent|wparam|lparam, 0, 0},
     { 0 }
@@ -297,6 +299,7 @@ static const struct message destroy_monthcal_multi_sel_style_seq[] = {
 
 /* expected message sequence for parent window*/
 static const struct message destroy_parent_seq[] = {
+    { 0x0090, sent|optional }, /* Vista */
     { WM_WINDOWPOSCHANGING, sent|wparam, 0},
     { WM_WINDOWPOSCHANGED, sent|wparam, 0},
     { WM_NCACTIVATE, sent|wparam|lparam, 0, 0},
@@ -316,7 +319,6 @@ static void test_monthcal(void)
     SYSTEMTIME st[2], st1[2];
     int res, month_range;
 
-    InitCommonControls();
     hwnd = CreateWindowA(MONTHCAL_CLASSA, "MonthCal", WS_POPUP | WS_VISIBLE, CW_USEDEFAULT,
                          0, 300, 300, 0, 0, NULL, NULL);
     ok(hwnd != NULL, "Failed to create MonthCal\n");
@@ -479,8 +481,6 @@ static HWND create_monthcal_control(DWORD style, HWND parent_window)
     struct subclass_info *info;
     HWND hwnd;
 
-    InitCommonControls();
-
     info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
     if (!info)
         return NULL;
@@ -720,7 +720,7 @@ static void test_monthcal_unicode(HWND hwnd)
 static void test_monthcal_HitTest(HWND hwnd)
 {
     MCHITTESTINFO mchit;
-    int res;
+    UINT res;
     SYSTEMTIME st;
 
     memset(&mchit, 0, sizeof(MCHITTESTINFO));
@@ -912,7 +912,7 @@ static void test_monthcal_todaylink(HWND hwnd)
     MCHITTESTINFO mchit;
     SYSTEMTIME st_test, st_new;
     BOOL error = FALSE;
-    int res;
+    UINT res;
 
     memset(&mchit, 0, sizeof(MCHITTESTINFO));
 
@@ -1110,7 +1110,22 @@ static void test_monthcal_MaxSelDay(HWND hwnd)
 
 START_TEST(monthcal)
 {
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+    INITCOMMONCONTROLSEX iccex;
     HWND hwnd, parent_wnd;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return;
+    }
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC  = ICC_DATE_CLASSES;
+    pInitCommonControlsEx(&iccex);
+
     test_monthcal();
 
     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
index 2716f33..10bbbef 100644 (file)
@@ -90,10 +90,22 @@ static void update_window(HWND hWnd)
 
 static void init(void)
 {
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
     WNDCLASSA wc;
     RECT rect;
     
-    InitCommonControls();
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (pInitCommonControlsEx)
+    {
+        INITCOMMONCONTROLSEX iccex;
+        iccex.dwSize = sizeof(iccex);
+        iccex.dwICC  = ICC_PROGRESS_CLASS;
+        pInitCommonControlsEx(&iccex);
+    }
+    else
+        InitCommonControls();
   
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.cbClsExtra = 0;
index 6bebeb7..e5bea38 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+/* make sure the structures work with a comctl32 v5.x */
+#define _WIN32_WINNT 0x500
+#define _WIN32_IE 0x500
+
 #include <assert.h>
 #include <stdarg.h>
 
@@ -252,6 +256,21 @@ rbsize_result_t rbsize_results[] = {
     { {328,   0, 511,  20}, 0x00, 183}, { {511,   0, 672,  20}, 0x00, 161},
     { {  0,  20, 672,  40}, 0x00, 200},
   }, },
+  { {0, 0, 672, 56}, 56, 2, {28, 28, }, 5, {
+    { {  0,   0, 114,  28}, 0x00, 40}, { {114,   0, 328,  28}, 0x00, 214},
+    { {328,   0, 511,  28}, 0x00, 183}, { {511,   0, 672,  28}, 0x00, 161},
+    { {  0,  28, 672,  56}, 0x00, 200},
+  }, },
+  { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
+    { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 328,  20}, 0x00, 214},
+    { {328,   0, 511,  20}, 0x00, 183}, { {511,   0, 672,  20}, 0x00, 161},
+    { {  0,  20, 672,  40}, 0x00, 200},
+  }, },
+  { {0, 0, 672, 56}, 56, 2, {28, 28, }, 5, {
+    { {  0,   0, 114,  28}, 0x00, 40}, { {114,   0, 328,  28}, 0x00, 214},
+    { {328,   0, 511,  28}, 0x00, 183}, { {511,   0, 672,  28}, 0x00, 161},
+    { {  0,  28, 672,  56}, 0x00, 200},
+  }, },
   { {0, 0, 672, 0}, 0, 0, {0, }, 0, {{{0, 0, 0, 0}, 0, 0},
   }, },
   { {0, 0, 672, 65}, 65, 1, {65, }, 3, {
@@ -329,6 +348,8 @@ static void layout_test(void)
 {
     HWND hRebar = NULL;
     REBARBANDINFO rbi;
+    HIMAGELIST himl;
+    REBARINFO ri;
 
     rebuild_rebar(&hRebar);
     check_sizes();
@@ -406,6 +427,27 @@ static void layout_test(void)
     SendMessageA(hRebar, RB_MINIMIZEBAND, 0, 0);
     check_sizes();
 
+    /* an image will increase the band height */
+    himl = ImageList_LoadImage(LoadLibrary("comctl32"), MAKEINTRESOURCE(121), 24, 2, CLR_NONE, IMAGE_BITMAP, LR_DEFAULTCOLOR);
+    ri.cbSize = sizeof(ri);
+    ri.fMask = RBIM_IMAGELIST;
+    ri.himl = himl;
+    ok(SendMessage(hRebar, RB_SETBARINFO, 0, (LPARAM)&ri), "RB_SETBARINFO failed\n");
+    rbi.fMask = RBBIM_IMAGE;
+    rbi.iImage = 1;
+    SendMessage(hRebar, RB_SETBANDINFO, 1, (LPARAM)&rbi);
+    check_sizes();
+
+    /* after removing it everything is back to normal*/
+    rbi.iImage = -1;
+    SendMessage(hRebar, RB_SETBANDINFO, 1, (LPARAM)&rbi);
+    check_sizes();
+
+    /* Only -1 means that the image is not present. Other invalid values increase the height */
+    rbi.iImage = -2;
+    SendMessage(hRebar, RB_SETBANDINFO, 1, (LPARAM)&rbi);
+    check_sizes();
+
     /* VARHEIGHT resizing test on a horizontal rebar */
     rebuild_rebar(&hRebar);
     SetWindowLong(hRebar, GWL_STYLE, GetWindowLong(hRebar, GWL_STYLE) | RBS_AUTOSIZE);
@@ -786,11 +828,24 @@ static void bandinfo_test(void)
 
 START_TEST(rebar)
 {
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+    INITCOMMONCONTROLSEX iccex;
     WNDCLASSA wc;
     MSG msg;
     RECT rc;
 
-    InitCommonControls();
+    /* LoadLibrary is needed. This file has no references to functions in comctl32 */
+    hComctl32 = LoadLibraryA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return;
+    }
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC = ICC_COOL_CLASSES;
+    pInitCommonControlsEx(&iccex);
 
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.cbClsExtra = 0;
@@ -825,4 +880,6 @@ START_TEST(rebar)
         DispatchMessageA(&msg);
     }
     DestroyWindow(hMainWnd);
+
+    FreeLibrary(hComctl32);
 }
index b094a4b..2693025 100644 (file)
@@ -40,6 +40,8 @@ static BOOL g_fExpectedHotItemOld;
 static BOOL g_fExpectedHotItemNew;
 static DWORD g_dwExpectedDispInfoMask;
 
+#define expect(EXPECTED,GOT) ok((GOT)==(EXPECTED), "Expected %d, got %d\n", (EXPECTED), (GOT))
+
 #define check_rect(name, val, exp) ok(val.top == exp.top && val.bottom == exp.bottom && \
     val.left == exp.left && val.right == exp.right, "invalid rect (" name ") (%d,%d) (%d,%d) - expected (%d,%d) (%d,%d)\n", \
     val.left, val.top, val.right, val.bottom, exp.left, exp.top, exp.right, exp.bottom);
@@ -1088,6 +1090,38 @@ static void test_setrows(void)
     DestroyWindow(hToolbar);
 }
 
+static void test_getstring(void)
+{
+    HWND hToolbar = NULL;
+    char str[10];
+    WCHAR strW[10];
+    static const char answer[] = "STR";
+    static const WCHAR answerW[] = { 'S','T','R',0 };
+    INT r;
+
+    hToolbar = CreateWindowExA(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hMainWnd, (HMENU)5, GetModuleHandle(NULL), NULL);
+    ok(hToolbar != NULL, "Toolbar creation problem\n");
+
+    r = SendMessage(hToolbar, TB_GETSTRING, MAKEWPARAM(0, 0), (LPARAM)NULL);
+    expect(-1, r);
+    r = SendMessage(hToolbar, TB_GETSTRINGW, MAKEWPARAM(0, 0), (LPARAM)NULL);
+    expect(-1, r);
+    r = SendMessage(hToolbar, TB_ADDSTRING, 0, (LPARAM)answer);
+    expect(0, r);
+    r = SendMessage(hToolbar, TB_GETSTRING, MAKEWPARAM(0, 0), (LPARAM)NULL);
+    expect(lstrlenA(answer), r);
+    r = SendMessage(hToolbar, TB_GETSTRINGW, MAKEWPARAM(0, 0), (LPARAM)NULL);
+    expect(lstrlenA(answer), r);
+    r = SendMessage(hToolbar, TB_GETSTRING, MAKEWPARAM(sizeof(str), 0), (LPARAM)str);
+    expect(lstrlenA(answer), r);
+    expect(0, lstrcmp(answer, str));
+    r = SendMessage(hToolbar, TB_GETSTRINGW, MAKEWPARAM(sizeof(strW), 0), (LPARAM)strW);
+    expect(lstrlenA(answer), r);
+    expect(0, lstrcmpW(answerW, strW));
+
+    DestroyWindow(hToolbar);
+}
+
 START_TEST(toolbar)
 {
     WNDCLASSA wc;
@@ -1122,6 +1156,7 @@ START_TEST(toolbar)
     test_createtoolbarex();
     test_dispinfo();
     test_setrows();
+    test_getstring();
 
     PostQuitMessage(0);
     while(GetMessageA(&msg,0,0,0)) {
index 713fa24..e9bce63 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2005 Dmitry Timoshkov
+ * Copyright 2008 Jason Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -60,9 +61,181 @@ static void test_create_tooltip(void)
     DestroyWindow(parent);
 }
 
+/* try to make sure pending X events have been processed before continuing */
+static void flush_events(int waitTime)
+{
+    MSG msg;
+    int diff = waitTime;
+    DWORD time = GetTickCount() + waitTime;
+
+    while (diff > 0)
+    {
+        if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(100,diff), QS_ALLEVENTS) == WAIT_TIMEOUT) break;
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+        diff = time - GetTickCount();
+    }
+}
+
+static int CD_Stages;
+static LRESULT CD_Result;
+static HWND g_hwnd;
+
+#define TEST_CDDS_PREPAINT           0x00000001
+#define TEST_CDDS_POSTPAINT          0x00000002
+#define TEST_CDDS_PREERASE           0x00000004
+#define TEST_CDDS_POSTERASE          0x00000008
+#define TEST_CDDS_ITEMPREPAINT       0x00000010
+#define TEST_CDDS_ITEMPOSTPAINT      0x00000020
+#define TEST_CDDS_ITEMPREERASE       0x00000040
+#define TEST_CDDS_ITEMPOSTERASE      0x00000080
+#define TEST_CDDS_SUBITEM            0x00000100
+
+static LRESULT CALLBACK CustomDrawWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch(msg) {
+
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+
+    case WM_NOTIFY:
+        if (((NMHDR *)lParam)->code == NM_CUSTOMDRAW) {
+            NMTTCUSTOMDRAW *ttcd = (NMTTCUSTOMDRAW*) lParam;
+            ok(ttcd->nmcd.hdr.hwndFrom == g_hwnd, "Unexpected hwnd source %x (%x)\n",
+                 (int)ttcd->nmcd.hdr.hwndFrom, (int) g_hwnd);
+            ok(ttcd->nmcd.hdr.idFrom == 0x1234ABCD, "Unexpected id %x\n", (int)ttcd->nmcd.hdr.idFrom);
+
+            switch (ttcd->nmcd.dwDrawStage) {
+            case CDDS_PREPAINT     : CD_Stages |= TEST_CDDS_PREPAINT; break;
+            case CDDS_POSTPAINT    : CD_Stages |= TEST_CDDS_POSTPAINT; break;
+            case CDDS_PREERASE     : CD_Stages |= TEST_CDDS_PREERASE; break;
+            case CDDS_POSTERASE    : CD_Stages |= TEST_CDDS_POSTERASE; break;
+            case CDDS_ITEMPREPAINT : CD_Stages |= TEST_CDDS_ITEMPREPAINT; break;
+            case CDDS_ITEMPOSTPAINT: CD_Stages |= TEST_CDDS_ITEMPOSTPAINT; break;
+            case CDDS_ITEMPREERASE : CD_Stages |= TEST_CDDS_ITEMPREERASE; break;
+            case CDDS_ITEMPOSTERASE: CD_Stages |= TEST_CDDS_ITEMPOSTERASE; break;
+            case CDDS_SUBITEM      : CD_Stages |= TEST_CDDS_SUBITEM; break;
+            default: CD_Stages = -1;
+            }
+
+            if (ttcd->nmcd.dwDrawStage == CDDS_PREPAINT) return CD_Result;
+        }
+        /* drop through */
+
+    default:
+        return DefWindowProcA(hWnd, msg, wParam, lParam);
+    }
+
+    return 0L;
+}
+
+static void test_customdraw(void) {
+    static struct {
+        LRESULT FirstReturnValue;
+        int ExpectedCalls;
+    } expectedResults[] = {
+        /* Valid notification responses */
+        {CDRF_DODEFAULT, TEST_CDDS_PREPAINT},
+        {CDRF_SKIPDEFAULT, TEST_CDDS_PREPAINT},
+        {CDRF_NOTIFYPOSTPAINT, TEST_CDDS_PREPAINT | TEST_CDDS_POSTPAINT},
+
+        /* Invalid notification responses */
+        {CDRF_NOTIFYITEMDRAW, TEST_CDDS_PREPAINT},
+        {CDRF_NOTIFYPOSTERASE, TEST_CDDS_PREPAINT},
+        {CDRF_NOTIFYSUBITEMDRAW, TEST_CDDS_PREPAINT},
+        {CDRF_NEWFONT, TEST_CDDS_PREPAINT}
+    };
+
+   int       iterationNumber;
+   WNDCLASSA wc;
+   LRESULT   lResult;
+
+   /* Create a class to use the custom draw wndproc */
+   wc.style = CS_HREDRAW | CS_VREDRAW;
+   wc.cbClsExtra = 0;
+   wc.cbWndExtra = 0;
+   wc.hInstance = GetModuleHandleA(NULL);
+   wc.hIcon = NULL;
+   wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
+   wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+   wc.lpszMenuName = NULL;
+   wc.lpszClassName = "CustomDrawClass";
+   wc.lpfnWndProc = CustomDrawWndProc;
+   RegisterClass(&wc);
+
+   for (iterationNumber = 0;
+        iterationNumber < sizeof(expectedResults)/sizeof(expectedResults[0]);
+        iterationNumber++) {
+
+       HWND parent, hwndTip;
+       TOOLINFO toolInfo = { 0 };
+
+       /* Create a main window */
+       parent = CreateWindowEx(0, "CustomDrawClass", NULL,
+                               WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                               WS_MAXIMIZEBOX | WS_VISIBLE,
+                               50, 50,
+                               300, 300,
+                               NULL, NULL, NULL, 0);
+       ok(parent != NULL, "Creation of main window failed\n");
+
+       /* Make it show */
+       ShowWindow(parent, SW_SHOWNORMAL);
+       flush_events(100);
+
+       /* Create Tooltip */
+       hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
+                                NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
+                                CW_USEDEFAULT, CW_USEDEFAULT,
+                                CW_USEDEFAULT, CW_USEDEFAULT,
+                                parent, NULL, GetModuleHandleA(NULL), 0);
+       ok(hwndTip != NULL, "Creation of tooltip window failed\n");
+
+       /* Set up parms for the wndproc to handle */
+       CD_Stages = 0;
+       CD_Result = expectedResults[iterationNumber].FirstReturnValue;
+       g_hwnd    = hwndTip;
+
+       /* Make it topmost, as per the MSDN */
+       SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,
+             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+       /* Create a tool */
+       toolInfo.cbSize = sizeof(TOOLINFO);
+       toolInfo.hwnd = parent;
+       toolInfo.hinst = GetModuleHandleA(NULL);
+       toolInfo.uFlags = TTF_SUBCLASS;
+       toolInfo.uId = (UINT_PTR)0x1234ABCD;
+       toolInfo.lpszText = (LPSTR)"This is a test tooltip";
+       toolInfo.lParam = 0xdeadbeef;
+       GetClientRect (parent, &toolInfo.rect);
+       lResult = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
+       ok(lResult, "Adding the tool to the tooltip failed\n");
+
+       /* Make tooltip appear quickly */
+       SendMessage(hwndTip, TTM_SETDELAYTIME, (WPARAM)TTDT_INITIAL, (LPARAM)MAKELONG(1,0));
+
+       /* Put cursor inside window, tooltip will appear immediately */
+       SetCursorPos(100, 100);
+       flush_events(200);
+
+       /* Check CustomDraw results */
+       ok(CD_Stages == expectedResults[iterationNumber].ExpectedCalls,
+          "CustomDraw run %d stages %x, expected %x\n", iterationNumber, CD_Stages,
+          expectedResults[iterationNumber].ExpectedCalls);
+
+       /* Clean up */
+       DestroyWindow(hwndTip);
+       DestroyWindow(parent);
+   }
+
+
+}
+
 START_TEST(tooltips)
 {
     InitCommonControls();
 
     test_create_tooltip();
+    test_customdraw();
 }
index 93010d3..c1ba7f3 100644 (file)
@@ -649,10 +649,23 @@ static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa
 
 START_TEST(treeview)
 {
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
     WNDCLASSA wc;
     MSG msg;
   
-    InitCommonControls();
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (pInitCommonControlsEx)
+    {
+        INITCOMMONCONTROLSEX iccex;
+        iccex.dwSize = sizeof(iccex);
+        iccex.dwICC  = ICC_TREEVIEW_CLASSES;
+        pInitCommonControlsEx(&iccex);
+    }
+    else
+        InitCommonControls();
+
     init_msg_sequences(MsgSequences, NUM_MSG_SEQUENCES);
   
     wc.style = CS_HREDRAW | CS_VREDRAW;
index fc3c02e..c29c693 100644 (file)
@@ -1,15 +1,17 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="comdlg32_winetest" type="win32cui" installbase="bin" installname="comdlg32_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="comdlg32_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>filedlg.c</file>
+       <file>printdlg.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>comdlg32</library>
        <library>user32</library>
        <library>kernel32</library>
        <library>ntdll</library>
-       <file>filedlg.c</file>
-       <file>printdlg.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index 783773f..744b6eb 100644 (file)
@@ -26,6 +26,7 @@
 #include "winerror.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "objbase.h"
 
 #include "cderr.h"
 #include "commdlg.h"
index 34fed19..7ec4c41 100644 (file)
@@ -31,6 +31,9 @@
        <directory name="icmp">
                <xi:include href="icmp/icmp.rbuild" />
        </directory>
+       <directory name="imm32">
+               <xi:include href="imm32/imm32.rbuild" />
+       </directory>
        <directory name="kernel32">
                <xi:include href="kernel32/kernel32.rbuild" />
        </directory>
@@ -73,6 +76,9 @@
        <directory name="riched20">
                <xi:include href="riched20/riched20.rbuild" />
        </directory>
+       <directory name="riched32">
+               <xi:include href="riched32/riched32.rbuild" />
+       </directory>
        <directory name="rpcrt4">
                <xi:include href="rpcrt4/rpcrt4.rbuild" />
        </directory>
index a2d7360..6c3dff0 100644 (file)
@@ -2,7 +2,7 @@
  * Implementation of hyperlinking (hlink.dll)
  *
  * Copyright 2006 Mike McCormack
- * Copyright 2007 Jacek Caban for CodeWeavers
+ * Copyright 2007-2008 Jacek Caban for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #include "wine/test.h"
 
+#define DEFINE_EXPECT(func) \
+    static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+    expect_ ## func = TRUE
+
+#define CHECK_EXPECT2(func) \
+    do { \
+        ok(expect_ ##func, "unexpected call " #func "\n"); \
+        called_ ## func = TRUE; \
+    }while(0)
+
+#define CHECK_EXPECT(func) \
+    do { \
+        CHECK_EXPECT2(func); \
+        expect_ ## func = FALSE; \
+    }while(0)
+
+#define CHECK_CALLED(func) \
+    do { \
+        ok(called_ ## func, "expected " #func "\n"); \
+        expect_ ## func = called_ ## func = FALSE; \
+    }while(0)
+
+DEFINE_EXPECT(IsSystemMoniker);
+DEFINE_EXPECT(BindToStorage);
+DEFINE_EXPECT(GetDisplayName);
+
 static const char *debugstr_w(LPCWSTR str)
 {
     static char buf[1024];
@@ -37,6 +65,18 @@ static const char *debugstr_w(LPCWSTR str)
     return buf;
 }
 
+static const char *debugstr_guid(REFIID riid)
+{
+    static char buf[50];
+
+    sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+            riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
+            riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
+            riid->Data4[5], riid->Data4[6], riid->Data4[7]);
+
+    return buf;
+}
+
 static void test_HlinkIsShortcut(void)
 {
     int i;
@@ -292,7 +332,11 @@ static void test_persist(void)
 
     hr = HlinkCreateFromString(url, NULL, NULL, NULL,
                                0, NULL, &IID_IHlink, (LPVOID*) &lnk);
-    ok(hr == S_OK, "IHlinCreateFromString failed with error 0x%08x\n", hr);
+    ok(hr == S_OK, "IHlinkCreateFromString failed with error 0x%08x\n", hr);
+    if (!lnk) {
+        skip("Can't create lnk, skipping test_persist.  Was wineprefixcreate run properly?\n");
+        return;
+    }
     test_persist_save_data("url only", lnk, expected_hlink_data, sizeof(expected_hlink_data));
     IHlink_Release(lnk);
 
@@ -444,6 +488,422 @@ static void test_HlinkCreateExtensionServices(void)
     IAuthenticate_Release(authenticate);
 }
 
+static void test_HlinkParseDisplayName(void)
+{
+    IMoniker *mon = NULL;
+    LPWSTR name;
+    DWORD issys;
+    ULONG eaten = 0;
+    IBindCtx *bctx;
+    HRESULT hres;
+
+    static const WCHAR winehq_urlW[] =
+            {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
+            '/','s','i','t','e','/','a','b','o','u','t',0};
+    static const WCHAR invalid_urlW[] = {'t','e','s','t',':','1','2','3','a','b','c',0};
+    static const WCHAR clsid_nameW[] = {'c','l','s','i','d',':',
+            '2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-','A','2','D','8',
+            '-','0','8','0','0','2','B','3','0','3','0','9','D',':',0};
+
+    CreateBindCtx(0, &bctx);
+
+    hres = HlinkParseDisplayName(bctx, winehq_urlW, FALSE, &eaten, &mon);
+    ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
+    ok(eaten == sizeof(winehq_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
+    ok(mon != NULL, "mon == NULL\n");
+
+    hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
+    ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
+    ok(!lstrcmpW(name, winehq_urlW), "wrong display name %s\n", debugstr_w(name));
+    CoTaskMemFree(name);
+
+    hres = IMoniker_IsSystemMoniker(mon, &issys);
+    ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
+    ok(issys == MKSYS_URLMONIKER, "issys=%x\n", issys);
+
+    IMoniker_Release(mon);
+
+    hres = HlinkParseDisplayName(bctx, clsid_nameW, FALSE, &eaten, &mon);
+    ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
+    ok(eaten == sizeof(clsid_nameW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
+    ok(mon != NULL, "mon == NULL\n");
+
+    hres = IMoniker_IsSystemMoniker(mon, &issys);
+    ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
+    ok(issys == MKSYS_CLASSMONIKER, "issys=%x\n", issys);
+
+    IMoniker_Release(mon);
+
+    hres = HlinkParseDisplayName(bctx, invalid_urlW, FALSE, &eaten, &mon);
+     ok(hres == S_OK, "HlinkParseDisplayName failed: %08x\n", hres);
+    ok(eaten == sizeof(invalid_urlW)/sizeof(WCHAR)-1, "eaten=%d\n", eaten);
+    ok(mon != NULL, "mon == NULL\n");
+
+    hres = IMoniker_GetDisplayName(mon, bctx, 0, &name);
+    ok(hres == S_OK, "GetDiasplayName failed: %08x\n", hres);
+    ok(!lstrcmpW(name, invalid_urlW), "wrong display name %s\n", debugstr_w(name));
+    CoTaskMemFree(name);
+
+    hres = IMoniker_IsSystemMoniker(mon, &issys);
+    ok(hres == S_OK, "IsSystemMoniker failed: %08x\n", hres);
+    ok(issys == MKSYS_FILEMONIKER, "issys=%x\n", issys);
+
+    IBindCtx_Release(bctx);
+}
+
+static IBindCtx *_bctx;
+
+static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
+{
+    ok(0, "unexpected call\n");
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
+        REFGUID guidService, REFIID riid, void **ppv)
+{
+    ok(0, "unexpected service %s\n", debugstr_guid(guidService));
+    return E_NOINTERFACE;
+}
+
+static IServiceProviderVtbl ServiceProviderVtbl = {
+    ServiceProvider_QueryInterface,
+    ServiceProvider_AddRef,
+    ServiceProvider_Release,
+    ServiceProvider_QueryService
+};
+
+static IServiceProvider ServiceProvider = { &ServiceProviderVtbl };
+
+static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
+{
+    *ppv = NULL;
+
+    if(IsEqualGUID(riid, &IID_IServiceProvider)) {
+        *ppv = &ServiceProvider;
+       return S_OK;
+    }
+
+    ok(0, "unexpected interface %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
+        IBinding *pib)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
+        ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
+        DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
+    BindStatusCallback_QueryInterface,
+    BindStatusCallback_AddRef,
+    BindStatusCallback_Release,
+    BindStatusCallback_OnStartBinding,
+    BindStatusCallback_GetPriority,
+    BindStatusCallback_OnLowResource,
+    BindStatusCallback_OnProgress,
+    BindStatusCallback_OnStopBinding,
+    BindStatusCallback_GetBindInfo,
+    BindStatusCallback_OnDataAvailable,
+    BindStatusCallback_OnObjectAvailable
+};
+
+static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl };
+
+static HRESULT WINAPI Moniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
+{
+    *ppv = NULL;
+
+    ok(0, "unexpected riid: %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI Moniker_AddRef(IMoniker *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI Moniker_Release(IMoniker *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI Moniker_GetClassID(IMoniker *iface, CLSID *pClassID)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_IsDirty(IMoniker *iface)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_Load(IMoniker *iface, IStream *pStm)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_BindToObject(IMoniker *iface, IBindCtx *pcb, IMoniker *pmkToLeft,
+        REFIID riidResult, void **ppvResult)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_BindToStorage(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
+        REFIID riid, void **ppv)
+{
+    IUnknown *unk;
+    HRESULT hres;
+
+    static OLECHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
+
+    CHECK_EXPECT(BindToStorage);
+
+    ok(pbc == _bctx, "pbc != _bctx\n");
+    ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
+    ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", debugstr_guid(riid));
+    ok(ppv != NULL, "ppv == NULL\n");
+    ok(*ppv == NULL, "*ppv=%p\n", *ppv);
+
+    hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
+    ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres);
+    ok(unk != NULL, "unk == NULL\n");
+
+    IUnknown_Release(unk);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI Moniker_Reduce(IMoniker *iface, IBindCtx *pbc, DWORD dwReduceHowFar,
+        IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
+        BOOL fOnlyIfNotGeneric, IMoniker **ppnkComposite)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_Enum(IMoniker *iface, BOOL fForwrd, IEnumMoniker **ppenumMoniker)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_Hash(IMoniker *iface, DWORD *pdwHash)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_IsRunning(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
+        IMoniker *pmkNewlyRunning)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_GetTimeOfLastChange(IMoniker *iface, IBindCtx *pbc,
+        IMoniker *pmkToLeft, FILETIME *pFileTime)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_Inverse(IMoniker *iface, IMoniker **ppmk)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther,
+        IMoniker **ppmkPrefix)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_RelativePathTo(IMoniker *iface, IMoniker *pmkOther,
+        IMoniker **pmkRelPath)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc,
+        IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
+{
+    static const WCHAR winehq_urlW[] =
+            {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g',
+            '/','s','i','t','e','/','a','b','o','u','t',0};
+
+    CHECK_EXPECT(GetDisplayName);
+
+    ok(pbc != NULL, "pbc == NULL\n");
+    ok(pbc != _bctx, "pbc == _bctx\n");
+    ok(pmkToLeft == NULL, "pmkToLeft=%p\n", pmkToLeft);
+
+    *ppszDisplayName = CoTaskMemAlloc(sizeof(winehq_urlW));
+    memcpy(*ppszDisplayName, winehq_urlW, sizeof(winehq_urlW));
+    return S_OK;
+}
+
+static HRESULT WINAPI Moniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc,
+        IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Moniker_IsSystemMoniker(IMoniker *iface, DWORD *pdwMksys)
+{
+    CHECK_EXPECT2(IsSystemMoniker);
+
+    *pdwMksys = MKSYS_URLMONIKER;
+    return S_OK;
+}
+
+static IMonikerVtbl MonikerVtbl = {
+    Moniker_QueryInterface,
+    Moniker_AddRef,
+    Moniker_Release,
+    Moniker_GetClassID,
+    Moniker_IsDirty,
+    Moniker_Load,
+    Moniker_Save,
+    Moniker_GetSizeMax,
+    Moniker_BindToObject,
+    Moniker_BindToStorage,
+    Moniker_Reduce,
+    Moniker_ComposeWith,
+    Moniker_Enum,
+    Moniker_IsEqual,
+    Moniker_Hash,
+    Moniker_IsRunning,
+    Moniker_GetTimeOfLastChange,
+    Moniker_Inverse,
+    Moniker_CommonPrefixWith,
+    Moniker_RelativePathTo,
+    Moniker_GetDisplayName,
+    Moniker_ParseDisplayName,
+    Moniker_IsSystemMoniker
+};
+
+static IMoniker Moniker = { &MonikerVtbl };
+
+static void test_HlinkResolveMonikerForData(void)
+{
+    IBindCtx *bctx;
+    HRESULT hres;
+
+    CreateBindCtx(0, &bctx);
+    _bctx = bctx;
+
+    SET_EXPECT(IsSystemMoniker);
+    SET_EXPECT(GetDisplayName);
+    SET_EXPECT(BindToStorage);
+
+    hres = HlinkResolveMonikerForData(&Moniker, 0, bctx, 0, NULL, &BindStatusCallback, NULL);
+    ok(hres == S_OK, "HlinkResolveMonikerForData failed: %08x\n", hres);
+
+    CHECK_CALLED(IsSystemMoniker);
+    CHECK_CALLED(GetDisplayName);
+    CHECK_CALLED(BindToStorage);
+
+    IBindCtx_Release(bctx);
+}
+
 START_TEST(hlink)
 {
     CoInitialize(NULL);
@@ -453,6 +913,8 @@ START_TEST(hlink)
     test_persist();
     test_special_reference();
     test_HlinkCreateExtensionServices();
+    test_HlinkParseDisplayName();
+    test_HlinkResolveMonikerForData();
 
     CoUninitialize();
 }
index de30316..a66a08a 100644 (file)
@@ -5,13 +5,13 @@
        <include base="hlink_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>hlink.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>hlink</library>
        <library>ole32</library>
        <library>kernel32</library>
        <library>uuid</library>
        <library>ntdll</library>
-       <file>hlink.c</file>
-       <file>testlist.c</file>
 </module>
 </group>
diff --git a/rostests/winetests/imm32/imm32.c b/rostests/winetests/imm32/imm32.c
new file mode 100644 (file)
index 0000000..d948f54
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Unit tests for imm32
+ *
+ * Copyright (c) 2008 Michael Jung
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winuser.h"
+#include "imm.h"
+
+#define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
+
+/*
+ * msgspy - record and analyse message traces sent to a certain window
+ */
+static struct _msg_spy {
+    HWND         hwnd;
+    HHOOK        get_msg_hook;
+    HHOOK        call_wnd_proc_hook;
+    CWPSTRUCT    msgs[32];
+    unsigned int i_msg;
+} msg_spy;
+
+static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
+{
+    if (HC_ACTION == nCode) {
+        MSG *msg = (MSG*)lParam;
+
+        if ((msg->hwnd == msg_spy.hwnd) &&
+            (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
+        {
+            msg_spy.msgs[msg_spy.i_msg].hwnd    = msg->hwnd;
+            msg_spy.msgs[msg_spy.i_msg].message = msg->message;
+            msg_spy.msgs[msg_spy.i_msg].wParam  = msg->wParam;
+            msg_spy.msgs[msg_spy.i_msg].lParam  = msg->lParam;
+            msg_spy.i_msg++;
+        }
+    }
+
+    return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
+}
+
+static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
+                                             LPARAM lParam)
+{
+    if (HC_ACTION == nCode) {
+        CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
+
+        if ((cwp->hwnd == msg_spy.hwnd) &&
+            (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
+        {
+            memcpy(&msg_spy.msgs[msg_spy.i_msg], cwp, sizeof(msg_spy.msgs[0]));
+            msg_spy.i_msg++;
+        }
+    }
+
+    return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
+}
+
+static void msg_spy_pump_msg_queue(void) {
+    MSG msg;
+
+    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+    return;
+}
+
+static void msg_spy_flush_msgs(void) {
+    msg_spy_pump_msg_queue();
+    msg_spy.i_msg = 0;
+}
+
+static CWPSTRUCT* msg_spy_find_msg(UINT message) {
+    int i;
+
+    msg_spy_pump_msg_queue();
+
+    if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
+        fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
+                __FILE__, __LINE__);
+
+    for (i = 0; i < msg_spy.i_msg; i++)
+        if (msg_spy.msgs[i].message == message)
+            return &msg_spy.msgs[i];
+
+    return NULL;
+}
+
+static void msg_spy_init(HWND hwnd) {
+    msg_spy.hwnd = hwnd;
+    msg_spy.get_msg_hook =
+            SetWindowsHookEx(WH_GETMESSAGE, get_msg_filter, GetModuleHandle(0),
+                             GetCurrentThreadId());
+    msg_spy.call_wnd_proc_hook =
+            SetWindowsHookEx(WH_CALLWNDPROC, call_wnd_proc_filter,
+                             GetModuleHandle(0), GetCurrentThreadId());
+    msg_spy.i_msg = 0;
+
+    msg_spy_flush_msgs();
+}
+
+static void msg_spy_cleanup() {
+    if (msg_spy.get_msg_hook)
+        UnhookWindowsHookEx(msg_spy.get_msg_hook);
+    if (msg_spy.call_wnd_proc_hook)
+        UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
+    memset(&msg_spy, 0, sizeof(msg_spy));
+}
+
+/*
+ * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
+ * messages being sent to this window in response.
+ */
+static const char wndcls[] = "winetest_imm32_wndcls";
+static HWND hwnd;
+
+static int init(void) {
+    WNDCLASSEX wc;
+
+    wc.cbSize        = sizeof(WNDCLASSEX);
+    wc.style         = 0;
+    wc.lpfnWndProc   = DefWindowProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = GetModuleHandle(0);
+    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
+    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
+    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+    wc.lpszMenuName  = NULL;
+    wc.lpszClassName = wndcls;
+    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
+
+    if (!RegisterClassExA(&wc))
+        return 0;
+
+    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
+                          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
+                          240, 120, NULL, NULL, GetModuleHandle(0), NULL);
+    if (!hwnd)
+        return 0;
+
+    ShowWindow(hwnd, SW_SHOWNORMAL);
+    UpdateWindow(hwnd);
+
+    msg_spy_init(hwnd);
+
+    return 1;
+}
+
+static void cleanup(void) {
+    msg_spy_cleanup();
+    if (hwnd)
+        DestroyWindow(hwnd);
+    UnregisterClass(wndcls, GetModuleHandle(0));
+}
+
+static int test_ImmNotifyIME(void) {
+    static const char string[] = "wine";
+    char resstr[16] = "";
+    HIMC imc;
+
+    imc = ImmGetContext(hwnd);
+    msg_spy_flush_msgs();
+
+    ok(ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0), "Canceling an "
+       "empty composition string succeeds.\n");
+    ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
+       "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
+       "the composition string being canceled is empty.\n");
+
+    msg_spy_flush_msgs();
+
+    ImmSetCompositionString(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
+    ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+    ok(msg_spy_find_msg(WM_IME_COMPOSITION) != NULL, "Windows does post "
+       "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
+       "the composition string being canceled is non empty.\n");
+    ok(!ImmGetCompositionString(imc, GCS_COMPSTR, resstr, sizeof(resstr)),
+       "After being canceled the composition string is empty.\n");
+
+    msg_spy_flush_msgs();
+
+    ok(ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0), "Canceling an "
+       "empty composition string succeeds.\n");
+    ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
+       "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
+       "the composition string being canceled is empty.\n");
+
+    msg_spy_flush_msgs();
+    ImmReleaseContext(hwnd, imc);
+
+    return 0;
+}
+
+START_TEST(imm32) {
+    if (init())
+        test_ImmNotifyIME();
+    cleanup();
+}
diff --git a/rostests/winetests/imm32/imm32.rbuild b/rostests/winetests/imm32/imm32.rbuild
new file mode 100644 (file)
index 0000000..e61c139
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
+<module name="imm32_winetest" type="win32cui" installbase="bin" installname="imm32_winetest.exe" allowwarnings="true" entrypoint="0">
+       <include base="imm32_winetest">.</include>
+       <define name="WINVER">0x600</define>
+       <define name="_WIN32_WINNT">0x600</define>
+       <file>imm32.c</file>
+       <file>testlist.c</file>
+       <library>wine</library>
+       <library>imm32</library>
+       <library>user32</library>
+       <library>kernel32</library>
+       <library>ntdll</library>
+</module>
+</group>
diff --git a/rostests/winetests/imm32/testlist.c b/rostests/winetests/imm32/testlist.c
new file mode 100644 (file)
index 0000000..a7911d1
--- /dev/null
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_imm32(void);
+
+const struct test winetest_testlist[] =
+{
+    { "imm32", func_imm32 },
+    { 0, 0 }
+};
index abe12d9..a9e500a 100644 (file)
@@ -1,13 +1,15 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="lz32_winetest" type="win32cui" installbase="bin" installname="lz32_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="lz32_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>lzexpand_main.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>lz32</library>
        <library>kernel32</library>
        <library>ntdll</library>
-       <file>lzexpand_main.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index b51067b..c67883a 100644 (file)
@@ -54,10 +54,7 @@ static char filename2[] = "testfile.yyy";
    a simple text file with the contents "This is a test file."
  
    The file was created using COMPRESS.EXE from the Windows Server 2003
-   Resource Kit from Microsoft.  The resource kit was retrieved from the
-   following URL:  
-
-   http://www.microsoft.com/downloads/details.aspx?FamilyID=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en
+   Resource Kit from Microsoft.
  */
 static const unsigned char compressed_file[] = 
   {0x53,0x5A,0x44,0x44,0x88,0xF0,0x27,0x33,0x41,
index 8712bb2..6da38a3 100644 (file)
@@ -1,15 +1,17 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="mapi32_winetest" type="win32cui" installbase="bin" installname="mapi32_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="mapi32_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
-       <library>wine</library>
-       <library>kernel32</library>
-       <library>uuid</library>
-       <library>ntdll</library>
        <file>imalloc.c</file>
        <file>prop.c</file>
        <file>util.c</file>
        <file>testlist.c</file>
+       <library>wine</library>
+       <library>kernel32</library>
+       <library>uuid</library>
+       <library>ntdll</library>
 </module>
+</group>
index d78eb36..adc1d85 100644 (file)
@@ -24,6 +24,7 @@
 #include "winuser.h"
 #include "winerror.h"
 #include "winnt.h"
+#include "initguid.h"
 #include "mapiutil.h"
 #include "mapitags.h"
 
index 748cb29..2f508f5 100644 (file)
@@ -1,15 +1,17 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<group>
 <module name="mlang_winetest" type="win32cui" installbase="bin" installname="mlang_winetest.exe" allowwarnings="true" entrypoint="0">
        <include base="mlang_winetest">.</include>
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
+       <file>mlang.c</file>
+       <file>testlist.c</file>
        <library>wine</library>
        <library>ole32</library>
        <library>gdi32</library>
        <library>kernel32</library>
        <library>uuid</library>
        <library>ntdll</library>
-       <file>mlang.c</file>
-       <file>testlist.c</file>
 </module>
+</group>
index 90e4b1f..f2e5c13 100644 (file)
@@ -465,85 +465,138 @@ static DISPID get_dispid( IDispatch *disp, const char *name )
 
 static void test_dispid(void)
 {
-    ok( get_dispid( pInstaller, "CreateRecord" ) == 1, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "OpenPackage" ) == 2, "dispid wrong\n");
-    todo_wine ok( get_dispid( pInstaller, "OpenProduct" ) == 3, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "OpenDatabase" ) == 4, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "SummaryInformation" ) == 5, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "UILevel" ) == 6, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "EnableLog" ) == 7, "dispid wrong\n");
-    }
-    ok( get_dispid( pInstaller, "InstallProduct" ) == 8, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "Version" ) == 9, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "LastErrorRecord" ) == 10, "dispid wrong\n");
-    }
-    ok( get_dispid( pInstaller, "RegistryValue" ) == 11, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "Environment" ) == 12, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FileAttributes" ) == 13, "dispid wrong\n");
+    DISPID dispid;
 
-    ok( get_dispid( pInstaller, "FileSize" ) == 15, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FileVersion" ) == 16, "dispid wrong\n");
+    dispid = get_dispid(pInstaller, "CreateRecord");
+    ok(dispid  == 1, "Expected 1, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "OpenPackage");
+    ok(dispid  == 2, "Expected 2, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "OpenDatabase");
+    ok(dispid == 4, "Expected 4, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "UILevel" );
+    ok(dispid == 6, "Expected 6, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "InstallProduct");
+    ok(dispid == 8, "Expected 8, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "Version");
+    ok(dispid == 9, "Expected 9, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "RegistryValue");
+    ok(dispid == 11, "Expected 11, got %d\n", dispid);
+    todo_wine
+    {
+        dispid = get_dispid(pInstaller, "OpenProduct");
+        ok(dispid  == 3, "Expected 3, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "SummaryInformation");
+        ok(dispid == 5, "Expected 5, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "EnableLog");
+        ok(dispid == 7, "Expected 7, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "LastErrorRecord");
+        ok(dispid == 10, "Expected 10, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "Environment");
+        ok(dispid == 12, "Expected 12, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FileAttributes");
+        ok(dispid == 13, "Expected 13, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FileSize");
+        ok(dispid == 15, "Expected 15, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FileVersion");
+        ok(dispid == 16, "Expected 16, got %d\n", dispid);
     }
-    ok( get_dispid( pInstaller, "ProductState" ) == 17, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ProductInfo" ) == 18, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "ConfigureProduct" ) == 19, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ReinstallProduct" ) == 20 , "dispid wrong\n");
-    ok( get_dispid( pInstaller, "CollectUserInfo" ) == 21, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ApplyPatch" ) == 22, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FeatureParent" ) == 23, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FeatureState" ) == 24, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "UseFeature" ) == 25, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FeatureUsageCount" ) == 26, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FeatureUsageDate" ) == 27, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ConfigureFeature" ) == 28, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ReinstallFeature" ) == 29, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ProvideComponent" ) == 30, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ComponentPath" ) == 31, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ProvideQualifiedComponent" ) == 32, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "QualifierDescription" ) == 33, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ComponentQualifiers" ) == 34, "dispid wrong\n");
+    dispid = get_dispid(pInstaller, "ProductState");
+    ok(dispid == 17, "Expected 17, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "ProductInfo");
+    ok(dispid == 18, "Expected 18, got %d\n", dispid);
+    todo_wine
+    {
+        dispid = get_dispid(pInstaller, "ConfigureProduct");
+        ok(dispid == 19, "Expected 19, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ReinstallProduct");
+        ok(dispid == 20 , "Expected 20, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "CollectUserInfo");
+        ok(dispid == 21, "Expected 21, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ApplyPatch");
+        ok(dispid == 22, "Expected 22, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FeatureParent");
+        ok(dispid == 23, "Expected 23, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FeatureState");
+        ok(dispid == 24, "Expected 24, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "UseFeature");
+        ok(dispid == 25, "Expected 25, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FeatureUsageCount");
+        ok(dispid == 26, "Expected 26, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FeatureUsageDate");
+        ok(dispid == 27, "Expected 27, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ConfigureFeature");
+        ok(dispid == 28, "Expected 28, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ReinstallFeature");
+        ok(dispid == 29, "Expected 29, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ProvideComponent");
+        ok(dispid == 30, "Expected 30, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ComponentPath");
+        ok(dispid == 31, "Expected 31, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ProvideQualifiedComponent");
+        ok(dispid == 32, "Expected 32, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "QualifierDescription");
+        ok(dispid == 33, "Expected 33, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ComponentQualifiers");
+        ok(dispid == 34, "Expected 34, got %d\n", dispid);
     }
-    ok( get_dispid( pInstaller, "Products" ) == 35, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "Features" ) == 36, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "Components" ) == 37, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ComponentClients" ) == 38, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "Patches" ) == 39, "dispid wrong\n");
+    dispid = get_dispid(pInstaller, "Products");
+    ok(dispid == 35, "Expected 35, got %d\n", dispid);
+    todo_wine
+    {
+        dispid = get_dispid(pInstaller, "Features");
+        ok(dispid == 36, "Expected 36, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "Components");
+        ok(dispid == 37, "Expected 37, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ComponentClients");
+        ok(dispid == 38, "Expected 38, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "Patches");
+        ok(dispid == 39, "Expected 39, got %d\n", dispid);
     }
-    ok( get_dispid( pInstaller, "RelatedProducts" ) == 40, "dispid wrong\n");
-    todo_wine {
-    ok( get_dispid( pInstaller, "PatchInfo" ) == 41, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "PatchTransforms" ) == 42, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "AddSource" ) == 43, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ClearSourceList" ) == 44, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ForceSourceListResolution" ) == 45, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ShortcutTarget" ) == 46, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FileHash" ) == 47, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "FileSignatureInfo" ) == 48, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "RemovePatches" ) == 49, "dispid wrong\n");
-
-    ok( get_dispid( pInstaller, "ApplyMultiplePatches" ) == 51, "dispid wrong\n");
-    ok( get_dispid( pInstaller, "ProductsEx" ) ==  52, "dispid wrong\n");
-
-    ok( get_dispid( pInstaller, "PatchesEx" ) == 55, "dispid wrong\n");
-
-    ok( get_dispid( pInstaller, "ExtractPatchXMLData" ) == 57, "dispid wrong\n");
+    dispid = get_dispid(pInstaller, "RelatedProducts");
+    ok(dispid == 40, "Expected 40, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "RemovePatches");
+    ok(dispid == 49 || dispid == -1, "Expected 49 or -1, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "ApplyMultiplePatches");
+    ok(dispid == 51 || dispid == -1, "Expected 51 or -1, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "ProductsEx");
+    ok(dispid == 52 || dispid == -1, "Expected 52 or -1, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "PatchesEx");
+    ok(dispid == 55 || dispid == -1, "Expected 55 or -1, got %d\n", dispid);
+    dispid = get_dispid(pInstaller, "ExtractPatchXMLData");
+    ok(dispid == 57 || dispid == -1, "Expected 57 or -1, got %d\n", dispid);
+    todo_wine
+    {
+        dispid = get_dispid(pInstaller, "PatchInfo");
+        ok(dispid == 41, "Expected 41, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "PatchTransforms");
+        ok(dispid == 42, "Expected 42, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "AddSource");
+        ok(dispid == 43, "Expected 43, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ClearSourceList");
+        ok(dispid == 44, "Expected 44, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ForceSourceListResolution");
+        ok(dispid == 45, "Expected 45, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "ShortcutTarget");
+        ok(dispid == 46, "Expected 46, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FileHash");
+        ok(dispid == 47, "Expected 47, got %d\n", dispid);
+        dispid = get_dispid(pInstaller, "FileSignatureInfo");
+        ok(dispid == 48, "Expected 48, got %d\n", dispid);
     }
 
     /* MSDN claims the following functions exist but IDispatch->GetIDsOfNames disagrees */
-    if (0)
-    {
-        get_dispid( pInstaller, "ProductElevated" );
-        get_dispid( pInstaller, "ProductInfoFromScript" );
-        get_dispid( pInstaller, "ProvideAssembly" );
-        get_dispid( pInstaller, "CreateAdvertiseScript" );
-        get_dispid( pInstaller, "AdvertiseProduct" );
-        get_dispid( pInstaller, "PatchFiles" );
-    }
+    dispid = get_dispid( pInstaller, "ProductElevated" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "ProductInfoFromScript" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "ProvideAssembly" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "CreateAdvertiseScript" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "AdvertiseProduct" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
+    dispid = get_dispid( pInstaller, "PatchFiles" );
+    ok(dispid == -1, "Expected -1, got %d\n", dispid);
 }
 
 /* Test basic IDispatch functions */
@@ -1526,10 +1579,8 @@ static void test_Database(IDispatch *pDatabase, BOOL readonly)
             ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr);
             ok_exception(hr, szModifyModeRecord);
 
-            /* View::Modify with MSIMODIFY_REFRESH should undo our changes */
             hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord);
-            /* Wine's MsiViewModify currently does not support MSIMODIFY_REFRESH */
-            todo_wine ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
+            ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr);
 
             /* Record::StringDataGet, confirm that the record is back to its unmodified value */
             memset(szString, 0, sizeof(szString));
@@ -1762,6 +1813,7 @@ static void test_Installer_RegistryValue(void)
     VARIANTARG vararg;
     WCHAR szString[MAX_PATH];
     HKEY hkey, hkey_sub;
+    HKEY curr_user = (HKEY)1;
     HRESULT hr;
     BOOL bRet;
 
@@ -1769,16 +1821,16 @@ static void test_Installer_RegistryValue(void)
     if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey );
 
     /* Does our key exist? Shouldn't; check with all three possible value parameter types */
-    hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
+    hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
     ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n");
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
+    hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
 
     /* Create key */
@@ -1807,87 +1859,87 @@ static void test_Installer_RegistryValue(void)
 
     /* Does our key exist? It should, and make sure we retrieve the correct default value */
     bRet = FALSE;
-    hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet);
+    hr = Installer_RegistryValueE(curr_user, szKey, &bRet);
     ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr);
     ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n");
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, NULL, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
 
     /* Ask for the value of a nonexistent key */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szExpand, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szExpand, szString);
     ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
 
     /* Get values of keys */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szOne, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szOne, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne);
 
     VariantInit(&vararg);
     V_VT(&vararg) = VT_BSTR;
     V_BSTR(&vararg) = SysAllocString(szTwo);
-    hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_I4);
+    hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_I4);
     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
     ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult));
     VariantClear(&varresult);
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szThree, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szThree, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY);
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFour, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szFour, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour);
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFive, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szFive, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFiveHi);
 
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szSix, szString);
+    hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString);
     ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr);
     ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_);
 
     VariantInit(&vararg);
     V_VT(&vararg) = VT_BSTR;
     V_BSTR(&vararg) = SysAllocString(szSeven);
-    hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_EMPTY);
+    hr = Installer_RegistryValue(curr_user, szKey, vararg, &varresult, VT_EMPTY);
     ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr);
 
     /* Get string class name for the key */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR);
+    hr = Installer_RegistryValueI(curr_user, szKey, 0, szString, VT_BSTR);
     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank);
 
     /* Get name of a value by positive number (RegEnumValue like), valid index */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 2, szString, VT_BSTR);
+    hr = Installer_RegistryValueI(curr_user, szKey, 2, szString, VT_BSTR);
     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
     /* RegEnumValue order seems different on wine */
     todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo);
 
     /* Get name of a value by positive number (RegEnumValue like), invalid index */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 10, szString, VT_EMPTY);
+    hr = Installer_RegistryValueI(curr_user, szKey, 10, szString, VT_EMPTY);
     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
 
     /* Get name of a subkey by negative number (RegEnumValue like), valid index */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -1, szString, VT_BSTR);
+    hr = Installer_RegistryValueI(curr_user, szKey, -1, szString, VT_BSTR);
     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
     ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight);
 
     /* Get name of a subkey by negative number (RegEnumValue like), invalid index */
     memset(szString, 0, sizeof(szString));
-    hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -10, szString, VT_EMPTY);
+    hr = Installer_RegistryValueI(curr_user, szKey, -10, szString, VT_EMPTY);
     ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr);
 
     /* clean up */
@@ -2116,6 +2168,12 @@ static void test_Installer_InstallProduct(void)
 
     /* Installer::InstallProduct */
     hr = Installer_InstallProduct(szMsifile, NULL);
+    if (hr == DISP_E_EXCEPTION)
+    {
+        skip("Installer object not supported.\n");
+        delete_test_files();
+        return;
+    }
     ok(hr == S_OK, "Installer_InstallProduct failed, hresult 0x%08x\n", hr);
 
     /* Installer::ProductState for our product code, which has been installed */
@@ -2140,14 +2198,14 @@ static void test_Installer_InstallProduct(void)
     /* Package name */
     memset(szString, 0, sizeof(szString));
     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PACKAGENAMEW, szString);
-    todo_wine ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
+    ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
     todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMsifile);
 
     /* Product name */
     memset(szString, 0, sizeof(szString));
     hr = Installer_ProductInfo(szProductCode, WINE_INSTALLPROPERTY_PRODUCTNAMEW, szString);
     ok(hr == S_OK, "Installer_ProductInfo failed, hresult 0x%08x\n", hr);
-    ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
+    todo_wine ok_w2("Installer_ProductInfo returned %s but expected %s\n", szString, szMSITEST);
 
     /* Installer::Products */
     test_Installer_Products(TRUE);
index e912a3a..9be1691 100644 (file)
@@ -1643,17 +1643,18 @@ static void test_msiimport(void)
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
 
     r = add_table_to_db(hdb, endlines1);
-    todo_wine
+    if (r == ERROR_FUNCTION_FAILED)
     {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+        /* win9x doesn't handle this case */
+        skip("endlines not handled correctly.\n");
+        MsiCloseHandle(hdb);
+        DeleteFileA(msifile);
+        return;
     }
 
     r = add_table_to_db(hdb, endlines2);
-    todo_wine
-    {
-        ok(r == ERROR_FUNCTION_FAILED,
-           "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
-    }
+    ok(r == ERROR_FUNCTION_FAILED,
+       "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
 
     query = "SELECT * FROM `TestTable`";
     r = MsiDatabaseOpenView(hdb, query, &view);
@@ -1752,7 +1753,8 @@ static void test_msiimport(void)
     MsiCloseHandle(rec);
 
     r = MsiViewFetch(view, &rec);
-    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+    ok(r == ERROR_NO_MORE_ITEMS,
+       "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
 
     r = MsiViewClose(view);
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
@@ -1761,37 +1763,28 @@ static void test_msiimport(void)
 
     query = "SELECT * FROM `Table`";
     r = MsiDatabaseOpenView(hdb, query, &view);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
 
     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
     count = MsiRecordGetFieldCount(rec);
-    todo_wine
-    {
-        ok(count == 6, "Expected 6, got %d\n", count);
-        ok(check_record(rec, 1, "A"), "Expected A\n");
-        ok(check_record(rec, 2, "B"), "Expected B\n");
-        ok(check_record(rec, 3, "C"), "Expected C\n");
-        ok(check_record(rec, 4, "D"), "Expected D\n");
-        ok(check_record(rec, 5, "E"), "Expected E\n");
-        ok(check_record(rec, 6, "F"), "Expected F\n");
-    }
+    ok(count == 6, "Expected 6, got %d\n", count);
+    ok(check_record(rec, 1, "A"), "Expected A\n");
+    ok(check_record(rec, 2, "B"), "Expected B\n");
+    ok(check_record(rec, 3, "C"), "Expected C\n");
+    ok(check_record(rec, 4, "D"), "Expected D\n");
+    ok(check_record(rec, 5, "E"), "Expected E\n");
+    ok(check_record(rec, 6, "F"), "Expected F\n");
     MsiCloseHandle(rec);
 
     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
     count = MsiRecordGetFieldCount(rec);
-    todo_wine
-    {
-        ok(count == 6, "Expected 6, got %d\n", count);
-        ok(check_record(rec, 1, "s72"), "Expected s72\n");
-        ok(check_record(rec, 2, "s72"), "Expected s72\n");
-        ok(check_record(rec, 3, "s72"), "Expected s72\n");
-        ok(check_record(rec, 4, "s72"), "Expected s72\n");
-        ok(check_record(rec, 5, "s72"), "Expected s72\n");
-        ok(check_record(rec, 6, "s72"), "Expected s72\n");
-    }
+    ok(count == 6, "Expected 6, got %d\n", count);
+    ok(check_record(rec, 1, "s72"), "Expected s72\n");
+    ok(check_record(rec, 2, "s72"), "Expected s72\n");
+    ok(check_record(rec, 3, "s72"), "Expected s72\n");
+    ok(check_record(rec, 4, "s72"), "Expected s72\n");
+    ok(check_record(rec, 5, "s72"), "Expected s72\n");
+    ok(check_record(rec, 6, "s72"), "Expected s72\n");
     MsiCloseHandle(rec);
 
     MsiViewClose(view);
@@ -1799,51 +1792,36 @@ static void test_msiimport(void)
 
     query = "SELECT * FROM `Table`";
     r = MsiDatabaseOpenView(hdb, query, &view);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
 
     r = MsiViewExecute(view, 0);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
 
     r = MsiViewFetch(view, &rec);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-        ok(check_record(rec, 1, "a"), "Expected 'a'\n");
-        ok(check_record(rec, 2, "b"), "Expected 'b'\n");
-        ok(check_record(rec, 3, "c"), "Expected 'c'\n");
-        ok(check_record(rec, 4, "d"), "Expected 'd'\n");
-        ok(check_record(rec, 5, "e"), "Expected 'e'\n");
-        ok(check_record(rec, 6, "f"), "Expected 'f'\n");
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(check_record(rec, 1, "a"), "Expected 'a'\n");
+    ok(check_record(rec, 2, "b"), "Expected 'b'\n");
+    ok(check_record(rec, 3, "c"), "Expected 'c'\n");
+    ok(check_record(rec, 4, "d"), "Expected 'd'\n");
+    ok(check_record(rec, 5, "e"), "Expected 'e'\n");
+    ok(check_record(rec, 6, "f"), "Expected 'f'\n");
 
     MsiCloseHandle(rec);
 
     r = MsiViewFetch(view, &rec);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-        ok(check_record(rec, 1, "g"), "Expected 'g'\n");
-        ok(check_record(rec, 2, "h"), "Expected 'h'\n");
-        ok(check_record(rec, 3, "i"), "Expected 'i'\n");
-        ok(check_record(rec, 4, "j"), "Expected 'j'\n");
-        ok(check_record(rec, 5, "k"), "Expected 'k'\n");
-        ok(check_record(rec, 6, "l"), "Expected 'l'\n");
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(check_record(rec, 1, "g"), "Expected 'g'\n");
+    ok(check_record(rec, 2, "h"), "Expected 'h'\n");
+    ok(check_record(rec, 3, "i"), "Expected 'i'\n");
+    ok(check_record(rec, 4, "j"), "Expected 'j'\n");
+    ok(check_record(rec, 5, "k"), "Expected 'k'\n");
+    ok(check_record(rec, 6, "l"), "Expected 'l'\n");
 
     MsiCloseHandle(rec);
 
     r = MsiViewFetch(view, &rec);
-    todo_wine
-    {
-        ok(r == ERROR_NO_MORE_ITEMS,
-           "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
-    }
+    ok(r == ERROR_NO_MORE_ITEMS,
+       "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
 
     MsiViewClose(view);
     MsiCloseHandle(view);
@@ -3235,6 +3213,11 @@ static void test_join(void)
     MsiViewClose(hview);
     MsiCloseHandle(hview);
 
+    query = "SELECT * FROM `Nonexistent`, `One`";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok( r == ERROR_BAD_QUERY_SYNTAX,
+        "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r );
+
     MsiCloseHandle(hdb);
     DeleteFile(msifile);
 }
@@ -4799,6 +4782,10 @@ static void test_order(void)
     hdb = create_db();
     ok(hdb, "failed to create db\n");
 
+    query = "CREATE TABLE `Empty` ( `A` SHORT NOT NULL PRIMARY KEY `A`)";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
     query = "CREATE TABLE `Mesa` ( `A` SHORT NOT NULL, `B` SHORT, `C` SHORT PRIMARY KEY `A`)";
     r = run_query(hdb, 0, query);
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
@@ -4984,6 +4971,18 @@ static void test_order(void)
     r = MsiViewFetch(hview, &hrec);
     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
 
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+    query = "SELECT * FROM `Empty` ORDER BY `A`";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+
     MsiViewClose(hview);
     MsiCloseHandle(hview);
     MsiCloseHandle(hdb);
@@ -5255,10 +5254,7 @@ static void test_quotes(void)
     write_file("import.idt", import_dat, (sizeof(import_dat) - 1) * sizeof(char));
 
     r = MsiDatabaseImportA(hdb, CURR_DIR, "import.idt");
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
 
     DeleteFileA("import.idt");
 
@@ -5275,19 +5271,13 @@ static void test_quotes(void)
     size = MAX_PATH;
     r = MsiRecordGetString(hrec, 1, buf, &size);
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    todo_wine
-    {
-        ok(!lstrcmp(buf, "This is a new 'string' ok"),
-           "Expected \"This is a new 'string' ok\", got %s\n", buf);
-    }
+    ok(!lstrcmp(buf, "This is a new 'string' ok"),
+       "Expected \"This is a new 'string' ok\", got %s\n", buf);
 
     MsiCloseHandle(hrec);
 
     r = MsiViewFetch(hview, &hrec);
-    todo_wine
-    {
-        ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
-    }
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
 
     MsiCloseHandle(hview);
 
@@ -5501,6 +5491,360 @@ static void test_carriagereturn(void)
     DeleteFileA(msifile);
 }
 
+static void test_noquotes(void)
+{
+    MSIHANDLE hdb, hview, hrec;
+    const char *query;
+    char buf[MAX_PATH];
+    UINT r;
+    DWORD size;
+
+    DeleteFile(msifile);
+
+    r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "CREATE TABLE Table ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "CREATE TABLE `Table` ( A CHAR(72) NOT NULL PRIMARY KEY `A` )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "CREATE TABLE `Table2` ( `A` CHAR(72) NOT NULL PRIMARY KEY A )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "CREATE TABLE `Table3` ( A CHAR(72) NOT NULL PRIMARY KEY A )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `_Tables`";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table2"), "Expected \"Table2\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table3"), "Expected \"Table3\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+    query = "SELECT * FROM `_Columns`";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf);
+
+    r = MsiRecordGetInteger(hrec, 2);
+    ok(r == 1, "Expected 1, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 3, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table2"), "Expected \"Table2\", got \"%s\"\n", buf);
+
+    r = MsiRecordGetInteger(hrec, 2);
+    ok(r == 1, "Expected 1, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 3, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "Table3"), "Expected \"Table3\", got \"%s\"\n", buf);
+
+    r = MsiRecordGetInteger(hrec, 2);
+    ok(r == 1, "Expected 1, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 3, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+    query = "INSERT INTO Table ( `A` ) VALUES ( 'hi' )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "INSERT INTO `Table` ( A ) VALUES ( 'hi' )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "INSERT INTO `Table` ( `A` ) VALUES ( hi )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "SELECT * FROM Table WHERE `A` = 'hi'";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "SELECT * FROM `Table` WHERE `A` = hi";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "SELECT * FROM Table";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "SELECT * FROM Table2";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+    query = "SELECT * FROM `Table` WHERE A = 'hi'";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buf, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf);
+
+    MsiCloseHandle(hrec);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+
+    MsiCloseHandle(hdb);
+    DeleteFileA(msifile);
+}
+
+static void read_file_data(LPCSTR filename, LPSTR buffer)
+{
+    OFSTRUCT ofs;
+    HFILE file;
+    DWORD read;
+
+    file = OpenFile(filename, &ofs, OF_READ);
+    ZeroMemory(buffer, MAX_PATH);
+    ReadFile((HANDLE)file, buffer, MAX_PATH, &read, NULL);
+    CloseHandle((HANDLE)file);
+}
+
+static void test_forcecodepage(void)
+{
+    MSIHANDLE hdb;
+    const char *query;
+    char buffer[MAX_PATH];
+    UINT r;
+
+    DeleteFile(msifile);
+
+    r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `_ForceCodepage`";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `_ForceCodepage`";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    r = MsiDatabaseCommit(hdb);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `_ForceCodepage`";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    MsiCloseHandle(hdb);
+
+    r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `_ForceCodepage`";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+    r = MsiDatabaseExport(hdb, "_ForceCodepage", CURR_DIR, "forcecodepage.idt");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    read_file_data("forcecodepage.idt", buffer);
+    ok(!lstrcmpA(buffer, "\r\n\r\n0\t_ForceCodepage\r\n"),
+       "Expected \"\r\n\r\n0\t_ForceCodepage\r\n\", got \"%s\"", buffer);
+
+    MsiCloseHandle(hdb);
+    DeleteFileA(msifile);
+    DeleteFileA("forcecodepage.idt");
+}
+
+static void test_viewmodify_refresh(void)
+{
+    MSIHANDLE hdb, hview, hrec;
+    const char *query;
+    char buffer[MAX_PATH];
+    UINT r;
+    DWORD size;
+
+    DeleteFile(msifile);
+
+    r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL, `B` INT PRIMARY KEY `A` )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hi', 1 )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `Table`";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "UPDATE `Table` SET `B` = 2 WHERE `A` = 'hi'";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buffer, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buffer, "hi"), "Expected \"hi\", got \"%s\"\n", buffer);
+    ok(size == 2, "Expected 2, got %d\n", size);
+
+    r = MsiRecordGetInteger(hrec, 2);
+    ok(r == 2, "Expected 2, got %d\n", r);
+
+    MsiCloseHandle(hrec);
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+
+    query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hello', 3 )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "SELECT * FROM `Table` WHERE `B` = 3";
+    r = MsiDatabaseOpenView(hdb, query, &hview);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiViewExecute(hview, 0);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewFetch(hview, &hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "UPDATE `Table` SET `B` = 2 WHERE `A` = 'hello'";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hithere', 3 )";
+    r = run_query(hdb, 0, query);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    size = MAX_PATH;
+    r = MsiRecordGetStringA(hrec, 1, buffer, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buffer, "hello"), "Expected \"hello\", got \"%s\"\n", buffer);
+    ok(size == 5, "Expected 5, got %d\n", size);
+
+    r = MsiRecordGetInteger(hrec, 2);
+    ok(r == 2, "Expected 2, got %d\n", r);
+
+    MsiCloseHandle(hrec);
+    MsiViewClose(hview);
+    MsiCloseHandle(hview);
+    MsiCloseHandle(hdb);
+    DeleteFileA(msifile);
+}
+
 START_TEST(db)
 {
     test_msidatabase();
@@ -5534,4 +5878,7 @@ START_TEST(db)
     test_deleterow();
     test_quotes();
     test_carriagereturn();
+    test_noquotes();
+    test_forcecodepage();
+    test_viewmodify_refresh();
 }
index aec347d..f4c1670 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <stdio.h>
 #include <windows.h>
+#include <shlwapi.h>
 #include <msi.h>
 #include <msiquery.h>
 
@@ -2176,6 +2177,7 @@ static void test_formatrecord_tables(void)
     CHAR buf[MAX_PATH];
     CHAR curr_dir[MAX_PATH];
     CHAR expected[MAX_PATH];
+    CHAR root[MAX_PATH];
     DWORD size;
     UINT r;
 
@@ -2304,48 +2306,57 @@ static void test_formatrecord_tables(void)
     r = MsiDoAction(hpkg, "CostFinalize");
     ok( r == ERROR_SUCCESS, "CostFinalize failed: %d\n", r);
 
+    size = MAX_PATH;
+    MsiGetProperty( hpkg, "ROOTDRIVE", root, &size );
+
+    sprintf( expected, "1: %sfrontal.txt ", root);
+
     /* frontal full file key */
     size = MAX_PATH;
     MsiRecordSetString( hrec, 1, "[#frontal_file]" );
     r = MsiFormatRecord( hpkg, hrec, buf, &size );
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
-    ok( !lstrcmp( buf, "1: C:\\frontal.txt " ), "Expected '1: C:\\frontal.txt ', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
 
     /* frontal short file key */
     size = MAX_PATH;
     MsiRecordSetString( hrec, 1, "[!frontal_file]" );
     r = MsiFormatRecord( hpkg, hrec, buf, &size );
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
-    ok( !lstrcmp( buf, "1: C:\\frontal.txt " ), "Expected '1: C:\\frontal.txt ', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
+
+    sprintf( expected, "1: %sI am a really long directory\\temporal.txt ", root);
 
     /* temporal full file key */
     size = MAX_PATH;
     MsiRecordSetString( hrec, 1, "[#temporal_file]" );
     r = MsiFormatRecord( hpkg, hrec, buf, &size );
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
-    ok( !lstrcmp( buf, "1: C:\\I am a really long directory\\temporal.txt " ),
-        "Expected '1: C:\\I am a really long directory\\temporal.txt ', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
 
     /* temporal short file key */
     size = MAX_PATH;
     MsiRecordSetString( hrec, 1, "[!temporal_file]" );
     r = MsiFormatRecord( hpkg, hrec, buf, &size );
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
-    ok( !lstrcmp( buf, "1: C:\\I am a really long directory\\temporal.txt " ),
-        "Expected '1: C:\\I am a really long directory\\temporal.txt ', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
 
     /* custom action 51, files don't exist */
     r = MsiDoAction( hpkg, "MyCustom" );
     ok( r == ERROR_SUCCESS, "MyCustom failed: %d\n", r);
 
+    sprintf( expected, "%sI am a really long directory\\temporal.txt", root);
+
     size = MAX_PATH;
     r = MsiGetProperty( hpkg, "prop", buf, &size );
     ok( r == ERROR_SUCCESS, "get property failed: %d\n", r);
-    ok( !lstrcmp( buf, "C:\\I am a really long directory\\temporal.txt" ),
-        "Expected 'C:\\I am a really long directory\\temporal.txt', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
+
+    sprintf( buf, "%sI am a really long directory", root );
+    CreateDirectory( buf, NULL );
 
-    CreateDirectory( "C:\\I am a really long directory", NULL );
-    create_test_file( "C:\\I am a really long directory\\temporal.txt" );
+    lstrcat( buf, "\\temporal.txt" );
+    create_test_file( buf );
 
     /* custom action 51, files exist */
     r = MsiDoAction( hpkg, "MyCustom" );
@@ -2356,8 +2367,7 @@ static void test_formatrecord_tables(void)
     ok( r == ERROR_SUCCESS, "get property failed: %d\n", r);
     todo_wine
     {
-        ok( !lstrcmp( buf, "C:\\I am a really long directory\\temporal.txt" ),
-            "Expected 'C:\\I am a really long directory\\temporal.txt', got %s\n", buf);
+        ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
     }
 
     /* custom action 51, escaped text 1 */
@@ -2387,13 +2397,14 @@ static void test_formatrecord_tables(void)
     ok( r == ERROR_SUCCESS, "get property failed: %d\n", r);
     ok( !lstrcmp( buf, "" ), "Expected '', got %s\n", buf);
 
+    sprintf( expected, "1: %sI am a really long directory\\ ", root);
+
     /* component with INSTALLSTATE_LOCAL */
     size = MAX_PATH;
     MsiRecordSetString( hrec, 1, "[$temporal]" );
     r = MsiFormatRecord( hpkg, hrec, buf, &size );
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
-    ok( !lstrcmp( buf, "1: C:\\I am a really long directory\\ " ),
-        "Expected '1: C:\\I am a really long directory\\ ', got %s\n", buf);
+    ok( !lstrcmp( buf, expected ), "Expected \"%s\", got \"%s\"\n", expected, buf);
 
     r = MsiSetComponentState( hpkg, "temporal", INSTALLSTATE_SOURCE );
     ok( r == ERROR_SUCCESS, "failed to set install state: %d\n", r);
@@ -2408,8 +2419,11 @@ static void test_formatrecord_tables(void)
     ok( r == ERROR_SUCCESS, "format record failed: %d\n", r);
     ok( !lstrcmp( buf, expected ), "Expected '%s', got %s\n", expected, buf);
 
-    DeleteFile( "C:\\I am a really long directory\\temporal.txt" );
-    RemoveDirectory( "C:\\I am a really long directory" );
+    sprintf( buf, "%sI am a really long directory\\temporal.txt", root );
+    DeleteFile( buf );
+
+    sprintf( buf, "%sI am a really long directory", root );
+    RemoveDirectory( buf );
 
     MsiCloseHandle( hrec );
     MsiCloseHandle( hpkg );
index b5892c0..5ac3852 100644 (file)
@@ -34,6 +34,8 @@
 
 static UINT (WINAPI *pMsiQueryComponentStateA)
     (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, INSTALLSTATE*);
+static UINT (WINAPI *pMsiSourceListEnumSourcesA)
+    (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPSTR, LPDWORD);
 static UINT (WINAPI *pMsiSourceListGetInfoA)
     (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPSTR, LPDWORD);
 
@@ -144,7 +146,9 @@ static const CHAR property_dat[] = "Property\tValue\n"
                                    "Setup\tSetup\n"
                                    "UpgradeCode\t{4C0EAA15-0264-4E5A-8758-609EF142B92D}\n"
                                    "AdminProperties\tPOSTADMIN\n"
-                                   "ROOTDRIVE\tC:\\\n";
+                                   "ROOTDRIVE\tC:\\\n"
+                                   "SERVNAME\tTestService\n"
+                                   "SERVDISP\tTestServiceDisp\n";
 
 static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
                                    "s72\ti2\tl255\tL255\tL0\ts72\n"
@@ -158,7 +162,7 @@ static const CHAR service_install_dat[] = "ServiceInstall\tName\tDisplayName\tSe
                                           "LoadOrderGroup\tDependencies\tStartName\tPassword\tArguments\tComponent_\tDescription\n"
                                           "s72\ts255\tL255\ti4\ti4\ti4\tS255\tS255\tS255\tS255\tS255\ts72\tL255\n"
                                           "ServiceInstall\tServiceInstall\n"
-                                          "TestService\tTestService\tTestService\t2\t3\t0\t\t\tTestService\t\t\tservice_comp\t\t";
+                                          "TestService\t[SERVNAME]\t[SERVDISP]\t2\t3\t0\t\t\tTestService\t\t\tservice_comp\t\t";
 
 static const CHAR service_control_dat[] = "ServiceControl\tName\tEvent\tArguments\tWait\tComponent_\n"
                                           "s72\tl255\ti2\tL255\tI2\ts72\n"
@@ -173,6 +177,13 @@ static const CHAR cc_component_dat[] = "Component\tComponentId\tDirectory_\tAttr
                                        "augustus\t\tMSITESTDIR\t0\t1\taugustus\n"
                                        "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n";
 
+static const CHAR cc2_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
+                                        "s72\tS38\ts72\ti2\tS255\tS72\n"
+                                        "Component\tComponent\n"
+                                        "maximus\t\tMSITESTDIR\t0\t1\tmaximus\n"
+                                        "augustus\t\tMSITESTDIR\t0\t0\taugustus\n"
+                                        "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n";
+
 static const CHAR cc_feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
                                      "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
                                      "Feature\tFeature\n"
@@ -192,6 +203,14 @@ static const CHAR cc_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion
                                   "augustus\taugustus\taugustus\t50000\t\t\t16384\t2\n"
                                   "caesar\tcaesar\tcaesar\t500\t\t\t16384\t12";
 
+static const CHAR cc2_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
+                                   "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
+                                   "File\tFile\n"
+                                   "maximus\tmaximus\tmaximus\t500\t\t\t16384\t1\n"
+                                   "augustus\taugustus\taugustus\t50000\t\t\t16384\t2\n"
+                                   "tiberius\tmaximus\ttiberius\t500\t\t\t16384\t3\n"
+                                   "caesar\tcaesar\tcaesar\t500\t\t\t16384\t12";
+
 static const CHAR cc_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
                                    "i2\ti4\tL64\tS255\tS32\tS72\n"
                                    "Media\tDiskId\n"
@@ -352,7 +371,7 @@ static const CHAR ci_install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
 static const CHAR ci_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n"
                                             "s72\ti2\tS64\tS0\tS255\n"
                                             "CustomAction\tAction\n"
-                                            "RunInstall\t23\tmsitest\\concurrent.msi\tMYPROP=[UILevel]\t\n";
+                                            "RunInstall\t87\tmsitest\\concurrent.msi\tMYPROP=[UILevel]\t\n";
 
 static const CHAR ci_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
                                        "s72\tS38\ts72\ti2\tS255\tS72\n"
@@ -586,6 +605,31 @@ static const CHAR wrv_registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tCompon
                                        "Registry\tRegistry\n"
                                        "regdata\t2\tSOFTWARE\\Wine\\msitest\tValue\t[~]one[~]two[~]three\taugustus";
 
+static const CHAR ca51_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
+                                         "s72\tS38\ts72\ti2\tS255\tS72\n"
+                                         "Component\tComponent\n"
+                                         "augustus\t\tMSITESTDIR\t0\tMYPROP=42\taugustus\n";
+
+static const CHAR ca51_install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+                                                "s72\tS255\tI2\n"
+                                                "InstallExecuteSequence\tAction\n"
+                                                "ValidateProductID\t\t700\n"
+                                                "GoodSetProperty\t\t725\n"
+                                                "BadSetProperty\t\t750\n"
+                                                "CostInitialize\t\t800\n"
+                                                "FileCost\t\t900\n"
+                                                "CostFinalize\t\t1000\n"
+                                                "InstallValidate\t\t1400\n"
+                                                "InstallInitialize\t\t1500\n"
+                                                "InstallFiles\t\t4000\n"
+                                                "InstallFinalize\t\t6600";
+
+static const CHAR ca51_custom_action_dat[] = "Action\tType\tSource\tTarget\n"
+                                             "s72\ti2\tS64\tS0\n"
+                                             "CustomAction\tAction\n"
+                                             "GoodSetProperty\t51\tMYPROP\t42\n"
+                                             "BadSetProperty\t51\t\tMYPROP\n";
+
 typedef struct _msi_table
 {
     const CHAR *filename;
@@ -622,6 +666,18 @@ static const msi_table cc_tables[] =
     ADD_TABLE(property),
 };
 
+static const msi_table cc2_tables[] =
+{
+    ADD_TABLE(cc2_component),
+    ADD_TABLE(directory),
+    ADD_TABLE(cc_feature),
+    ADD_TABLE(cc_feature_comp),
+    ADD_TABLE(cc2_file),
+    ADD_TABLE(install_exec_seq),
+    ADD_TABLE(cc_media),
+    ADD_TABLE(property),
+};
+
 static const msi_table co_tables[] =
 {
     ADD_TABLE(cc_component),
@@ -887,6 +943,31 @@ static const msi_table wrv_tables[] =
     ADD_TABLE(wrv_registry),
 };
 
+static const msi_table sf_tables[] =
+{
+    ADD_TABLE(wrv_component),
+    ADD_TABLE(directory),
+    ADD_TABLE(rof_feature),
+    ADD_TABLE(ci2_feature_comp),
+    ADD_TABLE(ci2_file),
+    ADD_TABLE(install_exec_seq),
+    ADD_TABLE(rof_media),
+    ADD_TABLE(property),
+};
+
+static const msi_table ca51_tables[] =
+{
+    ADD_TABLE(ca51_component),
+    ADD_TABLE(directory),
+    ADD_TABLE(rof_feature),
+    ADD_TABLE(ci2_feature_comp),
+    ADD_TABLE(ci2_file),
+    ADD_TABLE(ca51_install_exec_seq),
+    ADD_TABLE(rof_media),
+    ADD_TABLE(property),
+    ADD_TABLE(ca51_custom_action),
+};
+
 /* cabinet definitions */
 
 /* make the max size large so there is only one cab file */
@@ -1007,6 +1088,7 @@ static void init_functionpointers(void)
       trace("GetProcAddress(%s) failed\n", #func);
 
     GET_PROC(MsiQueryComponentStateA);
+    GET_PROC(MsiSourceListEnumSourcesA);
     GET_PROC(MsiSourceListGetInfoA);
 
 #undef GET_PROC
@@ -1331,6 +1413,15 @@ static void test_MsiInstallProduct(void)
     LONG res;
     HKEY hkey;
     DWORD num, size, type;
+    SC_HANDLE scm;
+
+    scm = OpenSCManager(NULL, NULL, GENERIC_ALL);
+    if (!scm && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
+    {
+        skip("Services are not implemented, we are most likely on win9x\n");
+        return;
+    }
+    CloseServiceHandle(scm);
 
     create_test_files();
     create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
@@ -1486,21 +1577,25 @@ static void create_cc_test_files(void)
 
     create_file("maximus", 500);
     create_file("augustus", 50000);
+    create_file("tiberius", 500);
     create_file("caesar", 500);
 
-    set_cab_parameters(&cabParams, "test1.cab", 200);
+    set_cab_parameters(&cabParams, "test1.cab", 40000);
 
     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
                       fci_read, fci_write, fci_close, fci_seek, fci_delete,
                       get_temp_file, &cabParams, cab_context);
     ok(hfci != NULL, "Failed to create an FCI context\n");
 
-    res = add_file(hfci, "maximus", tcompTYPE_MSZIP);
+    res = add_file(hfci, "maximus", tcompTYPE_NONE);
     ok(res, "Failed to add file maximus\n");
 
-    res = add_file(hfci, "augustus", tcompTYPE_MSZIP);
+    res = add_file(hfci, "augustus", tcompTYPE_NONE);
     ok(res, "Failed to add file augustus\n");
 
+    res = add_file(hfci, "tiberius", tcompTYPE_NONE);
+    ok(res, "Failed to add file tiberius\n");
+
     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
     ok(res, "Failed to flush the cabinet\n");
 
@@ -1511,6 +1606,7 @@ static void create_cc_test_files(void)
 
     DeleteFile("maximus");
     DeleteFile("augustus");
+    DeleteFile("tiberius");
     DeleteFile("caesar");
 }
 
@@ -1542,13 +1638,29 @@ static void test_continuouscabs(void)
     MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
 
     r = MsiInstallProductA(msifile, NULL);
-    ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
-    todo_wine
+    if (r == ERROR_SUCCESS) /* win9x has a problem with this */
     {
         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
         ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n");
         ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n");
+        ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
+        ok(delete_pf("msitest", FALSE), "File not installed\n");
     }
+
+    delete_cab_files();
+    DeleteFile(msifile);
+
+    create_cc_test_files();
+    create_database(msifile, cc2_tables, sizeof(cc2_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
+    r = MsiInstallProductA(msifile, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+    ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
+    ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n");
+    ok(delete_pf("msitest\\tiberius", TRUE), "File not installed\n");
+    ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n");
     ok(delete_pf("msitest", FALSE), "File not installed\n");
 
     delete_cab_files();
@@ -1605,13 +1717,13 @@ static void test_caborder(void)
     create_database(msifile, co_tables, sizeof(co_tables) / sizeof(msi_table));
 
     r = MsiInstallProductA(msifile, NULL);
-    ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n");
+    ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
     ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n");
     ok(!delete_pf("msitest", FALSE), "File is installed\n");
     todo_wine
     {
+        ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n");
         ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n");
-        ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
     }
 
     delete_cab_files();
@@ -1621,11 +1733,11 @@ static void test_caborder(void)
     create_database(msifile, co2_tables, sizeof(co2_tables) / sizeof(msi_table));
 
     r = MsiInstallProductA(msifile, NULL);
-    ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n");
     ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n");
     todo_wine
     {
         ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+        ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n");
         ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n");
         ok(!delete_pf("msitest", FALSE), "File is installed\n");
     }
@@ -1679,14 +1791,14 @@ static void test_samesequence(void)
     MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
 
     r = MsiInstallProductA(msifile, NULL);
-    todo_wine
+    if (r == ERROR_SUCCESS) /* win9x has a problem with this */
     {
         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
         ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n");
         ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n");
+        ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
+        ok(delete_pf("msitest", FALSE), "File not installed\n");
     }
-    ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
-    ok(delete_pf("msitest", FALSE), "File not installed\n");
 
     delete_cab_files();
     DeleteFile(msifile);
@@ -1702,14 +1814,14 @@ static void test_uiLevelFlags(void)
     MsiSetInternalUI(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY, NULL);
 
     r = MsiInstallProductA(msifile, NULL);
-    ok(!delete_pf("msitest\\maximus", TRUE), "UI install occurred, but execute-only was requested.\n");
-    todo_wine
+    if (r == ERROR_SUCCESS) /* win9x has a problem with this */
     {
         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+        ok(!delete_pf("msitest\\maximus", TRUE), "UI install occurred, but execute-only was requested.\n");
         ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n");
+        ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n");
+        ok(delete_pf("msitest", FALSE), "File not installed\n");
     }
-    ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n");
-    ok(delete_pf("msitest", FALSE), "File not installed\n");
 
     delete_cab_files();
     DeleteFile(msifile);
@@ -1848,9 +1960,15 @@ static void test_concurrentinstall(void)
     ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n");
     ok(delete_pf("msitest", FALSE), "File not installed\n");
 
-    /* Delete the files in the temp (current) folder */
-    DeleteFile(msifile);
     DeleteFile(path);
+
+    r = MsiInstallProductA(msifile, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+    ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");
+    ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n");
+    ok(delete_pf("msitest", FALSE), "File not installed\n");
+
+    DeleteFile(msifile);
     DeleteFile("msitest\\msitest\\augustus");
     DeleteFile("msitest\\maximus");
     RemoveDirectory("msitest\\msitest");
@@ -1980,7 +2098,7 @@ static void get_date_str(LPSTR date)
     sprintf(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
 }
 
-static void test_publish(void)
+static void test_publish_registerproduct(void)
 {
     UINT r;
     LONG res;
@@ -1992,6 +2110,12 @@ static void test_publish(void)
 
     static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
 
+    if (!pMsiQueryComponentStateA)
+    {
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
+    }
+
     get_date_str(date);
     GetTempPath(MAX_PATH, temp);
 
@@ -2022,121 +2146,6 @@ static void test_publish(void)
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
     ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
-    /* nothing published */
-    r = MsiInstallProductA(msifile, NULL);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
-    ok(pf_exists("msitest\\maximus"), "File not installed\n");
-    ok(pf_exists("msitest"), "File not installed\n");
-
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-
-    /* install again */
-    r = MsiInstallProductA(msifile, NULL);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File not installed\n");
-    ok(pf_exists("msitest"), "File not installed\n");
-
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-
-    /* try to uninstall */
-    r = MsiInstallProductA(msifile, "REMOVE=ALL");
-    todo_wine
-    {
-        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    }
-    ok(pf_exists("msitest\\maximus"), "File deleted\n");
-    ok(pf_exists("msitest"), "File deleted\n");
-
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-
-    /* PublishProduct */
-    r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1");
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File not installed\n");
-    ok(pf_exists("msitest"), "File not installed\n");
-
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-
-    /* try to uninstall after PublishProduct */
-    r = MsiInstallProductA(msifile, "REMOVE=ALL");
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File deleted\n");
-    ok(pf_exists("msitest"), "File deleted\n");
-
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-
     /* RegisterProduct */
     r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
@@ -2149,6 +2158,9 @@ static void test_publish(void)
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
     r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
                                 "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
     ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
@@ -2186,8 +2198,8 @@ static void test_publish(void)
 
     RegCloseKey(prodkey);
 
-    /* complete uninstall */
-    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    /* try to uninstall after RegisterProduct */
+    r = MsiInstallProductA(msifile, "REMOVE=ALL");
     todo_wine
     {
         ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
@@ -2216,7 +2228,10 @@ static void test_publish(void)
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+    todo_wine
+    {
+        ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+    }
 
     todo_wine
     {
@@ -2247,14 +2262,47 @@ static void test_publish(void)
 
     RegCloseKey(prodkey);
 
-    /* PublishProduct and RegisterProduct */
-    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1");
+    /* full install to remove */
+    r = MsiInstallProductA(msifile, "FULL=1");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File not installed\n");
-    ok(pf_exists("msitest"), "File not installed\n");
+    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    RegCloseKey(uninstall);
+    DeleteFile(msifile);
+    DeleteFile("msitest\\maximus");
+    RemoveDirectory("msitest");
+    delete_pfmsitest_files();
+}
+
+static void test_publish_publishproduct(void)
+{
+    UINT r;
+    LONG res;
+    HKEY uninstall, prodkey;
+    INSTALLSTATE state;
+    CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
+
+    static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
+
+    if (!pMsiQueryComponentStateA)
+    {
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
+    }
+
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\maximus", 500);
+
+    create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
 
     state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
@@ -2264,52 +2312,472 @@ static void test_publish(void)
 
     r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
                                 "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
-    CHECK_REG_STR(prodkey, "DisplayName", "MSITEST");
-    CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1");
-    CHECK_REG_STR(prodkey, "InstallDate", date);
-    CHECK_REG_STR(prodkey, "InstallSource", temp);
-    CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    CHECK_REG_STR(prodkey, "Publisher", "Wine");
-    CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL);
-    CHECK_REG_STR(prodkey, "Comments", NULL);
-    CHECK_REG_STR(prodkey, "Contact", NULL);
-    CHECK_REG_STR(prodkey, "HelpLink", NULL);
-    CHECK_REG_STR(prodkey, "HelpTelephone", NULL);
-    CHECK_REG_STR(prodkey, "InstallLocation", NULL);
-    CHECK_REG_STR(prodkey, "Readme", NULL);
-    CHECK_REG_STR(prodkey, "Size", NULL);
-    CHECK_REG_STR(prodkey, "URLInfoAbout", NULL);
-    CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL);
-    CHECK_REG_DWORD(prodkey, "Language", 1033);
-    CHECK_REG_DWORD(prodkey, "Version", 0x1010001);
-    CHECK_REG_DWORD(prodkey, "VersionMajor", 1);
-    CHECK_REG_DWORD(prodkey, "VersionMinor", 1);
-    CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1);
+    /* PublishProduct */
+    r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* try to uninstall after PublishProduct */
+    r = MsiInstallProductA(msifile, "REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File deleted\n");
+    ok(pf_exists("msitest"), "File deleted\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* full install to remove */
+    r = MsiInstallProductA(msifile, "FULL=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    RegCloseKey(uninstall);
+    DeleteFile(msifile);
+    DeleteFile("msitest\\maximus");
+    RemoveDirectory("msitest");
+    delete_pfmsitest_files();
+}
+
+static void test_publish_publishfeatures(void)
+{
+    UINT r;
+    LONG res;
+    HKEY uninstall, prodkey;
+    INSTALLSTATE state;
+    CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
+
+    static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
+
+    if (!pMsiQueryComponentStateA)
+    {
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
+    }
+
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\maximus", 500);
+
+    create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* PublishFeatures */
+    r = MsiInstallProductA(msifile, "PUBLISH_FEATURES=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    /* try to uninstall after PublishFeatures */
+    r = MsiInstallProductA(msifile, "REMOVE=ALL");
     todo_wine
     {
-        CHECK_REG_DWORD(prodkey, "EstimatedSize", 12);
+        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     }
+    ok(pf_exists("msitest\\maximus"), "File deleted\n");
+    ok(pf_exists("msitest"), "File deleted\n");
 
-    RegCloseKey(prodkey);
+    /* PublishFeatures and PublishProduct */
+    r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1 PUBLISH_FEATURES=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    /* PublishFeatures and RegisterProduct */
+    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_FEATURES=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    /* full install to remove */
+    r = MsiInstallProductA(msifile, "FULL=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    RegCloseKey(uninstall);
+    DeleteFile(msifile);
+    DeleteFile("msitest\\maximus");
+    RemoveDirectory("msitest");
+    delete_pfmsitest_files();
+}
+
+static void test_publish_registeruser(void)
+{
+    UINT r;
+    LONG res;
+    HKEY uninstall, prodkey;
+    INSTALLSTATE state;
+    CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
+
+    static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
+
+    if (!pMsiQueryComponentStateA)
+    {
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
+    }
+
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\maximus", 500);
+
+    create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* RegisterUser */
+    r = MsiInstallProductA(msifile, "REGISTER_USER=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* try to uninstall after RegisterUser */
+    r = MsiInstallProductA(msifile, "REMOVE=ALL");
+    todo_wine
+    {
+        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    }
+    ok(pf_exists("msitest\\maximus"), "File deleted\n");
+    ok(pf_exists("msitest"), "File deleted\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* full install to remove */
+    r = MsiInstallProductA(msifile, "FULL=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    RegCloseKey(uninstall);
+    DeleteFile(msifile);
+    DeleteFile("msitest\\maximus");
+    RemoveDirectory("msitest");
+    delete_pfmsitest_files();
+}
+
+static void get_user_sid(LPSTR *usersid)
+{
+    HANDLE token;
+    BYTE buf[1024];
+    DWORD size;
+    PTOKEN_USER user;
+    HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll");
+    static BOOL (WINAPI *pConvertSidToStringSidA)(PSID, LPSTR*);
+
+    *usersid = NULL;
+    pConvertSidToStringSidA = (void *)GetProcAddress(hadvapi32, "ConvertSidToStringSidA");
+    if (!pConvertSidToStringSidA)
+        return;
+
+    OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token);
+    size = sizeof(buf);
+    GetTokenInformation(token, TokenUser, (void *)buf, size, &size);
+    user = (PTOKEN_USER)buf;
+    pConvertSidToStringSidA(user->User.Sid, usersid);
+}
+
+static void test_publish_processcomponents(void)
+{
+    UINT r;
+    LONG res;
+    HKEY uninstall, prodkey, comp;
+    INSTALLSTATE state;
+    LPSTR usersid;
+    CHAR keypath[MAX_PATH];
+    CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
+
+    static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
+
+    if (!pMsiQueryComponentStateA)
+    {
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
+    }
+
+    get_user_sid(&usersid);
+    if (!usersid)
+    {
+        skip("ConvertSidToStringSidA is not available\n");
+        return;
+    }
+
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\maximus", 500);
+
+    create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* ProcessComponent */
+    r = MsiInstallProductA(msifile, "PROCESS_COMPONENTS=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* try to uninstall after ProcessComponents */
+    r = MsiInstallProductA(msifile, "REMOVE=ALL");
+    todo_wine
+    {
+        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    }
+    ok(pf_exists("msitest\\maximus"), "File deleted\n");
+    ok(pf_exists("msitest"), "File deleted\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+
+    /* ProcessComponent with PublishProduct */
+    r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    /* uninstall */
+    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    /* ProcessComponent with RegisterProduct */
+    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PROCESS_COMPONENTS=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
+
+    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
+    ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+
+    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
 
-    /* try it again */
-    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1");
+    /* ProcessComponent with RegisterProduct and PublishProduct */
+    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
     ok(pf_exists("msitest\\maximus"), "File not installed\n");
     ok(pf_exists("msitest"), "File not installed\n");
 
     state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    todo_wine
-    {
-        ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-    }
+    ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
 
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
@@ -2319,52 +2787,65 @@ static void test_publish(void)
 
     r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
                                 "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    todo_wine
-    {
-        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    }
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
-
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    todo_wine
-    {
-        ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-    }
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
 
-    /* uninstall has a problem with this */
+    /* uninstall */
     r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
-    todo_wine
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Components\\CBABC2FDCCB35E749A8944D8C1C098B5");
+
+    /* native has trouble removing the comp key unless it's a full install */
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &comp);
+    if (res == ERROR_SUCCESS)
     {
-        ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+        RegDeleteKeyA(comp, "");
+        RegCloseKey(comp);
     }
-    ok(pf_exists("msitest\\maximus"), "File deleted\n");
-    ok(pf_exists("msitest"), "File deleted\n");
 
-    state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+    RegCloseKey(uninstall);
+    DeleteFile(msifile);
+    DeleteFile("msitest\\maximus");
+    RemoveDirectory("msitest");
+    delete_pfmsitest_files();
+}
 
-    state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+static void test_publish(void)
+{
+    UINT r;
+    LONG res;
+    HKEY uninstall, prodkey;
+    INSTALLSTATE state;
+    CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
+    char date[MAX_PATH];
+    char temp[MAX_PATH];
 
-    r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                                "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
-    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+    static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
 
-    res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    todo_wine
+    if (!pMsiQueryComponentStateA)
     {
-        ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
+        skip("MsiQueryComponentStateA is not available\n");
+        return;
     }
 
-    /* PublishProduct and RegisterProduct and ProcessComponents */
-    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1");
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File not installed\n");
-    ok(pf_exists("msitest"), "File not installed\n");
+    get_date_str(date);
+    GetTempPath(MAX_PATH, temp);
+
+    res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\maximus", 500);
+
+    create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
 
     state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
@@ -2374,46 +2855,17 @@ static void test_publish(void)
 
     r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
                                 "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
-
-    CHECK_REG_STR(prodkey, "DisplayName", "MSITEST");
-    CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1");
-    CHECK_REG_STR(prodkey, "InstallDate", date);
-    CHECK_REG_STR(prodkey, "InstallSource", temp);
-    CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    CHECK_REG_STR(prodkey, "Publisher", "Wine");
-    CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
-    CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL);
-    CHECK_REG_STR(prodkey, "Comments", NULL);
-    CHECK_REG_STR(prodkey, "Contact", NULL);
-    CHECK_REG_STR(prodkey, "HelpLink", NULL);
-    CHECK_REG_STR(prodkey, "HelpTelephone", NULL);
-    CHECK_REG_STR(prodkey, "InstallLocation", NULL);
-    CHECK_REG_STR(prodkey, "Readme", NULL);
-    CHECK_REG_STR(prodkey, "Size", NULL);
-    CHECK_REG_STR(prodkey, "URLInfoAbout", NULL);
-    CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL);
-    CHECK_REG_DWORD(prodkey, "Language", 1033);
-    CHECK_REG_DWORD(prodkey, "Version", 0x1010001);
-    CHECK_REG_DWORD(prodkey, "VersionMajor", 1);
-    CHECK_REG_DWORD(prodkey, "VersionMinor", 1);
-    CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1);
-    todo_wine
-    {
-        CHECK_REG_DWORD(prodkey, "EstimatedSize", 12);
-    }
-
-    RegCloseKey(prodkey);
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
-    /* complete uninstall */
-    r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(pf_exists("msitest\\maximus"), "File deleted\n");
-    ok(pf_exists("msitest"), "File deleted\n");
+    /* nothing published */
+    r = MsiInstallProductA(msifile, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+    ok(pf_exists("msitest\\maximus"), "File not installed\n");
+    ok(pf_exists("msitest"), "File not installed\n");
 
     state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
@@ -2430,13 +2882,10 @@ static void test_publish(void)
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    todo_wine
-    {
-        ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-    }
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
-    /* PublishProduct, RegisterProduct, ProcessComponents, PublishFeatures */
-    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1 PUBLISH_FEATURES=1");
+    /* PublishProduct and RegisterProduct */
+    r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
     ok(pf_exists("msitest\\maximus"), "File not installed\n");
     ok(pf_exists("msitest"), "File not installed\n");
@@ -2445,15 +2894,15 @@ static void test_publish(void)
     ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state);
 
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature");
-    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo");
-    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
                                 "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state);
+    ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r);
+    ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
@@ -2487,14 +2936,10 @@ static void test_publish(void)
 
     RegCloseKey(prodkey);
 
-    /* complete uninstall */
     r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-    todo_wine
-    {
-        ok(!pf_exists("msitest\\maximus"), "File deleted\n");
-        ok(!pf_exists("msitest"), "File deleted\n");
-    }
+    ok(pf_exists("msitest\\maximus"), "File deleted\n");
+    ok(pf_exists("msitest"), "File deleted\n");
 
     state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
@@ -2511,10 +2956,7 @@ static void test_publish(void)
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    todo_wine
-    {
-        ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-    }
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
     /* complete install */
     r = MsiInstallProductA(msifile, "FULL=1");
@@ -2852,10 +3294,7 @@ static void test_publish(void)
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
     res = RegOpenKeyA(uninstall, prodcode, &prodkey);
-    todo_wine
-    {
-        ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
-    }
+    ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res);
 
     /* make sure 'Program Files\msitest' is removed */
     delete_pfmsitest_files();
@@ -2871,8 +3310,15 @@ static void test_publishsourcelist(void)
     UINT r;
     DWORD size;
     CHAR value[MAX_PATH];
+    CHAR path[MAX_PATH];
     CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
 
+    if (!pMsiSourceListEnumSourcesA || !pMsiSourceListGetInfoA)
+    {
+        skip("MsiSourceListEnumSourcesA and/or MsiSourceListGetInfoA are not available\n");
+        return;
+    }
+
     CreateDirectoryA("msitest", NULL);
     create_file("msitest\\maximus", 500);
 
@@ -2888,7 +3334,13 @@ static void test_publishsourcelist(void)
     /* nothing published */
     size = 0xdeadbeef;
     r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                              MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+                               MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
+
+    size = 0xdeadbeef;
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_URL, 0, NULL, &size);
     ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
 
@@ -2900,7 +3352,13 @@ static void test_publishsourcelist(void)
     /* after RegisterProduct */
     size = 0xdeadbeef;
     r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                              MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+                               MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
+
+    size = 0xdeadbeef;
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_URL, 0, NULL, &size);
     ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
 
@@ -2912,7 +3370,13 @@ static void test_publishsourcelist(void)
     /* after ProcessComponents */
     size = 0xdeadbeef;
     r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                              MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+                               MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
+
+    size = 0xdeadbeef;
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_URL, 0, NULL, &size);
     ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
 
@@ -2924,7 +3388,13 @@ static void test_publishsourcelist(void)
     /* after PublishFeatures */
     size = 0xdeadbeef;
     r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                              MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+                               MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size);
+    ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
+
+    size = 0xdeadbeef;
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_URL, 0, NULL, &size);
     ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
     ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size);
 
@@ -2937,13 +3407,69 @@ static void test_publishsourcelist(void)
     size = MAX_PATH;
     lstrcpyA(value, "aaa");
     r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
-                              MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, &size);
-    todo_wine
-    {
-        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
-        ok(!lstrcmpA(value, "msitest.msi"), "Expected 'msitest.msi', got %s\n", value);
-        ok(size == 11, "Expected 11, got %d\n", size);
-    }
+                               MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, "msitest.msi"), "Expected 'msitest.msi', got %s\n", value);
+    ok(size == 11, "Expected 11, got %d\n", size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                               MSICODE_PRODUCT, INSTALLPROPERTY_MEDIAPACKAGEPATH, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, ""), "Expected \"\", got \"%s\"\n", value);
+    ok(size == 0, "Expected 0, got %d\n", size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                               MSICODE_PRODUCT, INSTALLPROPERTY_DISKPROMPT, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, ""), "Expected \"\", got \"%s\"\n", value);
+    ok(size == 0, "Expected 0, got %d\n", size);
+
+    lstrcpyA(path, CURR_DIR);
+    lstrcatA(path, "\\");
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                               MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCE, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, path), "Expected \"%s\", got \"%s\"\n", path, value);
+    ok(size == lstrlenA(path), "Expected %d, got %d\n", lstrlenA(path), size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                               MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDTYPE, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, "n"), "Expected \"n\", got \"%s\"\n", value);
+    ok(size == 1, "Expected 1, got %d\n", size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_URL, 0, value, &size);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+    ok(!lstrcmpA(value, "aaa"), "Expected value to be unchanged, got %s\n", value);
+    ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_NETWORK, 0, value, &size);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(value, path), "Expected \"%s\", got \"%s\"\n", path, value);
+    ok(size == lstrlenA(path), "Expected %d, got %d\n", lstrlenA(path), size);
+
+    size = MAX_PATH;
+    lstrcpyA(value, "aaa");
+    r = pMsiSourceListEnumSourcesA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
+                                   MSICODE_PRODUCT | MSISOURCETYPE_NETWORK, 1, value, &size);
+    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+    ok(!lstrcmpA(value, "aaa"), "Expected value to be unchanged, got %s\n", value);
+    ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size);
 
     /* complete uninstall */
     r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL");
@@ -3530,12 +4056,18 @@ static void test_movefiles(void)
     ok(delete_pf("msitest\\kiribati", TRUE), "File not moved\n");
     ok(!delete_pf("msitest\\lebanon", TRUE), "File moved\n");
     ok(!delete_pf("msitest\\lebanon", FALSE), "Directory moved\n");
-    ok(!delete_pf("msitest\\apple", TRUE), "File should not exist\n");
+    /* either apple or application will be moved depending on directory order */
+    if (!delete_pf("msitest\\apple", TRUE))
+        ok(delete_pf("msitest\\application", TRUE), "File not moved\n");
+    else
+        ok(!delete_pf("msitest\\application", TRUE), "File should not exist\n");
     ok(delete_pf("msitest\\wildcard", TRUE), "File not moved\n");
-    ok(delete_pf("msitest\\application", TRUE), "File not moved\n");
     ok(!delete_pf("msitest\\ape", TRUE), "File moved\n");
-    ok(delete_pf("msitest\\foo", TRUE), "File not moved\n");
-    ok(!delete_pf("msitest\\fao", TRUE), "File should not exist\n");
+    /* either fao or foo will be moved depending on directory order */
+    if (delete_pf("msitest\\foo", TRUE))
+        ok(!delete_pf("msitest\\fao", TRUE), "File should not exist\n");
+    else
+        ok(delete_pf("msitest\\fao", TRUE), "File not moved\n");
     ok(delete_pf("msitest\\single", TRUE), "File not moved\n");
     ok(!delete_pf("msitest\\fbod", TRUE), "File moved\n");
     ok(delete_pf("msitest\\budding", TRUE), "File not moved\n");
@@ -3685,6 +4217,62 @@ static void test_writeregistryvalues(void)
     RemoveDirectory("msitest");
 }
 
+static void test_sourcefolder(void)
+{
+    UINT r;
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("augustus", 500);
+
+    create_database(msifile, sf_tables, sizeof(sf_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
+    r = MsiInstallProductA(msifile, NULL);
+    ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n");
+    todo_wine
+    {
+        ok(r == ERROR_INSTALL_FAILURE,
+           "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+        ok(!delete_pf("msitest", FALSE), "File installed\n");
+    }
+
+    RemoveDirectoryA("msitest");
+
+    r = MsiInstallProductA(msifile, NULL);
+    todo_wine
+    {
+        ok(r == ERROR_INSTALL_FAILURE,
+           "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+        ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n");
+        ok(!delete_pf("msitest", FALSE), "File installed\n");
+    }
+
+    DeleteFile(msifile);
+    DeleteFile("augustus");
+}
+
+static void test_customaction51(void)
+{
+    UINT r;
+
+    CreateDirectoryA("msitest", NULL);
+    create_file("msitest\\augustus", 500);
+
+    create_database(msifile, ca51_tables, sizeof(ca51_tables) / sizeof(msi_table));
+
+    MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
+    r = MsiInstallProductA(msifile, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+    ok(delete_pf("msitest\\augustus", TRUE), "File installed\n");
+    ok(delete_pf("msitest", FALSE), "File installed\n");
+
+    DeleteFile(msifile);
+    DeleteFile("msitest\\augustus");
+    RemoveDirectory("msitest");
+}
+
 START_TEST(install)
 {
     DWORD len;
@@ -3717,6 +4305,11 @@ START_TEST(install)
     test_cabisextracted();
     test_concurrentinstall();
     test_setpropertyfolder();
+    test_publish_registerproduct();
+    test_publish_publishproduct();
+    test_publish_publishfeatures();
+    test_publish_registeruser();
+    test_publish_processcomponents();
     test_publish();
     test_publishsourcelist();
     test_transformprop();
@@ -3728,6 +4321,8 @@ START_TEST(install)
     test_missingcab();
     test_duplicatefiles();
     test_writeregistryvalues();
+    test_sourcefolder();
+    test_customaction51();
 
     SetCurrentDirectoryA(prev_path);
 }
index 520f0e7..bb45d8d 100644 (file)
@@ -34,6 +34,8 @@ static INSTALLSTATE (WINAPI *pMsiGetComponentPathA)
     (LPCSTR, LPCSTR, LPSTR, DWORD*);
 static UINT (WINAPI *pMsiGetFileHashA)
     (LPCSTR, DWORD, PMSIFILEHASHINFO);
+static UINT (WINAPI *pMsiGetProductInfoExA)
+    (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, LPSTR, LPDWORD);
 static UINT (WINAPI *pMsiOpenPackageExA)
     (LPCSTR, DWORD, MSIHANDLE*);
 static UINT (WINAPI *pMsiOpenPackageExW)
@@ -55,6 +57,7 @@ static void init_functionpointers(void)
 
     GET_PROC(hmsi, MsiGetComponentPathA)
     GET_PROC(hmsi, MsiGetFileHashA)
+    GET_PROC(hmsi, MsiGetProductInfoExA)
     GET_PROC(hmsi, MsiOpenPackageExA)
     GET_PROC(hmsi, MsiOpenPackageExW)
     GET_PROC(hmsi, MsiQueryComponentStateA)
@@ -301,7 +304,8 @@ static void test_MsiGetFileHash(void)
 
     /* szFilePath is empty */
     r = pMsiGetFileHashA("", 0, &hash);
-    ok(r == ERROR_PATH_NOT_FOUND, "Expected ERROR_PATH_NOT_FOUND, got %d\n", r);
+    ok(r == ERROR_PATH_NOT_FOUND || r == ERROR_BAD_PATHNAME,
+       "Expected ERROR_PATH_NOT_FOUND or ERROR_BAD_PATHNAME, got %d\n", r);
 
     /* szFilePath is nonexistent */
     r = pMsiGetFileHashA(name, 0, &hash);
@@ -643,7 +647,7 @@ static void test_MsiQueryFeatureState(void)
     state = MsiQueryFeatureStateA(prodcode, "feature");
     ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
 
-    res = RegSetValueExA(userkey, "feature", 0, REG_SZ, (const BYTE *)"", 8);
+    res = RegSetValueExA(userkey, "feature", 0, REG_SZ, (const BYTE *)"", 2);
     ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
 
     state = MsiQueryFeatureStateA(prodcode, "feature");
@@ -2042,6 +2046,4000 @@ static void test_MsiGetFileVersion(void)
     HeapFree(GetProcessHeap(), 0, langcheck);
 }
 
+static void test_MsiGetProductInfo(void)
+{
+    UINT r;
+    LONG res;
+    HKEY propkey, source;
+    HKEY prodkey, localkey;
+    CHAR prodcode[MAX_PATH];
+    CHAR prod_squashed[MAX_PATH];
+    CHAR packcode[MAX_PATH];
+    CHAR pack_squashed[MAX_PATH];
+    CHAR buf[MAX_PATH];
+    CHAR keypath[MAX_PATH];
+    LPSTR usersid;
+    DWORD sz, val = 42;
+
+    create_test_guid(prodcode, prod_squashed);
+    create_test_guid(packcode, pack_squashed);
+    get_user_sid(&usersid);
+
+    /* NULL szProduct */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(NULL, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* empty szProduct */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA("", INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* garbage szProduct */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA("garbage", INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* guid without brackets */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D",
+                           INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* guid with brackets */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}",
+                           INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* same length as guid, but random */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93",
+                           INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* not installed, NULL szAttribute */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, NULL, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* not installed, NULL lpValueBuf */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, NULL, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* not installed, NULL pcchValueBuf */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, NULL);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* created guid cannot possibly be an installed product code */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* managed product code exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteKeyA(prodkey, "");
+    RegCloseKey(prodkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* local user product code exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* both local and managed product code exist */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "link"), "Expected \"link\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    /* pcchBuf is NULL */
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, NULL, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    /* lpValueBuf is NULL */
+    sz = MAX_PATH;
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    /* lpValueBuf is NULL, pcchValueBuf is too small */
+    sz = 2;
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    /* lpValueBuf is NULL, pcchValueBuf is too small */
+    sz = 2;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to remain unchanged, got \"%s\"\n", buf);
+    ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    /* lpValueBuf is NULL, pcchValueBuf is exactly 4 */
+    sz = 4;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"),
+       "Expected buf to remain unchanged, got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "IMadeThis", 0, REG_SZ, (LPBYTE)"random", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* random property not supported by MSI, value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, "IMadeThis", buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteValueA(propkey, "IMadeThis");
+    RegDeleteValueA(propkey, "HelpLink");
+    RegDeleteKeyA(propkey, "");
+    RegDeleteKeyA(localkey, "");
+    RegDeleteKeyA(prodkey, "");
+    RegCloseKey(propkey);
+    RegCloseKey(localkey);
+    RegCloseKey(prodkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* local user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "link"), "Expected \"link\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    RegDeleteValueA(propkey, "HelpLink");
+    RegDeleteKeyA(propkey, "");
+    RegDeleteKeyA(localkey, "");
+    RegDeleteKeyA(prodkey, "");
+    RegCloseKey(propkey);
+    RegCloseKey(localkey);
+    RegCloseKey(prodkey);
+
+    lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* classes product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* local user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteKeyA(propkey, "");
+    RegDeleteKeyA(localkey, "");
+    RegCloseKey(propkey);
+    RegCloseKey(localkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, "S-1-5-18\\\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Local System product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+        "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "link"), "Expected \"link\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLEDPRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "name"), "Expected \"name\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayName", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayName type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLEDPRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayVersion", 0, REG_SZ, (LPBYTE)"1.1.1", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayVersion value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONSTRING, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "1.1.1"), "Expected \"1.1.1\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayVersion", 0,
+                         REG_DWORD, (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayVersion type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONSTRING, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpTelephone", 0, REG_SZ, (LPBYTE)"tele", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpTelephone value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "tele"), "Expected \"tele\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpTelephone", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpTelephone type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallLocation", 0, REG_SZ, (LPBYTE)"loc", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallLocation value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLLOCATION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "loc"), "Expected \"loc\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallLocation", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallLocation type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLLOCATION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallSource", 0, REG_SZ, (LPBYTE)"source", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallSource value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLSOURCE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "source"), "Expected \"source\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallSource", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallSource type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLSOURCE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallDate", 0, REG_SZ, (LPBYTE)"date", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallDate value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLDATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "date"), "Expected \"date\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallDate", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallDate type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTALLDATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Publisher", 0, REG_SZ, (LPBYTE)"pub", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Publisher value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PUBLISHER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "pub"), "Expected \"pub\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Publisher", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Publisher type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PUBLISHER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"pack", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LOCALPACKAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "pack"), "Expected \"pack\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LOCALPACKAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "UrlInfoAbout", 0, REG_SZ, (LPBYTE)"about", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* UrlInfoAbout value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_URLINFOABOUT, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "about"), "Expected \"about\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "UrlInfoAbout", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* UrlInfoAbout type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_URLINFOABOUT, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "UrlUpdateInfo", 0, REG_SZ, (LPBYTE)"info", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* UrlUpdateInfo value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_URLUPDATEINFO, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "info"), "Expected \"info\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "UrlUpdateInfo", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* UrlUpdateInfo type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_URLUPDATEINFO, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMinor", 0, REG_SZ, (LPBYTE)"1", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMinor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONMINOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "1"), "Expected \"1\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMinor", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMinor type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONMINOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMajor", 0, REG_SZ, (LPBYTE)"1", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMajor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONMAJOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "1"), "Expected \"1\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMajor", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMajor type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSIONMAJOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductID", 0, REG_SZ, (LPBYTE)"id", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductID value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTID, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "id"), "Expected \"id\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductID", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductID type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTID, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegCompany", 0, REG_SZ, (LPBYTE)"comp", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegCompany value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_REGCOMPANY, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "comp"), "Expected \"comp\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegCompany", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegCompany type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_REGCOMPANY, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegOwner", 0, REG_SZ, (LPBYTE)"own", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegOwner value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_REGOWNER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "own"), "Expected \"own\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegOwner", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegOwner type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_REGOWNER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstanceType", 0, REG_SZ, (LPBYTE)"type", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstanceType value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTANCETYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstanceType", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstanceType type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTANCETYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "InstanceType", 0, REG_SZ, (LPBYTE)"type", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstanceType value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTANCETYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "type"), "Expected \"type\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "InstanceType", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstanceType type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_INSTANCETYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Transforms", 0, REG_SZ, (LPBYTE)"tforms", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Transforms", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Transforms", 0, REG_SZ, (LPBYTE)"tforms", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "tforms"), "Expected \"tforms\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Transforms", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Language", 0, REG_SZ, (LPBYTE)"lang", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Language", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Language", 0, REG_SZ, (LPBYTE)"lang", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "lang"), "Expected \"lang\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Language", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductName", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "name"), "Expected \"name\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductName", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Assignment", 0, REG_SZ, (LPBYTE)"at", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Assignment value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Assignment", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Assignment type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Assignment", 0, REG_SZ, (LPBYTE)"at", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Assignment value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "at"), "Expected \"at\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Assignment", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Assignment type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageCode", 0, REG_SZ, (LPBYTE)"code", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageCode", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "PackageCode", 0, REG_SZ, (LPBYTE)"code", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_BAD_CONFIGURATION,
+       "Expected ERROR_BAD_CONFIGURATION, got %d\n", r);
+    ok(!lstrcmpA(buf, "code"), "Expected \"code\", got \"%s\"\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "PackageCode", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "PackageCode", 0, REG_SZ, (LPBYTE)pack_squashed, 33);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, packcode), "Expected \"%s\", got \"%s\"\n", packcode, buf);
+    ok(sz == 38, "Expected 38, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Version", 0, REG_SZ, (LPBYTE)"ver", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Version", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Version", 0, REG_SZ, (LPBYTE)"ver", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "ver"), "Expected \"ver\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Version", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductIcon", 0, REG_SZ, (LPBYTE)"ico", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductIcon", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductIcon", 0, REG_SZ, (LPBYTE)"ico", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "ico"), "Expected \"ico\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductIcon", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegCreateKeyA(prodkey, "SourceList", &source);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    res = RegSetValueExA(source, "PackageName", 0, REG_SZ, (LPBYTE)"packname", 9);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGENAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "packname"), "Expected \"packname\", got \"%s\"\n", buf);
+    ok(sz == 8, "Expected 8, got %d\n", sz);
+
+    res = RegSetValueExA(source, "PackageName", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageName type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_PACKAGENAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "AuthorizedLUAApp", 0, REG_SZ, (LPBYTE)"auth", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Authorized value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    if (r != ERROR_UNKNOWN_PROPERTY)
+    {
+        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+        ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+        ok(sz == 0, "Expected 0, got %d\n", sz);
+    }
+
+    res = RegSetValueExA(propkey, "AuthorizedLUAApp", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* AuthorizedLUAApp type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    if (r != ERROR_UNKNOWN_PROPERTY)
+    {
+        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+        ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+        ok(sz == 0, "Expected 0, got %d\n", sz);
+    }
+
+    res = RegSetValueExA(prodkey, "AuthorizedLUAApp", 0, REG_SZ, (LPBYTE)"auth", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Authorized value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    if (r != ERROR_UNKNOWN_PROPERTY)
+    {
+        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+        ok(!lstrcmpA(buf, "auth"), "Expected \"auth\", got \"%s\"\n", buf);
+        ok(sz == 4, "Expected 4, got %d\n", sz);
+    }
+
+    res = RegSetValueExA(prodkey, "AuthorizedLUAApp", 0, REG_DWORD,
+                         (const BYTE *)&val, sizeof(DWORD));
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* AuthorizedLUAApp type is REG_DWORD */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = MsiGetProductInfoA(prodcode, INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    if (r != ERROR_UNKNOWN_PROPERTY)
+    {
+        ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+        ok(!lstrcmpA(buf, "42"), "Expected \"42\", got \"%s\"\n", buf);
+        ok(sz == 2, "Expected 2, got %d\n", sz);
+    }
+
+    RegDeleteValueA(propkey, "HelpLink");
+    RegDeleteValueA(propkey, "DisplayName");
+    RegDeleteValueA(propkey, "DisplayVersion");
+    RegDeleteValueA(propkey, "HelpTelephone");
+    RegDeleteValueA(propkey, "InstallLocation");
+    RegDeleteValueA(propkey, "InstallSource");
+    RegDeleteValueA(propkey, "InstallDate");
+    RegDeleteValueA(propkey, "Publisher");
+    RegDeleteValueA(propkey, "LocalPackage");
+    RegDeleteValueA(propkey, "UrlInfoAbout");
+    RegDeleteValueA(propkey, "UrlUpdateInfo");
+    RegDeleteValueA(propkey, "VersionMinor");
+    RegDeleteValueA(propkey, "VersionMajor");
+    RegDeleteValueA(propkey, "ProductID");
+    RegDeleteValueA(propkey, "RegCompany");
+    RegDeleteValueA(propkey, "RegOwner");
+    RegDeleteValueA(propkey, "InstanceType");
+    RegDeleteValueA(propkey, "Transforms");
+    RegDeleteValueA(propkey, "Language");
+    RegDeleteValueA(propkey, "ProductName");
+    RegDeleteValueA(propkey, "Assignment");
+    RegDeleteValueA(propkey, "PackageCode");
+    RegDeleteValueA(propkey, "Version");
+    RegDeleteValueA(propkey, "ProductIcon");
+    RegDeleteValueA(propkey, "AuthorizedLUAApp");
+    RegDeleteKeyA(propkey, "");
+    RegDeleteKeyA(localkey, "");
+    RegDeleteValueA(prodkey, "InstanceType");
+    RegDeleteValueA(prodkey, "Transforms");
+    RegDeleteValueA(prodkey, "Language");
+    RegDeleteValueA(prodkey, "ProductName");
+    RegDeleteValueA(prodkey, "Assignment");
+    RegDeleteValueA(prodkey, "PackageCode");
+    RegDeleteValueA(prodkey, "Version");
+    RegDeleteValueA(prodkey, "ProductIcon");
+    RegDeleteValueA(prodkey, "AuthorizedLUAApp");
+    RegDeleteValueA(source, "PackageName");
+    RegDeleteKeyA(source, "");
+    RegDeleteKeyA(prodkey, "");
+    RegCloseKey(propkey);
+    RegCloseKey(localkey);
+    RegCloseKey(source);
+    RegCloseKey(prodkey);
+}
+
+static void test_MsiGetProductInfoEx(void)
+{
+    UINT r;
+    LONG res;
+    HKEY propkey, userkey;
+    HKEY prodkey, localkey;
+    CHAR prodcode[MAX_PATH];
+    CHAR prod_squashed[MAX_PATH];
+    CHAR packcode[MAX_PATH];
+    CHAR pack_squashed[MAX_PATH];
+    CHAR buf[MAX_PATH];
+    CHAR keypath[MAX_PATH];
+    LPSTR usersid;
+    DWORD sz;
+
+    if (!pMsiGetProductInfoExA)
+    {
+        skip("MsiGetProductInfoExA is not available\n");
+        return;
+    }
+
+    create_test_guid(prodcode, prod_squashed);
+    create_test_guid(packcode, pack_squashed);
+    get_user_sid(&usersid);
+
+    /* NULL szProductCode */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(NULL, usersid, MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* empty szProductCode */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA("", usersid, MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* garbage szProductCode */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA("garbage", usersid, MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* guid without brackets */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* guid with brackets */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* szValue is non-NULL while pcchValue is NULL */
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, NULL);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+
+    /* dwContext is out of range */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid, 42,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* szProperty is NULL */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              NULL, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* szProperty is empty */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              "", buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* szProperty is not a valid property */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              "notvalid", buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* same length as guid, but random */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    /* MSIINSTALLCONTEXT_USERUNMANAGED */
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* local user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    RegDeleteValueA(propkey, "LocalPackage");
+
+    /* LocalPackage value must exist */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage exists, but HelpLink does not exist */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "link"), "Expected \"link\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpTelephone", 0, REG_SZ, (LPBYTE)"phone", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpTelephone value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "phone"), "Expected \"phone\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    /* szValue and pcchValue are NULL */
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, NULL, NULL);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+    /* pcchValue is exactly 5 */
+    sz = 5;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_MORE_DATA,
+       "Expected ERROR_MORE_DATA, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 10, "Expected 10, got %d\n", sz);
+
+    /* szValue is NULL, pcchValue is exactly 5 */
+    sz = 5;
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(sz == 10, "Expected 10, got %d\n", sz);
+
+    /* szValue is NULL, pcchValue is MAX_PATH */
+    sz = MAX_PATH;
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(sz == 10, "Expected 10, got %d\n", sz);
+
+    /* pcchValue is exactly 0 */
+    sz = 0;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_MORE_DATA,
+       "Expected ERROR_MORE_DATA, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected \"apple\", got \"%s\"\n", buf);
+    ok(sz == 10, "Expected 10, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "notvalid", 0, REG_SZ, (LPBYTE)"invalid", 8);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* szProperty is not a valid property */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              "notvalid", buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallDate", 0, REG_SZ, (LPBYTE)"date", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallDate value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLDATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "date"), "Expected \"date\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLEDPRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "name"), "Expected \"name\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallLocation", 0, REG_SZ, (LPBYTE)"loc", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallLocation value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLLOCATION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "loc"), "Expected \"loc\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallSource", 0, REG_SZ, (LPBYTE)"source", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallSource value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLSOURCE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "source"), "Expected \"source\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_LOCALPACKAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "local"), "Expected \"local\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Publisher", 0, REG_SZ, (LPBYTE)"pub", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Publisher value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PUBLISHER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "pub"), "Expected \"pub\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "URLInfoAbout", 0, REG_SZ, (LPBYTE)"about", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLInfoAbout value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_URLINFOABOUT, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "about"), "Expected \"about\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "URLUpdateInfo", 0, REG_SZ, (LPBYTE)"update", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLUpdateInfo value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_URLUPDATEINFO, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "update"), "Expected \"update\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMinor", 0, REG_SZ, (LPBYTE)"2", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMinor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONMINOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "2"), "Expected \"2\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMajor", 0, REG_SZ, (LPBYTE)"3", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMajor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONMAJOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "3"), "Expected \"3\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayVersion", 0, REG_SZ, (LPBYTE)"3.2.1", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayVersion value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONSTRING, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "3.2.1"), "Expected \"3.2.1\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductID", 0, REG_SZ, (LPBYTE)"id", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductID value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTID, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "id"), "Expected \"id\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegCompany", 0, REG_SZ, (LPBYTE)"comp", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegCompany value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_REGCOMPANY, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "comp"), "Expected \"comp\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegOwner", 0, REG_SZ, (LPBYTE)"owner", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegOwner value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_REGOWNER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "owner"), "Expected \"owner\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Transforms", 0, REG_SZ, (LPBYTE)"trans", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Language", 0, REG_SZ, (LPBYTE)"lang", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "AssignmentType", 0, REG_SZ, (LPBYTE)"type", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* FIXME */
+
+    /* AssignmentType value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageCode", 0, REG_SZ, (LPBYTE)"code", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Version", 0, REG_SZ, (LPBYTE)"ver", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductIcon", 0, REG_SZ, (LPBYTE)"icon", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PACKAGENAME, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "AuthorizedLUAApp", 0, REG_SZ, (LPBYTE)"auth", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* AuthorizedLUAApp value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteValueA(propkey, "AuthorizedLUAApp");
+    RegDeleteValueA(propkey, "PackageName");
+    RegDeleteValueA(propkey, "ProductIcon");
+    RegDeleteValueA(propkey, "Version");
+    RegDeleteValueA(propkey, "PackageCode");
+    RegDeleteValueA(propkey, "AssignmentType");
+    RegDeleteValueA(propkey, "ProductName");
+    RegDeleteValueA(propkey, "Language");
+    RegDeleteValueA(propkey, "Transforms");
+    RegDeleteValueA(propkey, "RegOwner");
+    RegDeleteValueA(propkey, "RegCompany");
+    RegDeleteValueA(propkey, "ProductID");
+    RegDeleteValueA(propkey, "DisplayVersion");
+    RegDeleteValueA(propkey, "VersionMajor");
+    RegDeleteValueA(propkey, "VersionMinor");
+    RegDeleteValueA(propkey, "URLUpdateInfo");
+    RegDeleteValueA(propkey, "URLInfoAbout");
+    RegDeleteValueA(propkey, "Publisher");
+    RegDeleteValueA(propkey, "LocalPackage");
+    RegDeleteValueA(propkey, "InstallSource");
+    RegDeleteValueA(propkey, "InstallLocation");
+    RegDeleteValueA(propkey, "DisplayName");
+    RegDeleteValueA(propkey, "InstallDate");
+    RegDeleteValueA(propkey, "HelpTelephone");
+    RegDeleteValueA(propkey, "HelpLink");
+    RegDeleteValueA(propkey, "LocalPackage");
+    RegDeleteKeyA(propkey, "");
+    RegCloseKey(propkey);
+    RegDeleteKeyA(localkey, "");
+    RegCloseKey(localkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteKeyA(userkey, "");
+    RegCloseKey(userkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "1"), "Expected \"1\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "HelpTelephone", 0, REG_SZ, (LPBYTE)"phone", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpTelephone value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "InstallDate", 0, REG_SZ, (LPBYTE)"date", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallDate value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLDATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "DisplayName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLEDPRODUCTNAME, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "InstallLocation", 0, REG_SZ, (LPBYTE)"loc", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallLocation value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLLOCATION, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "InstallSource", 0, REG_SZ, (LPBYTE)"source", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallSource value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_INSTALLSOURCE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_LOCALPACKAGE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Publisher", 0, REG_SZ, (LPBYTE)"pub", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Publisher value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PUBLISHER, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "URLInfoAbout", 0, REG_SZ, (LPBYTE)"about", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLInfoAbout value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_URLINFOABOUT, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "URLUpdateInfo", 0, REG_SZ, (LPBYTE)"update", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLUpdateInfo value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_URLUPDATEINFO, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "VersionMinor", 0, REG_SZ, (LPBYTE)"2", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMinor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONMINOR, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "VersionMajor", 0, REG_SZ, (LPBYTE)"3", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMajor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONMAJOR, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "DisplayVersion", 0, REG_SZ, (LPBYTE)"3.2.1", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayVersion value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSIONSTRING, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductID", 0, REG_SZ, (LPBYTE)"id", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductID value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTID, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "RegCompany", 0, REG_SZ, (LPBYTE)"comp", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegCompany value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_REGCOMPANY, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "RegOwner", 0, REG_SZ, (LPBYTE)"owner", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegOwner value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_REGOWNER, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Transforms", 0, REG_SZ, (LPBYTE)"trans", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "trans"), "Expected \"trans\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "Language", 0, REG_SZ, (LPBYTE)"lang", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "lang"), "Expected \"lang\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "name"), "Expected \"name\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "AssignmentType", 0, REG_SZ, (LPBYTE)"type", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* FIXME */
+
+    /* AssignmentType value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
+    ok(sz == 0, "Expected 0, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "PackageCode", 0, REG_SZ, (LPBYTE)"code", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* FIXME */
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    todo_wine
+    {
+        ok(r == ERROR_BAD_CONFIGURATION,
+           "Expected ERROR_BAD_CONFIGURATION, got %d\n", r);
+        ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+        ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+    }
+
+    res = RegSetValueExA(prodkey, "Version", 0, REG_SZ, (LPBYTE)"ver", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "ver"), "Expected \"ver\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "ProductIcon", 0, REG_SZ, (LPBYTE)"icon", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "icon"), "Expected \"icon\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "PackageName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_PACKAGENAME, buf, &sz);
+    todo_wine
+    {
+        ok(r == ERROR_UNKNOWN_PRODUCT,
+           "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+        ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+        ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+    }
+
+    res = RegSetValueExA(prodkey, "AuthorizedLUAApp", 0, REG_SZ, (LPBYTE)"auth", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* AuthorizedLUAApp value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERUNMANAGED,
+                              INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "auth"), "Expected \"auth\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    RegDeleteValueA(prodkey, "AuthorizedLUAApp");
+    RegDeleteValueA(prodkey, "PackageName");
+    RegDeleteValueA(prodkey, "ProductIcon");
+    RegDeleteValueA(prodkey, "Version");
+    RegDeleteValueA(prodkey, "PackageCode");
+    RegDeleteValueA(prodkey, "AssignmentType");
+    RegDeleteValueA(prodkey, "ProductName");
+    RegDeleteValueA(prodkey, "Language");
+    RegDeleteValueA(prodkey, "Transforms");
+    RegDeleteValueA(prodkey, "RegOwner");
+    RegDeleteValueA(prodkey, "RegCompany");
+    RegDeleteValueA(prodkey, "ProductID");
+    RegDeleteValueA(prodkey, "DisplayVersion");
+    RegDeleteValueA(prodkey, "VersionMajor");
+    RegDeleteValueA(prodkey, "VersionMinor");
+    RegDeleteValueA(prodkey, "URLUpdateInfo");
+    RegDeleteValueA(prodkey, "URLInfoAbout");
+    RegDeleteValueA(prodkey, "Publisher");
+    RegDeleteValueA(prodkey, "LocalPackage");
+    RegDeleteValueA(prodkey, "InstallSource");
+    RegDeleteValueA(prodkey, "InstallLocation");
+    RegDeleteValueA(prodkey, "DisplayName");
+    RegDeleteValueA(prodkey, "InstallDate");
+    RegDeleteValueA(prodkey, "HelpTelephone");
+    RegDeleteValueA(prodkey, "HelpLink");
+    RegDeleteValueA(prodkey, "LocalPackage");
+    RegDeleteKeyA(prodkey, "");
+    RegCloseKey(prodkey);
+
+    /* MSIINSTALLCONTEXT_USERMANAGED */
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* local user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegCreateKeyA(localkey, "InstallProperties", &propkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallProperties key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ManagedLocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ManagedLocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "link"), "Expected \"link\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "HelpTelephone", 0, REG_SZ, (LPBYTE)"phone", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpTelephone value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_HELPTELEPHONE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "phone"), "Expected \"phone\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallDate", 0, REG_SZ, (LPBYTE)"date", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallDate value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_INSTALLDATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "date"), "Expected \"date\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_INSTALLEDPRODUCTNAME, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "name"), "Expected \"name\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallLocation", 0, REG_SZ, (LPBYTE)"loc", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallLocation value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_INSTALLLOCATION, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "loc"), "Expected \"loc\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "InstallSource", 0, REG_SZ, (LPBYTE)"source", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* InstallSource value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_INSTALLSOURCE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "source"), "Expected \"source\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "LocalPackage", 0, REG_SZ, (LPBYTE)"local", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* LocalPackage value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_LOCALPACKAGE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "local"), "Expected \"local\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Publisher", 0, REG_SZ, (LPBYTE)"pub", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Publisher value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PUBLISHER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "pub"), "Expected \"pub\", got \"%s\"\n", buf);
+    ok(sz == 3, "Expected 3, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "URLInfoAbout", 0, REG_SZ, (LPBYTE)"about", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLInfoAbout value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_URLINFOABOUT, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "about"), "Expected \"about\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "URLUpdateInfo", 0, REG_SZ, (LPBYTE)"update", 7);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* URLUpdateInfo value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_URLUPDATEINFO, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "update"), "Expected \"update\", got \"%s\"\n", buf);
+    ok(sz == 6, "Expected 6, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMinor", 0, REG_SZ, (LPBYTE)"2", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMinor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_VERSIONMINOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "2"), "Expected \"2\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "VersionMajor", 0, REG_SZ, (LPBYTE)"3", 2);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* VersionMajor value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_VERSIONMAJOR, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "3"), "Expected \"3\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "DisplayVersion", 0, REG_SZ, (LPBYTE)"3.2.1", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* DisplayVersion value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_VERSIONSTRING, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "3.2.1"), "Expected \"3.2.1\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductID", 0, REG_SZ, (LPBYTE)"id", 3);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductID value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTID, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "id"), "Expected \"id\", got \"%s\"\n", buf);
+    ok(sz == 2, "Expected 2, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegCompany", 0, REG_SZ, (LPBYTE)"comp", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegCompany value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_REGCOMPANY, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "comp"), "Expected \"comp\", got \"%s\"\n", buf);
+    ok(sz == 4, "Expected 4, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "RegOwner", 0, REG_SZ, (LPBYTE)"owner", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* RegOwner value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_REGOWNER, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "owner"), "Expected \"owner\", got \"%s\"\n", buf);
+    ok(sz == 5, "Expected 5, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Transforms", 0, REG_SZ, (LPBYTE)"trans", 6);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Transforms value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_TRANSFORMS, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Language", 0, REG_SZ, (LPBYTE)"lang", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Language value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_LANGUAGE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTNAME, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "AssignmentType", 0, REG_SZ, (LPBYTE)"type", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* FIXME */
+
+    /* AssignmentType value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_ASSIGNMENTTYPE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageCode", 0, REG_SZ, (LPBYTE)"code", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageCode value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PACKAGECODE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "Version", 0, REG_SZ, (LPBYTE)"ver", 4);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* Version value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_VERSION, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "ProductIcon", 0, REG_SZ, (LPBYTE)"icon", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* ProductIcon value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTICON, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "PackageName", 0, REG_SZ, (LPBYTE)"name", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* PackageName value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PACKAGENAME, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(propkey, "AuthorizedLUAApp", 0, REG_SZ, (LPBYTE)"auth", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* AuthorizedLUAApp value exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_AUTHORIZED_LUA_APP, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    RegDeleteValueA(propkey, "AuthorizedLUAApp");
+    RegDeleteValueA(propkey, "PackageName");
+    RegDeleteValueA(propkey, "ProductIcon");
+    RegDeleteValueA(propkey, "Version");
+    RegDeleteValueA(propkey, "PackageCode");
+    RegDeleteValueA(propkey, "AssignmentType");
+    RegDeleteValueA(propkey, "ProductName");
+    RegDeleteValueA(propkey, "Language");
+    RegDeleteValueA(propkey, "Transforms");
+    RegDeleteValueA(propkey, "RegOwner");
+    RegDeleteValueA(propkey, "RegCompany");
+    RegDeleteValueA(propkey, "ProductID");
+    RegDeleteValueA(propkey, "DisplayVersion");
+    RegDeleteValueA(propkey, "VersionMajor");
+    RegDeleteValueA(propkey, "VersionMinor");
+    RegDeleteValueA(propkey, "URLUpdateInfo");
+    RegDeleteValueA(propkey, "URLInfoAbout");
+    RegDeleteValueA(propkey, "Publisher");
+    RegDeleteValueA(propkey, "LocalPackage");
+    RegDeleteValueA(propkey, "InstallSource");
+    RegDeleteValueA(propkey, "InstallLocation");
+    RegDeleteValueA(propkey, "DisplayName");
+    RegDeleteValueA(propkey, "InstallDate");
+    RegDeleteValueA(propkey, "HelpTelephone");
+    RegDeleteValueA(propkey, "HelpLink");
+    RegDeleteValueA(propkey, "ManagedLocalPackage");
+    RegDeleteKeyA(propkey, "");
+    RegCloseKey(propkey);
+    RegDeleteKeyA(localkey, "");
+    RegCloseKey(localkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    ok(!lstrcmpA(buf, "1"), "Expected \"1\", got \"%s\"\n", buf);
+    ok(sz == 1, "Expected 1, got %d\n", sz);
+
+    RegDeleteKeyA(userkey, "");
+    RegCloseKey(userkey);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* current user product key exists */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_PRODUCTSTATE, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    res = RegSetValueExA(prodkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists, user product key does not exist */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PRODUCT,
+       "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
+    ok(sz == MAX_PATH, "Expected MAX_PATH, got %d\n", sz);
+
+    lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\");
+    lstrcatA(keypath, usersid);
+    lstrcatA(keypath, "\\Installer\\Products\\");
+    lstrcatA(keypath, prod_squashed);
+
+    res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    res = RegSetValueExA(userkey, "HelpLink", 0, REG_SZ, (LPBYTE)"link", 5);
+    ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res);
+
+    /* HelpLink value exists, user product key does exist */
+    sz = MAX_PATH;
+    lstrcpyA(buf, "apple");
+    r = pMsiGetProductInfoExA(prodcode, usersid,
+                              MSIINSTALLCONTEXT_USERMANAGED,
+                              INSTALLPROPERTY_HELPLINK, buf, &sz);
+    ok(r == ERROR_UNKNOWN_PROPERTY,
+       "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r);
+    ok(!lstrcmpA(buf, "apple"), "Expected buf to be unchanged,&