[MSI]
authorAmine Khaldi <amine.khaldi@reactos.org>
Mon, 14 May 2012 21:41:31 +0000 (21:41 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Mon, 14 May 2012 21:41:31 +0000 (21:41 +0000)
* Sync to Wine 1.5.4.

svn path=/trunk/; revision=56588

22 files changed:
reactos/dll/win32/msi/CMakeLists.txt
reactos/dll/win32/msi/action.c
reactos/dll/win32/msi/appsearch.c
reactos/dll/win32/msi/assembly.c
reactos/dll/win32/msi/automation.c
reactos/dll/win32/msi/custom.c
reactos/dll/win32/msi/dialog.c
reactos/dll/win32/msi/files.c
reactos/dll/win32/msi/install.c
reactos/dll/win32/msi/media.c
reactos/dll/win32/msi/msi.c
reactos/dll/win32/msi/msi.rc
reactos/dll/win32/msi/msi.rgs
reactos/dll/win32/msi/msi.spec
reactos/dll/win32/msi/msipriv.h
reactos/dll/win32/msi/package.c
reactos/dll/win32/msi/registry.c
reactos/dll/win32/msi/script.c
reactos/dll/win32/msi/sql.tab.c
reactos/dll/win32/msi/table.c
reactos/include/psdk/msi.h
reactos/media/doc/README.WINE

index b1d9c0d..4c11ba2 100644 (file)
@@ -7,7 +7,6 @@ add_definitions(-D__WINESRC__ -DMSIRUNMODE=MSIRUNMODE_T)
 remove_definitions(-D_WIN32_WINNT=0x502)
 add_definitions(-D_WIN32_WINNT=0x600)
 
-set_rc_compiler()
 spec2def(msi.dll msi.spec ADD_IMPORTLIB)
 
 generate_idl_iids(msiserver.idl)
@@ -98,5 +97,3 @@ add_pch(msi msipriv.h)
 add_cd_file(TARGET msi DESTINATION reactos/system32 FOR all)
 
 endif(NOT MSVC)
-
-
index 5444dfc..c1d3b49 100644 (file)
@@ -222,7 +222,6 @@ static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
             switch (*p)
             {
             case ' ':
-                if (!count) goto done;
                 in_quotes = 1;
                 ignore = 1;
                 len++;
@@ -234,8 +233,7 @@ static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
                 break;
             default:
                 state = state_token;
-                if (!count) in_quotes = 0;
-                else in_quotes = 1;
+                in_quotes = 1;
                 len++;
                 break;
             }
@@ -482,8 +480,7 @@ UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
 
 static BOOL needs_ui_sequence(MSIPACKAGE *package)
 {
-    INT level = msi_get_property_int(package->db, szUILevel, 0);
-    return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
+    return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
 }
 
 UINT msi_set_context(MSIPACKAGE *package)
@@ -539,6 +536,12 @@ static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
     if (rc != ERROR_SUCCESS)
         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
 
+    if (package->need_reboot_now)
+    {
+        TRACE("action %s asked for immediate reboot, suspending installation\n",
+              debugstr_w(action));
+        rc = ACTION_ForceReboot( package );
+    }
     return rc;
 }
 
@@ -881,6 +884,20 @@ static UINT ACTION_CreateFolders(MSIPACKAGE *package)
     return rc;
 }
 
+static void remove_persistent_folder( MSIFOLDER *folder )
+{
+    FolderList *fl;
+
+    LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
+    {
+        remove_persistent_folder( fl->folder );
+    }
+    if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
+    {
+        if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
+    }
+}
+
 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
 {
     MSIPACKAGE *package = param;
@@ -924,9 +941,8 @@ static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
     msi_ui_actiondata( package, szRemoveFolders, uirow );
     msiobj_release( &uirow->hdr );
 
-    RemoveDirectoryW( full_path );
     folder = msi_get_loaded_folder( package, dir );
-    folder->State = FOLDER_STATE_REMOVED;
+    remove_persistent_folder( folder );
     return ERROR_SUCCESS;
 }
 
@@ -1617,16 +1633,19 @@ static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
         r = MsiQueryComponentStateW( package->ProductCode, NULL,
                                      MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
                                      &comp->Installed );
-        if (r != ERROR_SUCCESS)
-            r = MsiQueryComponentStateW( package->ProductCode, NULL,
-                                         MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
-                                         &comp->Installed );
-        if (r != ERROR_SUCCESS)
-            r = MsiQueryComponentStateW( package->ProductCode, NULL,
-                                         MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
-                                         &comp->Installed );
-        if (r != ERROR_SUCCESS)
-            comp->Installed = INSTALLSTATE_ABSENT;
+        if (r == ERROR_SUCCESS) continue;
+
+        r = MsiQueryComponentStateW( package->ProductCode, NULL,
+                                     MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
+                                     &comp->Installed );
+        if (r == ERROR_SUCCESS) continue;
+
+        r = MsiQueryComponentStateW( package->ProductCode, NULL,
+                                     MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
+                                     &comp->Installed );
+        if (r == ERROR_SUCCESS) continue;
+
+        comp->Installed = INSTALLSTATE_ABSENT;
     }
 }
 
@@ -1795,6 +1814,9 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
                 }
                 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
                 {
+                    TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
+                          debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
+                          debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
                     fl->feature->Action = feature->Action;
                     fl->feature->ActionRequest = feature->ActionRequest;
                 }
@@ -1825,12 +1847,14 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
         {
             FeatureList *fl;
 
-            if (!is_feature_selected( feature, level )) continue;
-
             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
             {
-                if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
+                if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
+                    (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
                 {
+                    TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
+                          debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
+                          debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
                     fl->feature->Action = feature->Action;
                     fl->feature->ActionRequest = feature->ActionRequest;
                 }
@@ -1843,7 +1867,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
     {
         ComponentList *cl;
 
-        TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
+        TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
               debugstr_w(feature->Feature), feature->Level, feature->Installed,
               feature->ActionRequest, feature->Action);
 
@@ -1958,7 +1982,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
             component->ActionRequest = INSTALLSTATE_UNKNOWN;
         }
 
-        TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
+        TRACE("component %s (installed %d request %d action %d)\n",
               debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
     }
 
@@ -2214,11 +2238,15 @@ static UINT calculate_file_cost( MSIPACKAGE *package )
     return ERROR_SUCCESS;
 }
 
-void msi_clean_path( WCHAR *p )
+WCHAR *msi_normalize_path( const WCHAR *in )
 {
-    WCHAR *q = p;
-    int n, len = 0;
+    const WCHAR *p = in;
+    WCHAR *q, *ret;
+    int n, len = strlenW( in ) + 2;
 
+    if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
+
+    len = 0;
     while (1)
     {
         /* copy until the end of the string or a space */
@@ -2245,32 +2273,20 @@ void msi_clean_path( WCHAR *p )
         else  /* copy n spaces */
             while (n && (*q++ = *p++)) n--;
     }
-}
-
-static WCHAR *get_target_dir_property( MSIDATABASE *db )
-{
-    int len;
-    WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
-
-    if (!target_dir) return NULL;
-
-    len = strlenW( target_dir );
-    if (target_dir[len - 1] == '\\') return target_dir;
-    if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
+    while (q - ret > 0 && q[-1] == ' ') q--;
+    if (q - ret > 0 && q[-1] != '\\')
     {
-        strcpyW( path, target_dir );
-        path[len] = '\\';
-        path[len + 1] = 0;
+        q[0] = '\\';
+        q[1] = 0;
     }
-    msi_free( target_dir );
-    return path;
+    return ret;
 }
 
 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
 {
     FolderList *fl;
     MSIFOLDER *folder, *parent, *child;
-    WCHAR *path;
+    WCHAR *path, *normalized_path;
 
     TRACE("resolving %s\n", debugstr_w(name));
 
@@ -2278,7 +2294,7 @@ void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL loa
 
     if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
     {
-        if (!load_prop || !(path = get_target_dir_property( package->db )))
+        if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
         {
             path = msi_dup_property( package->db, szRootDrive );
         }
@@ -2293,16 +2309,17 @@ void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL loa
         else
             path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
     }
-    msi_clean_path( path );
-    if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
+    normalized_path = msi_normalize_path( path );
+    msi_free( path );
+    if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
     {
         TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
-        msi_free( path );
+        msi_free( normalized_path );
         return;
     }
-    msi_set_property( package->db, folder->Directory, path );
+    msi_set_property( package->db, folder->Directory, normalized_path );
     msi_free( folder->ResolvedTarget );
-    folder->ResolvedTarget = path;
+    folder->ResolvedTarget = normalized_path;
 
     LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
     {
@@ -2953,7 +2970,7 @@ static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
     r = MSI_EvaluateConditionW(package,cond);
     if (r == MSICONDITION_FALSE)
     {
-        if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
+        if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
         {
             LPWSTR deformated;
             message = MSI_RecordGetString(row,2);
@@ -3608,25 +3625,9 @@ static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
     target = MSI_RecordGetString(row, 5);
     if (strchrW(target, '['))
     {
-        int len;
-        WCHAR *format_string, *p;
-
-        if (!(p = strchrW( target, ']' ))) goto err;
-        len = p - target + 1;
-        format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
-        memcpy( format_string, target, len * sizeof(WCHAR) );
-        format_string[len] = 0;
-        deformat_string( package, format_string, &deformated );
-        msi_free( format_string );
-
-        path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
-        strcpyW( path, deformated );
-        PathAddBackslashW( path );
-        strcatW( path, p + 1 );
+        deformat_string( package, target, &path );
         TRACE("target path is %s\n", debugstr_w(path));
-
         IShellLinkW_SetPath( sl, path );
-        msi_free( deformated );
         msi_free( path );
     }
     else
@@ -4479,7 +4480,7 @@ static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
     MSIFILE *file;
     MSIRECORD *uirow;
 
-    filename = MSI_RecordGetString(row,1);
+    filename = MSI_RecordGetString( row, 1 );
     file = msi_get_loaded_file( package, filename );
     if (!file)
     {
@@ -4497,7 +4498,7 @@ static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
     register_dll( file->TargetPath, FALSE );
 
     uirow = MSI_CreateRecord( 2 );
-    MSI_RecordSetStringW( uirow, 1, filename );
+    MSI_RecordSetStringW( uirow, 1, file->File );
     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
     msi_ui_actiondata( package, szSelfRegModules, uirow );
     msiobj_release( &uirow->hdr );
@@ -4547,7 +4548,7 @@ static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
     register_dll( file->TargetPath, TRUE );
 
     uirow = MSI_CreateRecord( 2 );
-    MSI_RecordSetStringW( uirow, 1, filename );
+    MSI_RecordSetStringW( uirow, 1, file->File );
     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
     msi_ui_actiondata( package, szSelfUnregModules, uirow );
     msiobj_release( &uirow->hdr );
@@ -5933,28 +5934,25 @@ static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
     MSIPACKAGE *package = param;
     MSICOMPONENT *comp;
     MSIRECORD *uirow;
-    LPCWSTR component;
     LPWSTR name = NULL, display_name = NULL;
     DWORD event, len;
     SC_HANDLE scm = NULL, service = NULL;
 
-    event = MSI_RecordGetInteger( rec, 3 );
-    if (!(event & msidbServiceControlEventDelete))
-        return ERROR_SUCCESS;
-
-    component = MSI_RecordGetString(rec, 6);
-    comp = msi_get_loaded_component(package, component);
+    comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
     if (!comp)
         return ERROR_SUCCESS;
 
+    event = MSI_RecordGetInteger( rec, 3 );
+    deformat_string( package, MSI_RecordGetString(rec, 2), &name );
+
     comp->Action = msi_get_component_action( package, comp );
-    if (comp->Action != INSTALLSTATE_ABSENT)
+    if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
+        !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
     {
-        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
+        TRACE("service %s not scheduled for removal\n", debugstr_w(name));
+        msi_free( name );
         return ERROR_SUCCESS;
     }
-
-    deformat_string( package, MSI_RecordGetString(rec, 2), &name );
     stop_service( name );
 
     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
@@ -6084,6 +6082,11 @@ static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
     ptr += lstrlenW(ptr) + 1;
     *ptr = '\0';
 
+    if (!driver_file->TargetPath)
+    {
+        const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
+        driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
+    }
     driver_path = strdupW(driver_file->TargetPath);
     ptr = strrchrW(driver_path, '\\');
     if (ptr) *ptr = '\0';
@@ -6907,7 +6910,7 @@ static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
 {
     TRACE("\n");
-    package->need_reboot = 1;
+    package->need_reboot_at_end = 1;
     return ERROR_SUCCESS;
 }
 
@@ -7532,7 +7535,7 @@ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
     }
     msi_free( reinstall );
 
-    if (rc == ERROR_SUCCESS && package->need_reboot)
+    if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
         return ERROR_SUCCESS_REBOOT_REQUIRED;
 
     return rc;
index 0010fd8..21daf43 100644 (file)
@@ -608,6 +608,65 @@ static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst,
     msi_free(deformatted);
 }
 
+static LANGID *parse_languages( const WCHAR *languages, DWORD *num_ids )
+{
+    UINT i, count = 1;
+    WCHAR *str = strdupW( languages ), *p, *q;
+    LANGID *ret;
+
+    if (!str) return NULL;
+    for (p = q = str; (q = strchrW( q, ',' )); q++) count++;
+
+    if (!(ret = msi_alloc( count * sizeof(LANGID) )))
+    {
+        msi_free( str );
+        return NULL;
+    }
+    i = 0;
+    while (*p)
+    {
+        q = strchrW( p, ',' );
+        if (q) *q = 0;
+        ret[i] = atoiW( p );
+        if (!q) break;
+        p = q + 1;
+        i++;
+    }
+    msi_free( str );
+    *num_ids = count;
+    return ret;
+}
+
+static BOOL match_languages( const void *version, const WCHAR *languages )
+{
+    struct lang
+    {
+        USHORT id;
+        USHORT codepage;
+    } *lang;
+    DWORD len, num_ids, i, j;
+    BOOL found = FALSE;
+    LANGID *ids;
+
+    if (!languages || !languages[0]) return TRUE;
+    if (!VerQueryValueW( version, szLangResource, (void **)&lang, &len )) return FALSE;
+    if (!(ids = parse_languages( languages, &num_ids ))) return FALSE;
+
+    for (i = 0; i < num_ids; i++)
+    {
+        found = FALSE;
+        for (j = 0; j < len / sizeof(struct lang); j++)
+        {
+            if (!ids[i] || ids[i] == lang[j].id) found = TRUE;
+        }
+        if (!found) goto done;
+    }
+
+done:
+    msi_free( ids );
+    return found;
+}
+
 /* Sets *matches to whether the file (whose path is filePath) matches the
  * versions set in sig.
  * Return ERROR_SUCCESS in case of success (whether or not the file matches),
@@ -616,69 +675,55 @@ static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst,
 static UINT ACTION_FileVersionMatches(const MSISIGNATURE *sig, LPCWSTR filePath,
  BOOL *matches)
 {
-    UINT rc = ERROR_SUCCESS;
+    UINT len;
+    void *version;
+    VS_FIXEDFILEINFO *info = NULL;
+    DWORD zero, size = GetFileVersionInfoSizeW( filePath, &zero );
 
     *matches = FALSE;
-    if (sig->Languages)
-    {
-        FIXME(": need to check version for languages %s\n",
-         debugstr_w(sig->Languages));
-    }
-    else
-    {
-        DWORD zero, size = GetFileVersionInfoSizeW(filePath, &zero);
 
-        if (size)
-        {
-            LPVOID buf = msi_alloc( size);
+    if (!size) return ERROR_SUCCESS;
+    if (!(version = msi_alloc( size ))) return ERROR_OUTOFMEMORY;
 
-            if (buf)
-            {
-                UINT versionLen;
-                LPVOID subBlock = NULL;
-
-                if (GetFileVersionInfoW(filePath, 0, size, buf))
-                    VerQueryValueW(buf, szBackSlash, &subBlock, &versionLen);
-                if (subBlock)
-                {
-                    VS_FIXEDFILEINFO *info = subBlock;
-
-                    TRACE("Comparing file version %d.%d.%d.%d:\n",
-                     HIWORD(info->dwFileVersionMS),
-                     LOWORD(info->dwFileVersionMS),
-                     HIWORD(info->dwFileVersionLS),
-                     LOWORD(info->dwFileVersionLS));
-                    if (info->dwFileVersionMS < sig->MinVersionMS
-                     || (info->dwFileVersionMS == sig->MinVersionMS &&
-                     info->dwFileVersionLS < sig->MinVersionLS))
-                    {
-                        TRACE("Less than minimum version %d.%d.%d.%d\n",
-                         HIWORD(sig->MinVersionMS),
-                         LOWORD(sig->MinVersionMS),
-                         HIWORD(sig->MinVersionLS),
-                         LOWORD(sig->MinVersionLS));
-                    }
-                    else if ((sig->MaxVersionMS || sig->MaxVersionLS) &&
-                             (info->dwFileVersionMS > sig->MaxVersionMS ||
-                              (info->dwFileVersionMS == sig->MaxVersionMS &&
-                               info->dwFileVersionLS > sig->MaxVersionLS)))
-                    {
-                        TRACE("Greater than maximum version %d.%d.%d.%d\n",
-                         HIWORD(sig->MaxVersionMS),
-                         LOWORD(sig->MaxVersionMS),
-                         HIWORD(sig->MaxVersionLS),
-                         LOWORD(sig->MaxVersionLS));
-                    }
-                    else
-                        *matches = TRUE;
-                }
-                msi_free( buf);
-            }
-            else
-                rc = ERROR_OUTOFMEMORY;
+    if (GetFileVersionInfoW( filePath, 0, size, version ))
+        VerQueryValueW( version, szBackSlash, (void **)&info, &len );
+
+    if (info)
+    {
+        TRACE("comparing file version %d.%d.%d.%d:\n",
+              HIWORD(info->dwFileVersionMS),
+              LOWORD(info->dwFileVersionMS),
+              HIWORD(info->dwFileVersionLS),
+              LOWORD(info->dwFileVersionLS));
+        if (info->dwFileVersionMS < sig->MinVersionMS
+            || (info->dwFileVersionMS == sig->MinVersionMS &&
+                info->dwFileVersionLS < sig->MinVersionLS))
+        {
+            TRACE("less than minimum version %d.%d.%d.%d\n",
+                   HIWORD(sig->MinVersionMS),
+                   LOWORD(sig->MinVersionMS),
+                   HIWORD(sig->MinVersionLS),
+                   LOWORD(sig->MinVersionLS));
+        }
+        else if ((sig->MaxVersionMS || sig->MaxVersionLS) &&
+                 (info->dwFileVersionMS > sig->MaxVersionMS ||
+                  (info->dwFileVersionMS == sig->MaxVersionMS &&
+                   info->dwFileVersionLS > sig->MaxVersionLS)))
+        {
+            TRACE("greater than maximum version %d.%d.%d.%d\n",
+                   HIWORD(sig->MaxVersionMS),
+                   LOWORD(sig->MaxVersionMS),
+                   HIWORD(sig->MaxVersionLS),
+                   LOWORD(sig->MaxVersionLS));
+        }
+        else if (!match_languages( version, sig->Languages ))
+        {
+            TRACE("languages %s not supported\n", debugstr_w( sig->Languages ));
         }
+        else *matches = TRUE;
     }
-    return rc;
+    msi_free( version );
+    return ERROR_SUCCESS;
 }
 
 /* Sets *matches to whether the file in findData matches that in sig.
index 6ce020e..712bb23 100644 (file)
@@ -262,13 +262,13 @@ static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_n
 
     memset( &info, 0, sizeof(info) );
     info.cbAssemblyInfo = sizeof(info);
-    hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_GETSIZE, display_name, &info );
-    if (hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
+    hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info );
+    if (hr == S_OK /* sxs version */ || hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
     {
-        TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
-        return FALSE;
+        return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
     }
-    return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
+    TRACE("QueryAssemblyInfo returned 0x%08x\n", hr);
+    return FALSE;
 }
 
 static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0};
@@ -425,6 +425,45 @@ UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
     return ERROR_SUCCESS;
 }
 
+UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp )
+{
+    HRESULT hr;
+    IAssemblyCache *cache;
+    MSIASSEMBLY *assembly = comp->assembly;
+    MSIFEATURE *feature = NULL;
+
+    if (comp->assembly->feature)
+        feature = msi_get_loaded_feature( package, comp->assembly->feature );
+
+    if (assembly->application)
+    {
+        if (feature) feature->Action = INSTALLSTATE_ABSENT;
+        return ERROR_SUCCESS;
+    }
+    TRACE("removing %s\n", debugstr_w(assembly->display_name));
+
+    if (assembly->attributes == msidbAssemblyAttributesWin32)
+    {
+        cache = package->cache_sxs;
+        hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
+        if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
+    }
+    else
+    {
+        unsigned int i;
+        for (i = 0; i < CLR_VERSION_MAX; i++)
+        {
+            if (!assembly->clr_version[i]) continue;
+            cache = package->cache_net[i];
+            hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL );
+            if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr);
+        }
+    }
+    if (feature) feature->Action = INSTALLSTATE_ABSENT;
+    assembly->installed = FALSE;
+    return ERROR_SUCCESS;
+}
+
 static WCHAR *build_local_assembly_path( const WCHAR *filename )
 {
     UINT i;
index d62c989..918572a 100644 (file)
@@ -44,19 +44,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
 /*
  * AutomationObject - "base" class for all automation objects. For each interface, we implement Invoke function
- *                    called from AutomationObject::Invoke, and pass this function to create_automation_object.
+ *                    called from AutomationObject::Invoke.
  */
 
 typedef struct AutomationObject AutomationObject;
 
-struct AutomationObject {
-    /*
-     * VTables - We provide IDispatch, IProvideClassInfo, IProvideClassInfo2, IProvideMultipleClassInfo
-     */
-    const IDispatchVtbl *lpVtbl;
-    const IProvideMultipleClassInfoVtbl *lpvtblIProvideMultipleClassInfo;
+typedef HRESULT (*autoInvokeFunc)(AutomationObject* This,
+    DISPID dispIdMember, REFIID riid, LCID lcid, WORD flags, DISPPARAMS* pDispParams,
+    VARIANT* result, EXCEPINFO* ei, UINT* arg_err);
+
+typedef void (*autoFreeFunc)(AutomationObject* This);
 
-    /* Object reference count */
+struct AutomationObject {
+    IDispatch IDispatch_iface;
+    IProvideMultipleClassInfo IProvideMultipleClassInfo_iface;
     LONG ref;
 
     /* Clsid for this class and it's appropriate ITypeInfo object */
@@ -67,68 +68,62 @@ struct AutomationObject {
     MSIHANDLE msiHandle;
 
     /* A function that is called from AutomationObject::Invoke, specific to this type of object. */
-    HRESULT (*funcInvoke)(
-        AutomationObject* This,
-        DISPID dispIdMember,
-        REFIID riid,
-        LCID lcid,
-        WORD wFlags,
-        DISPPARAMS* pDispParams,
-        VARIANT* pVarResult,
-        EXCEPINFO* pExcepInfo,
-        UINT* puArgErr);
-
+    autoInvokeFunc funcInvoke;
     /* A function that is called from AutomationObject::Release when the object is being freed to free any private
      * data structures (or NULL) */
-    void (*funcFree)(AutomationObject* This);
+    autoFreeFunc funcFree;
 };
 
-/*
- * ListEnumerator - IEnumVARIANT implementation for MSI automation lists.
- */
+typedef struct {
+    AutomationObject autoobj;
+    int count;
+    VARIANT *data;
+} ListObject;
+
+static HRESULT create_database(MSIHANDLE, IDispatch**);
+static HRESULT create_list_enumerator(ListObject*, void**);
+static HRESULT create_summaryinfo(MSIHANDLE, IDispatch**);
+static HRESULT create_view(MSIHANDLE, IDispatch**);
 
+/* ListEnumerator - IEnumVARIANT implementation for MSI automation lists */
 typedef struct {
     IEnumVARIANT IEnumVARIANT_iface;
     LONG ref;
 
     /* Current position and pointer to AutomationObject that stores actual data */
-    ULONG ulPos;
-    AutomationObject *pObj;
+    ULONG pos;
+    ListObject *list;
 } ListEnumerator;
 
-/*
- * Structures for additional data required by specific automation objects
- */
-
 typedef struct {
-    ULONG ulCount;
-    VARIANT *pVars;
-} ListData;
+    AutomationObject autoobj;
+    IDispatch *installer;
+} SessionObject;
 
-typedef struct {
-    /* The parent Installer object */
-    IDispatch *pInstaller;
-} SessionData;
+static inline AutomationObject *impl_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
+{
+    return CONTAINING_RECORD(iface, AutomationObject, IProvideMultipleClassInfo_iface);
+}
 
-/* VTables */
-static const struct IDispatchVtbl AutomationObject_Vtbl;
-static const struct IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl;
-static const struct IEnumVARIANTVtbl ListEnumerator_Vtbl;
+static inline AutomationObject *impl_from_IDispatch( IDispatch *iface )
+{
+    return CONTAINING_RECORD(iface, AutomationObject, IDispatch_iface);
+}
 
 /* Load type info so we don't have to process GetIDsOfNames */
 HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid)
 {
+    static const WCHAR msiserverW[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b',0};
+    ITypeInfo *ti = NULL;
+    ITypeLib *lib = NULL;
     HRESULT hr;
-    LPTYPELIB pLib = NULL;
-    LPTYPEINFO pInfo = NULL;
-    static const WCHAR szMsiServer[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b'};
 
-    TRACE("(%p)->(%s,%d)\n", iface, debugstr_guid(clsid), lcid);
+    TRACE("(%p)->(%s, %d)\n", iface, debugstr_guid(clsid), lcid);
 
     /* Load registered type library */
-    hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &pLib);
+    hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &lib);
     if (FAILED(hr)) {
-        hr = LoadTypeLib(szMsiServer, &pLib);
+        hr = LoadTypeLib(msiserverW, &lib);
         if (FAILED(hr)) {
             ERR("Could not load msiserver.tlb\n");
             return hr;
@@ -136,101 +131,20 @@ HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID
     }
 
     /* Get type information for object */
-    hr = ITypeLib_GetTypeInfoOfGuid(pLib, clsid, &pInfo);
-    ITypeLib_Release(pLib);
+    hr = ITypeLib_GetTypeInfoOfGuid(lib, clsid, &ti);
+    ITypeLib_Release(lib);
     if (FAILED(hr)) {
         ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid));
         return hr;
     }
-    *pptinfo = pInfo;
-    return S_OK;
-}
-
-/* Create the automation object, placing the result in the pointer ppObj. The automation object is created
- * with the appropriate clsid and invocation function. */
-static HRESULT create_automation_object(MSIHANDLE msiHandle, IUnknown *pUnkOuter, void **ppObj, REFIID clsid,
-        HRESULT (*funcInvoke)(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*),
-        void (*funcFree)(AutomationObject*), SIZE_T sizetPrivateData)
-{
-    AutomationObject *object;
-    HRESULT hr;
-
-    TRACE("(%d,%p,%p,%s,%p,%p,%ld)\n", msiHandle, pUnkOuter, ppObj, debugstr_guid(clsid), funcInvoke, funcFree, sizetPrivateData);
-
-    if( pUnkOuter )
-        return CLASS_E_NOAGGREGATION;
-
-    object = msi_alloc_zero( sizeof(AutomationObject) + sizetPrivateData );
-
-    /* Set all the VTable references */
-    object->lpVtbl = &AutomationObject_Vtbl;
-    object->lpvtblIProvideMultipleClassInfo = &AutomationObject_IProvideMultipleClassInfo_Vtbl;
-    object->ref = 1;
-
-    /* Store data that was passed */
-    object->msiHandle = msiHandle;
-    object->clsid = (LPCLSID)clsid;
-    object->funcInvoke = funcInvoke;
-    object->funcFree = funcFree;
-
-    /* Load our TypeInfo so we don't have to process GetIDsOfNames */
-    object->iTypeInfo = NULL;
-    hr = load_type_info((IDispatch *)object, &object->iTypeInfo, clsid, 0x0);
-    if (FAILED(hr)) {
-        msi_free( object );
-        return hr;
-    }
-
-    *ppObj = object;
-
+    *pptinfo = ti;
     return S_OK;
 }
 
-/* Create a list enumerator, placing the result in the pointer ppObj.  */
-static HRESULT create_list_enumerator(IUnknown *pUnkOuter, LPVOID *ppObj, AutomationObject *pObj, ULONG ulPos)
-{
-    ListEnumerator *object;
-
-    TRACE("(%p,%p,%p,%uld)\n", pUnkOuter, ppObj, pObj, ulPos);
-
-    if( pUnkOuter )
-        return CLASS_E_NOAGGREGATION;
-
-    object = msi_alloc_zero( sizeof(ListEnumerator) );
-
-    /* Set all the VTable references */
-    object->IEnumVARIANT_iface.lpVtbl = &ListEnumerator_Vtbl;
-    object->ref = 1;
-
-    /* Store data that was passed */
-    object->ulPos = ulPos;
-    object->pObj = pObj;
-    if (pObj) IDispatch_AddRef((IDispatch *)pObj);
-
-    *ppObj = object;
-    return S_OK;
-}
-
-/* Macros to get pointer to AutomationObject from the other VTables. */
-static inline AutomationObject *obj_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
-{
-    return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideMultipleClassInfo));
-}
-
-/* Macro to get pointer to private object data */
-static inline void *private_data( AutomationObject *This )
-{
-    return This + 1;
-}
-
-/*
- * AutomationObject methods
- */
-
-/*** IUnknown methods ***/
+/* AutomationObject methods */
 static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
 
     TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
 
@@ -239,30 +153,28 @@ static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID r
 
     *ppvObject = 0;
 
-    if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, This->clsid))
-        *ppvObject = This;
+    if (IsEqualGUID(riid, &IID_IUnknown)  ||
+        IsEqualGUID(riid, &IID_IDispatch) ||
+        IsEqualGUID(riid, This->clsid))
+        *ppvObject = &This->IDispatch_iface;
     else if (IsEqualGUID(riid, &IID_IProvideClassInfo) ||
              IsEqualGUID(riid, &IID_IProvideClassInfo2) ||
              IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
-        *ppvObject = &This->lpvtblIProvideMultipleClassInfo;
+        *ppvObject = &This->IProvideMultipleClassInfo_iface;
     else
     {
-        TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
+        TRACE("() : asking for unsupported interface %s\n", debugstr_guid(riid));
         return E_NOINTERFACE;
     }
 
-    /*
-     * Query Interface always increases the reference count by one when it is
-     * successful
-     */
-    IClassFactory_AddRef(iface);
+    IDispatch_AddRef(iface);
 
     return S_OK;
 }
 
 static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
 
     TRACE("(%p/%p)\n", iface, This);
 
@@ -271,7 +183,7 @@ static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
 
 static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
     ULONG ref = InterlockedDecrement(&This->ref);
 
     TRACE("(%p/%p)\n", iface, This);
@@ -287,12 +199,11 @@ static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
     return ref;
 }
 
-/*** IDispatch methods ***/
 static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
         IDispatch* iface,
         UINT* pctinfo)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
 
     TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
     *pctinfo = 1;
@@ -305,7 +216,7 @@ static HRESULT WINAPI AutomationObject_GetTypeInfo(
         LCID lcid,
         ITypeInfo** ppTInfo)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
     TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
 
     ITypeInfo_AddRef(This->iTypeInfo);
@@ -321,7 +232,7 @@ static HRESULT WINAPI AutomationObject_GetIDsOfNames(
         LCID lcid,
         DISPID* rgDispId)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
     HRESULT hr;
     TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
 
@@ -354,7 +265,7 @@ static HRESULT WINAPI AutomationObject_Invoke(
         EXCEPINFO* pExcepInfo,
         UINT* puArgErr)
 {
-    AutomationObject *This = (AutomationObject *)iface;
+    AutomationObject *This = impl_from_IDispatch(iface);
     HRESULT hr;
     unsigned int uArgErr;
     VARIANT varResultDummy;
@@ -441,7 +352,7 @@ static HRESULT WINAPI AutomationObject_Invoke(
     return hr;
 }
 
-static const struct IDispatchVtbl AutomationObject_Vtbl =
+static const struct IDispatchVtbl AutomationObjectVtbl =
 {
     AutomationObject_QueryInterface,
     AutomationObject_AddRef,
@@ -456,37 +367,37 @@ static const struct IDispatchVtbl AutomationObject_Vtbl =
  * IProvideMultipleClassInfo methods
  */
 
-static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_QueryInterface(
+static HRESULT WINAPI ProvideMultipleClassInfo_QueryInterface(
   IProvideMultipleClassInfo* iface,
   REFIID     riid,
   VOID**     ppvoid)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
-    return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
+    return IDispatch_QueryInterface(&This->IDispatch_iface, riid, ppvoid);
 }
 
-static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
+static ULONG WINAPI ProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
-    return AutomationObject_AddRef((IDispatch *)This);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
+    return IDispatch_AddRef(&This->IDispatch_iface);
 }
 
-static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
+static ULONG WINAPI ProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
-    return AutomationObject_Release((IDispatch *)This);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
+    return IDispatch_Release(&This->IDispatch_iface);
 }
 
-static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
+static HRESULT WINAPI ProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
     TRACE("(%p/%p)->(%p)\n", iface, This, ppTI);
-    return load_type_info((IDispatch *)This, ppTI, This->clsid, 0);
+    return load_type_info(&This->IDispatch_iface, ppTI, This->clsid, 0);
 }
 
-static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
+static HRESULT WINAPI ProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
     TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
 
     if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
@@ -497,16 +408,16 @@ static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvid
     }
 }
 
-static HRESULT WINAPI AutomationObject_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
+static HRESULT WINAPI ProvideMultipleClassInfo_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
 
     TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
     *pcti = 1;
     return S_OK;
 }
 
-static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
+static HRESULT WINAPI ProvideMultipleClassInfo_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
         ULONG iti,
         DWORD dwFlags,
         ITypeInfo** pptiCoClass,
@@ -515,7 +426,7 @@ static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo*
         IID* piidPrimary,
         IID* piidSource)
 {
-    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
 
     TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
 
@@ -523,7 +434,7 @@ static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo*
         return E_INVALIDARG;
 
     if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
-        load_type_info((IDispatch *)This, pptiCoClass, This->clsid, 0);
+        load_type_info(&This->IDispatch_iface, pptiCoClass, This->clsid, 0);
 
     if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
     {
@@ -542,17 +453,36 @@ static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo*
     return S_OK;
 }
 
-static const IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl =
+static const IProvideMultipleClassInfoVtbl ProvideMultipleClassInfoVtbl =
 {
-    AutomationObject_IProvideMultipleClassInfo_QueryInterface,
-    AutomationObject_IProvideMultipleClassInfo_AddRef,
-    AutomationObject_IProvideMultipleClassInfo_Release,
-    AutomationObject_IProvideMultipleClassInfo_GetClassInfo,
-    AutomationObject_IProvideMultipleClassInfo_GetGUID,
-    AutomationObject_GetMultiTypeInfoCount,
-    AutomationObject_GetInfoOfIndex
+    ProvideMultipleClassInfo_QueryInterface,
+    ProvideMultipleClassInfo_AddRef,
+    ProvideMultipleClassInfo_Release,
+    ProvideMultipleClassInfo_GetClassInfo,
+    ProvideMultipleClassInfo_GetGUID,
+    ProvideMultipleClassInfo_GetMultiTypeInfoCount,
+    ProvideMultipleClassInfo_GetInfoOfIndex
 };
 
+static HRESULT init_automation_object(AutomationObject *This, MSIHANDLE msiHandle, REFIID clsid,
+        autoInvokeFunc invokeFunc, autoFreeFunc freeFunc)
+{
+    TRACE("(%p, %d, %s, %p, %p)\n", This, msiHandle, debugstr_guid(clsid), invokeFunc, freeFunc);
+
+    This->IDispatch_iface.lpVtbl = &AutomationObjectVtbl;
+    This->IProvideMultipleClassInfo_iface.lpVtbl = &ProvideMultipleClassInfoVtbl;
+    This->ref = 1;
+
+    This->msiHandle = msiHandle;
+    This->clsid = (LPCLSID)clsid;
+    This->funcInvoke = invokeFunc;
+    This->funcFree   = freeFunc;
+
+    /* Load our TypeInfo so we don't have to process GetIDsOfNames */
+    This->iTypeInfo = NULL;
+    return load_type_info(&This->IDispatch_iface, &This->iTypeInfo, clsid, 0);
+}
+
 /*
  * ListEnumerator methods
  */
@@ -562,7 +492,6 @@ static inline ListEnumerator *impl_from_IEnumVARIANT(IEnumVARIANT* iface)
     return CONTAINING_RECORD(iface, ListEnumerator, IEnumVARIANT_iface);
 }
 
-/*** IUnknown methods ***/
 static HRESULT WINAPI ListEnumerator_QueryInterface(IEnumVARIANT* iface, REFIID riid,
         void** ppvObject)
 {
@@ -575,15 +504,18 @@ static HRESULT WINAPI ListEnumerator_QueryInterface(IEnumVARIANT* iface, REFIID
 
     *ppvObject = 0;
 
-    if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IEnumVARIANT))
-        *ppvObject = This;
+    if (IsEqualGUID(riid, &IID_IUnknown) ||
+        IsEqualGUID(riid, &IID_IEnumVARIANT))
+    {
+        *ppvObject = &This->IEnumVARIANT_iface;
+    }
     else
     {
         TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
         return E_NOINTERFACE;
     }
 
-    IClassFactory_AddRef(iface);
+    IEnumVARIANT_AddRef(iface);
     return S_OK;
 }
 
@@ -605,39 +537,34 @@ static ULONG WINAPI ListEnumerator_Release(IEnumVARIANT* iface)
 
     if (!ref)
     {
-        if (This->pObj) IDispatch_Release((IDispatch *)This->pObj);
+        if (This->list) IDispatch_Release(&This->list->autoobj.IDispatch_iface);
         msi_free(This);
     }
 
     return ref;
 }
 
-/* IEnumVARIANT methods */
-
 static HRESULT WINAPI ListEnumerator_Next(IEnumVARIANT* iface, ULONG celt, VARIANT* rgVar,
-        ULONG* pCeltFetched)
+        ULONG* fetched)
 {
     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
-    ListData *data = private_data(This->pObj);
-    ULONG idx, local;
+    ULONG i, local;
 
-    TRACE("(%p,%uld,%p,%p)\n", iface, celt, rgVar, pCeltFetched);
+    TRACE("(%p, %uld, %p, %p)\n", iface, celt, rgVar, fetched);
 
-    if (pCeltFetched != NULL)
-        *pCeltFetched = 0;
+    if (fetched) *fetched = 0;
 
-    if (rgVar == NULL)
+    if (!rgVar)
         return S_FALSE;
 
     for (local = 0; local < celt; local++)
         VariantInit(&rgVar[local]);
 
-    for (idx = This->ulPos, local = 0; idx < data->ulCount && local < celt; idx++, local++)
-        VariantCopy(&rgVar[local], &data->pVars[idx]);
+    for (i = This->pos, local = 0; i < This->list->count && local < celt; i++, local++)
+        VariantCopy(&rgVar[local], &This->list->data[i]);
 
-    if (pCeltFetched != NULL)
-        *pCeltFetched = local;
-    This->ulPos = idx;
+    if (fetched) *fetched = local;
+    This->pos = i;
 
     return (local < celt) ? S_FALSE : S_OK;
 }
@@ -645,16 +572,16 @@ static HRESULT WINAPI ListEnumerator_Next(IEnumVARIANT* iface, ULONG celt, VARIA
 static HRESULT WINAPI ListEnumerator_Skip(IEnumVARIANT* iface, ULONG celt)
 {
     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
-    ListData *data = private_data(This->pObj);
 
     TRACE("(%p,%uld)\n", iface, celt);
 
-    This->ulPos += celt;
-    if (This->ulPos >= data->ulCount)
+    This->pos += celt;
+    if (This->pos >= This->list->count)
     {
-        This->ulPos = data->ulCount;
+        This->pos = This->list->count;
         return S_FALSE;
     }
+
     return S_OK;
 }
 
@@ -664,7 +591,7 @@ static HRESULT WINAPI ListEnumerator_Reset(IEnumVARIANT* iface)
 
     TRACE("(%p)\n", iface);
 
-    This->ulPos = 0;
+    This->pos = 0;
     return S_OK;
 }
 
@@ -679,7 +606,7 @@ static HRESULT WINAPI ListEnumerator_Clone(IEnumVARIANT* iface, IEnumVARIANT **p
         return S_FALSE;
 
     *ppEnum = NULL;
-    hr = create_list_enumerator(NULL, (LPVOID *)ppEnum, This->pObj, 0);
+    hr = create_list_enumerator(This->list, (LPVOID *)ppEnum);
     if (FAILED(hr))
     {
         if (*ppEnum)
@@ -701,6 +628,28 @@ static const struct IEnumVARIANTVtbl ListEnumerator_Vtbl =
     ListEnumerator_Clone
 };
 
+/* Create a list enumerator, placing the result in the pointer ppObj.  */
+static HRESULT create_list_enumerator(ListObject *list, void **ppObj)
+{
+    ListEnumerator *object;
+
+    TRACE("(%p, %p)\n", list, ppObj);
+
+    object = msi_alloc(sizeof(ListEnumerator));
+
+    /* Set all the VTable references */
+    object->IEnumVARIANT_iface.lpVtbl = &ListEnumerator_Vtbl;
+    object->ref = 1;
+
+    /* Store data that was passed */
+    object->pos = 0;
+    object->list = list;
+    if (list) IDispatch_AddRef(&list->autoobj.IDispatch_iface);
+
+    *ppObj = object;
+    return S_OK;
+}
+
 /*
  * Individual Object Invocation Functions
  */
@@ -935,7 +884,7 @@ static HRESULT RecordImpl_Invoke(
             } else if (wFlags & DISPATCH_PROPERTYPUT) {
                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
                 if (FAILED(hr)) return hr;
-                hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
+                hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
                 if (FAILED(hr)) return hr;
                 if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
                 {
@@ -956,7 +905,7 @@ static HRESULT RecordImpl_Invoke(
             } else if (wFlags & DISPATCH_PROPERTYPUT) {
                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
                 if (FAILED(hr)) return hr;
-                hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
+                hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
                 if (FAILED(hr)) return hr;
                 if ((ret = MsiRecordSetInteger(This->msiHandle, V_I4(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
                 {
@@ -977,6 +926,26 @@ static HRESULT RecordImpl_Invoke(
     return S_OK;
 }
 
+static HRESULT create_record(MSIHANDLE msiHandle, IDispatch **disp)
+{
+    AutomationObject *record;
+    HRESULT hr;
+
+    record = msi_alloc(sizeof(*record));
+    if (!record) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(record, msiHandle, &DIID_Record, RecordImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(record);
+        return hr;
+    }
+
+    *disp = &record->IDispatch_iface;
+
+    return hr;
+}
+
 static HRESULT ListImpl_Invoke(
         AutomationObject* This,
         DISPID dispIdMember,
@@ -988,19 +957,16 @@ static HRESULT ListImpl_Invoke(
         EXCEPINFO* pExcepInfo,
         UINT* puArgErr)
 {
-    ListData *data = private_data(This);
-    HRESULT hr;
-    VARIANTARG varg0;
+    ListObject *list = (ListObject*)This;
     IUnknown *pUnk = NULL;
-
-    VariantInit(&varg0);
+    HRESULT hr;
 
     switch (dispIdMember)
     {
          case DISPID_LIST__NEWENUM:
              if (wFlags & DISPATCH_METHOD) {
                  V_VT(pVarResult) = VT_UNKNOWN;
-                 if (SUCCEEDED(hr = create_list_enumerator(NULL, (LPVOID *)&pUnk, This, 0)))
+                 if (SUCCEEDED(hr = create_list_enumerator(list, (LPVOID *)&pUnk)))
                      V_UNKNOWN(pVarResult) = pUnk;
                  else
                      ERR("Failed to create IEnumVARIANT object, hresult 0x%08x\n", hr);
@@ -1010,11 +976,14 @@ static HRESULT ListImpl_Invoke(
 
          case DISPID_LIST_ITEM:
              if (wFlags & DISPATCH_PROPERTYGET) {
-                hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
+                VARIANTARG index;
+
+                VariantInit(&index);
+                hr = DispGetParam(pDispParams, 0, VT_I4, &index, puArgErr);
                 if (FAILED(hr)) return hr;
-                if (V_I4(&varg0) < 0 || V_I4(&varg0) >= data->ulCount)
+                if (V_I4(&index) < 0 || V_I4(&index) >= list->count)
                     return DISP_E_BADINDEX;
-                VariantCopy(pVarResult, &data->pVars[V_I4(&varg0)]);
+                VariantCopy(pVarResult, &list->data[V_I4(&index)]);
             }
             else return DISP_E_MEMBERNOTFOUND;
             break;
@@ -1022,7 +991,7 @@ static HRESULT ListImpl_Invoke(
          case DISPID_LIST_COUNT:
             if (wFlags & DISPATCH_PROPERTYGET) {
                 V_VT(pVarResult) = VT_I4;
-                V_I4(pVarResult) = data->ulCount;
+                V_I4(pVarResult) = list->count;
             }
             else return DISP_E_MEMBERNOTFOUND;
             break;
@@ -1031,19 +1000,97 @@ static HRESULT ListImpl_Invoke(
             return DISP_E_MEMBERNOTFOUND;
     }
 
-    VariantClear(&varg0);
-
     return S_OK;
 }
 
 static void ListImpl_Free(AutomationObject *This)
 {
-    ListData *data = private_data(This);
-    ULONG idx;
+    ListObject *list = (ListObject*)This;
+    int i;
+
+    for (i = 0; i < list->count; i++)
+        VariantClear(&list->data[i]);
+    msi_free(list->data);
+}
+
+static HRESULT get_products_count(const WCHAR *product, int *len)
+{
+    int i = 0;
+
+    while (1)
+    {
+        WCHAR dataW[GUID_SIZE];
+        UINT ret;
+
+        /* all or related only */
+        if (product)
+            ret = MsiEnumRelatedProductsW(product, 0, i, dataW);
+        else
+            ret = MsiEnumProductsW(i, dataW);
+
+        if (ret == ERROR_NO_MORE_ITEMS) break;
+
+        if (ret != ERROR_SUCCESS)
+            return DISP_E_EXCEPTION;
+
+        i++;
+    }
 
-    for (idx=0; idx<data->ulCount; idx++)
-        VariantClear(&data->pVars[idx]);
-    msi_free(data->pVars);
+    *len = i;
+
+    return S_OK;
+}
+
+static HRESULT create_list(const WCHAR *product, IDispatch **dispatch)
+{
+    ListObject *list;
+    HRESULT hr;
+    int i;
+
+    list = msi_alloc_zero(sizeof(ListObject));
+    if (!list) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(&list->autoobj, 0, &DIID_StringList, ListImpl_Invoke, ListImpl_Free);
+    if (hr != S_OK)
+    {
+        msi_free(list);
+        return hr;
+    }
+
+    *dispatch = &list->autoobj.IDispatch_iface;
+
+    hr = get_products_count(product, &list->count);
+    if (hr != S_OK)
+    {
+        IDispatch_Release(*dispatch);
+        return hr;
+    }
+
+    list->data = msi_alloc(list->count*sizeof(VARIANT));
+    if (!list->data)
+    {
+        IDispatch_Release(*dispatch);
+        return E_OUTOFMEMORY;
+    }
+
+    for (i = 0; i < list->count; i++)
+    {
+        WCHAR dataW[GUID_SIZE];
+        UINT ret;
+
+        /* all or related only */
+        if (product)
+            ret = MsiEnumRelatedProductsW(product, 0, i, dataW);
+        else
+            ret = MsiEnumProductsW(i, dataW);
+
+        if (ret == ERROR_NO_MORE_ITEMS) break;
+
+        V_VT(&list->data[i]) = VT_BSTR;
+        V_BSTR(&list->data[i]) = SysAllocString(dataW);
+    }
+
+    return S_OK;
 }
 
 static HRESULT ViewImpl_Invoke(
@@ -1058,7 +1105,6 @@ static HRESULT ViewImpl_Invoke(
         UINT* puArgErr)
 {
     MSIHANDLE msiHandle;
-    IDispatch *pDispatch = NULL;
     UINT ret;
     VARIANTARG varg0, varg1;
     HRESULT hr;
@@ -1086,8 +1132,10 @@ static HRESULT ViewImpl_Invoke(
                 V_VT(pVarResult) = VT_DISPATCH;
                 if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS)
                 {
-                    if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
-                        V_DISPATCH(pVarResult) = pDispatch;
+                    IDispatch *dispatch = NULL;
+
+                    if (SUCCEEDED(hr = create_record(msiHandle, &dispatch)))
+                        V_DISPATCH(pVarResult) = dispatch;
                     else
                         ERR("Failed to create Record object, hresult 0x%08x\n", hr);
                 }
@@ -1164,8 +1212,8 @@ static HRESULT DatabaseImpl_Invoke(
         EXCEPINFO* pExcepInfo,
         UINT* puArgErr)
 {
+    IDispatch *dispatch = NULL;
     MSIHANDLE msiHandle;
-    IDispatch *pDispatch = NULL;
     UINT ret;
     VARIANTARG varg0, varg1;
     HRESULT hr;
@@ -1185,9 +1233,9 @@ static HRESULT DatabaseImpl_Invoke(
                 V_VT(pVarResult) = VT_DISPATCH;
                 if ((ret = MsiGetSummaryInformationW(This->msiHandle, NULL, V_I4(&varg0), &msiHandle)) == ERROR_SUCCESS)
                 {
-                    hr = create_automation_object(msiHandle, NULL, (LPVOID *)&pDispatch, &DIID_SummaryInfo, SummaryInfoImpl_Invoke, NULL, 0);
+                    hr = create_summaryinfo(msiHandle, &dispatch);
                     if (SUCCEEDED(hr))
-                        V_DISPATCH(pVarResult) = pDispatch;
+                        V_DISPATCH(pVarResult) = dispatch;
                     else
                         ERR("Failed to create SummaryInfo object: 0x%08x\n", hr);
                 }
@@ -1208,8 +1256,8 @@ static HRESULT DatabaseImpl_Invoke(
                 V_VT(pVarResult) = VT_DISPATCH;
                 if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS)
                 {
-                    if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_View, ViewImpl_Invoke, NULL, 0)))
-                        V_DISPATCH(pVarResult) = pDispatch;
+                    if (SUCCEEDED(hr = create_view(msiHandle, &dispatch)))
+                        V_DISPATCH(pVarResult) = dispatch;
                     else
                         ERR("Failed to create View object, hresult 0x%08x\n", hr);
                 }
@@ -1249,10 +1297,9 @@ static HRESULT SessionImpl_Invoke(
         EXCEPINFO* pExcepInfo,
         UINT* puArgErr)
 {
-    SessionData *data = private_data(This);
+    SessionObject *session = (SessionObject*)This;
     WCHAR *szString;
     DWORD dwLen;
-    IDispatch *pDispatch = NULL;
     MSIHANDLE msiHandle;
     LANGID langId;
     UINT ret;
@@ -1268,8 +1315,8 @@ static HRESULT SessionImpl_Invoke(
         case DISPID_SESSION_INSTALLER:
             if (wFlags & DISPATCH_PROPERTYGET) {
                 V_VT(pVarResult) = VT_DISPATCH;
-                IDispatch_AddRef(data->pInstaller);
-                V_DISPATCH(pVarResult) = data->pInstaller;
+                IDispatch_AddRef(session->installer);
+                V_DISPATCH(pVarResult) = session->installer;
             }
             else return DISP_E_MEMBERNOTFOUND;
             break;
@@ -1293,7 +1340,7 @@ static HRESULT SessionImpl_Invoke(
             } else if (wFlags & DISPATCH_PROPERTYPUT) {
                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
                 if (FAILED(hr)) return hr;
-                hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
+                hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
                 if (FAILED(hr)) {
                     VariantClear(&varg0);
                     return hr;
@@ -1327,7 +1374,7 @@ static HRESULT SessionImpl_Invoke(
             } else if (wFlags & DISPATCH_PROPERTYPUT) {
                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
                 if (FAILED(hr)) return hr;
-                hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BOOL, &varg1, puArgErr);
+                hr = DispGetParam(pDispParams, 1, VT_BOOL, &varg1, puArgErr);
                 if (FAILED(hr)) return hr;
                 if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS)
                 {
@@ -1343,8 +1390,10 @@ static HRESULT SessionImpl_Invoke(
                 V_VT(pVarResult) = VT_DISPATCH;
                 if ((msiHandle = MsiGetActiveDatabase(This->msiHandle)))
                 {
-                    if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Database, DatabaseImpl_Invoke, NULL, 0)))
-                        V_DISPATCH(pVarResult) = pDispatch;
+                    IDispatch *dispatch;
+
+                    if (SUCCEEDED(hr = create_database(msiHandle, &dispatch)))
+                        V_DISPATCH(pVarResult) = dispatch;
                     else
                         ERR("Failed to create Database object, hresult 0x%08x\n", hr);
                 }
@@ -1466,7 +1515,7 @@ static HRESULT SessionImpl_Invoke(
             } else if (wFlags & DISPATCH_PROPERTYPUT) {
                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
                 if (FAILED(hr)) return hr;
-                hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
+                hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
                 if (FAILED(hr)) {
                     VariantClear(&varg0);
                     return hr;
@@ -1580,8 +1629,7 @@ static HRESULT InstallerImpl_CreateRecord(WORD wFlags,
     if (!hrec)
         return DISP_E_EXCEPTION;
 
-    hr = create_automation_object(hrec, NULL, (LPVOID*)&dispatch,
-                                  &DIID_Record, RecordImpl_Invoke, NULL, 0);
+    hr = create_record(hrec, &dispatch);
     if (SUCCEEDED(hr))
         V_DISPATCH(pVarResult) = dispatch;
 
@@ -1637,7 +1685,7 @@ static HRESULT InstallerImpl_OpenPackage(AutomationObject* This,
         goto done;
     }
 
-    hr = create_session(hpkg, (IDispatch *)This, &dispatch);
+    hr = create_session(hpkg, &This->IDispatch_iface, &dispatch);
     if (SUCCEEDED(hr))
         V_DISPATCH(pVarResult) = dispatch;
 
@@ -1706,8 +1754,7 @@ static HRESULT InstallerImpl_OpenDatabase(WORD wFlags,
         goto done;
     }
 
-    hr = create_automation_object(hdb, NULL, (LPVOID *)&dispatch,
-                                  &DIID_Database, DatabaseImpl_Invoke, NULL, 0);
+    hr = create_database(hdb, &dispatch);
     if (SUCCEEDED(hr))
         V_DISPATCH(pVarResult) = dispatch;
 
@@ -2155,157 +2202,52 @@ done:
     return hr;
 }
 
-static void cleanup_products(IDispatch* dispatch, ULONG count)
-{
-    UINT i;
-    ListData* ldata = private_data((AutomationObject *)dispatch);
-
-    for (i = 0; i < count - 1; i++)
-        VariantClear(&ldata->pVars[i]);
-
-    ldata->ulCount = 0;
-    msi_free(ldata->pVars);
-
-    IDispatch_Release(dispatch);
-}
-
-static HRESULT InstallerImpl_Products(WORD wFlags,
+static HRESULT InstallerImpl_Products(WORD flags,
                                       DISPPARAMS* pDispParams,
-                                      VARIANT* pVarResult,
+                                      VARIANT* result,
                                       EXCEPINFO* pExcepInfo,
                                       UINT* puArgErr)
 {
-    UINT ret;
-    HRESULT hr;
-    ULONG idx = 0;
-    ListData *ldata;
     IDispatch *dispatch;
-    WCHAR product[GUID_SIZE];
+    HRESULT hr;
 
-    if (!(wFlags & DISPATCH_PROPERTYGET))
+    if (!(flags & DISPATCH_PROPERTYGET))
         return DISP_E_MEMBERNOTFOUND;
 
-    /* Find number of products. */
-    while ((ret = MsiEnumProductsW(idx, product)) == ERROR_SUCCESS)
-        idx++;
-
-    if (ret != ERROR_NO_MORE_ITEMS)
-        return DISP_E_EXCEPTION;
-
-    V_VT(pVarResult) = VT_DISPATCH;
-    hr = create_automation_object(0, NULL, (LPVOID*)&dispatch,
-                                  &DIID_StringList, ListImpl_Invoke,
-                                  ListImpl_Free, sizeof(ListData));
+    hr = create_list(NULL, &dispatch);
     if (FAILED(hr))
         return hr;
 
-    V_DISPATCH(pVarResult) = dispatch;
+    V_VT(result) = VT_DISPATCH;
+    V_DISPATCH(result) = dispatch;
 
-    /* Save product strings. */
-    ldata = private_data((AutomationObject *)dispatch);
-    ldata->ulCount = 0;
-    ldata->pVars = msi_alloc_zero(sizeof(VARIANT) * idx);
-    if (!ldata->pVars)
-    {
-        IDispatch_Release(dispatch);
-        return E_OUTOFMEMORY;
-    }
-
-    ldata->ulCount = idx;
-    for (idx = 0; idx < ldata->ulCount; idx++)
-    {
-        ret = MsiEnumProductsW(idx, product);
-        if (ret != ERROR_SUCCESS)
-        {
-            cleanup_products(dispatch, idx - 1);
-            return DISP_E_EXCEPTION;
-        }
-
-        VariantInit(&ldata->pVars[idx]);
-        V_VT(&ldata->pVars[idx]) = VT_BSTR;
-        V_BSTR(&ldata->pVars[idx]) = SysAllocString(product);
-    }
-
-    return S_OK;
+    return hr;
 }
 
-static HRESULT InstallerImpl_RelatedProducts(WORD wFlags,
+static HRESULT InstallerImpl_RelatedProducts(WORD flags,
                                              DISPPARAMS* pDispParams,
-                                             VARIANT* pVarResult,
+                                             VARIANT* result,
                                              EXCEPINFO* pExcepInfo,
                                              UINT* puArgErr)
 {
-    UINT ret;
-    ULONG idx;
-    HRESULT hr;
-    ListData *ldata;
-    VARIANTARG varg0;
     IDispatch* dispatch;
-    WCHAR product[GUID_SIZE];
+    VARIANTARG related;
+    HRESULT hr;
 
-    if (!(wFlags & DISPATCH_PROPERTYGET))
+    if (!(flags & DISPATCH_PROPERTYGET))
         return DISP_E_MEMBERNOTFOUND;
 
-    VariantInit(&varg0);
-    hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
+    VariantInit(&related);
+    hr = DispGetParam(pDispParams, 0, VT_BSTR, &related, puArgErr);
     if (FAILED(hr))
         return hr;
 
-    /* Find number of related products. */
-    idx = 0;
-    do
-    {
-        ret = MsiEnumRelatedProductsW(V_BSTR(&varg0), 0, idx, product);
-        if (ret == ERROR_SUCCESS)
-            idx++;
-    } while (ret == ERROR_SUCCESS);
-
-    if (ret != ERROR_NO_MORE_ITEMS)
-    {
-        hr = DISP_E_EXCEPTION;
-        goto done;
-    }
-
-    V_VT(pVarResult) = VT_DISPATCH;
-
-    hr = create_automation_object(0, NULL, (LPVOID*)&dispatch,
-                                  &DIID_StringList, ListImpl_Invoke,
-                                  ListImpl_Free, sizeof(ListData));
-    if (FAILED(hr))
-        goto done;
-
-    V_DISPATCH(pVarResult) = dispatch;
-
-    /* Save product strings. */
-    ldata = private_data((AutomationObject *)dispatch);
-    ldata->pVars = msi_alloc(sizeof(VARIANT) * idx);
-    if (!ldata->pVars)
-    {
-        IDispatch_Release(dispatch);
-        hr = E_OUTOFMEMORY;
-        goto done;
-    }
-
-    ldata->ulCount = idx;
-    for (idx = 0; idx < ldata->ulCount; idx++)
-    {
-        ret = MsiEnumRelatedProductsW(V_BSTR(&varg0), 0, idx, product);
-        if (ret != ERROR_SUCCESS)
-        {
-            cleanup_products(dispatch, idx - 1);
-            hr = DISP_E_EXCEPTION;
-            goto done;
-        }
+    hr = create_list(V_BSTR(&related), &dispatch);
+    VariantClear(&related);
 
-        VariantInit(&ldata->pVars[idx]);
-        V_VT(&ldata->pVars[idx]) = VT_BSTR;
-        V_BSTR(&ldata->pVars[idx]) = SysAllocString(product);
-    }
-
-    hr = S_OK;
+    V_VT(result) = VT_DISPATCH;
+    V_DISPATCH(result) = dispatch;
 
-done:
-    VariantClear(&varg0);
     return hr;
 }
 
@@ -2409,17 +2351,112 @@ static HRESULT InstallerImpl_Invoke(
     }
 }
 
-/* Wrapper around create_automation_object to create an installer object. */
-HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj)
+HRESULT create_msiserver(IUnknown *outer, void **ppObj)
 {
-    return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke, NULL, 0);
+    AutomationObject *installer;
+    HRESULT hr;
+
+    TRACE("(%p %p)\n", outer, ppObj);
+
+    if (outer)
+        return CLASS_E_NOAGGREGATION;
+
+    installer = msi_alloc(sizeof(AutomationObject));
+    if (!installer) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(installer, 0, &DIID_Installer, InstallerImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(installer);
+        return hr;
+    }
+
+    *ppObj = &installer->IDispatch_iface;
+
+    return hr;
 }
 
-/* Wrapper around create_automation_object to create a session object. */
-HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch)
+HRESULT create_session(MSIHANDLE msiHandle, IDispatch *installer, IDispatch **disp)
 {
-    HRESULT hr = create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke, NULL, sizeof(SessionData));
-    if (SUCCEEDED(hr) && pDispatch && *pDispatch)
-        ((SessionData *)private_data((AutomationObject *)*pDispatch))->pInstaller = pInstaller;
+    SessionObject *session;
+    HRESULT hr;
+
+    session = msi_alloc(sizeof(SessionObject));
+    if (!session) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(&session->autoobj, msiHandle, &DIID_Session, SessionImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(session);
+        return hr;
+    }
+
+    session->installer = installer;
+    *disp = &session->autoobj.IDispatch_iface;
+
+    return hr;
+}
+
+static HRESULT create_database(MSIHANDLE msiHandle, IDispatch **dispatch)
+{
+    AutomationObject *database;
+    HRESULT hr;
+
+    TRACE("(%d %p)\n", msiHandle, dispatch);
+
+    database = msi_alloc(sizeof(AutomationObject));
+    if (!database) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(database, msiHandle, &DIID_Database, DatabaseImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(database);
+        return hr;
+    }
+
+    *dispatch = &database->IDispatch_iface;
+
+    return hr;
+}
+
+static HRESULT create_view(MSIHANDLE msiHandle, IDispatch **dispatch)
+{
+    AutomationObject *view;
+    HRESULT hr;
+
+    TRACE("(%d %p)\n", msiHandle, dispatch);
+
+    view = msi_alloc(sizeof(AutomationObject));
+    if (!view) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(view, msiHandle, &DIID_View, ViewImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(view);
+        return hr;
+    }
+
+    *dispatch = &view->IDispatch_iface;
+
+    return hr;
+}
+
+static HRESULT create_summaryinfo(MSIHANDLE msiHandle, IDispatch **disp)
+{
+    AutomationObject *info;
+    HRESULT hr;
+
+    info = msi_alloc(sizeof(*info));
+    if (!info) return E_OUTOFMEMORY;
+
+    hr = init_automation_object(info, msiHandle, &DIID_SummaryInfo, SummaryInfoImpl_Invoke, NULL);
+    if (hr != S_OK)
+    {
+        msi_free(info);
+        return hr;
+    }
+
+    *disp = &info->IDispatch_iface;
+
     return hr;
 }
index 7543767..9e50035 100644 (file)
@@ -903,11 +903,11 @@ static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
     if( row )
     {
         LPCWSTR error = MSI_RecordGetString( row, 1 );
-        if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
+        if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
             MessageBoxW( NULL, error, NULL, MB_OK );
         msiobj_release( &row->hdr );
     }
-    else if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
+    else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
         MessageBoxW( NULL, deformated, NULL, MB_OK );
 
     msi_free( deformated );
index 4e908ff..a90d379 100644 (file)
@@ -3978,8 +3978,7 @@ UINT msi_spawn_error_dialog( MSIPACKAGE *package, LPWSTR error_dialog, LPWSTR er
         'M','S','I','E','r','r','o','r','D','i','a','l','o','g','R','e','s','u','l','t',0
     };
 
-    if ( (msi_get_property_int( package->db, szUILevel, 0 ) & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE )
-        return ERROR_SUCCESS;
+    if ((package->ui_level & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE) return ERROR_SUCCESS;
 
     if ( !error_dialog )
     {
index 909f71f..fe72c24 100644 (file)
@@ -215,7 +215,7 @@ static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source)
             MoveFileExW(tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT))
         {
             file->state = msifs_installed;
-            package->need_reboot = 1;
+            package->need_reboot_at_end = 1;
             gle = ERROR_SUCCESS;
         }
         else
@@ -247,6 +247,17 @@ static UINT msi_create_directory( MSIPACKAGE *package, const WCHAR *dir )
     return ERROR_SUCCESS;
 }
 
+static MSIFILE *find_file( MSIPACKAGE *package, const WCHAR *filename )
+{
+    MSIFILE *file;
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if (file->state != msifs_installed && !strcmpiW( filename, file->File )) return file;
+    }
+    return NULL;
+}
+
 static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
                             LPWSTR *path, DWORD *attrs, PVOID user)
 {
@@ -255,8 +266,7 @@ static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
 
     if (action == MSICABEXTRACT_BEGINEXTRACT)
     {
-        f = msi_get_loaded_file(package, file);
-        if (!f)
+        if (!(f = find_file( package, file )))
         {
             TRACE("unknown file in cabinet (%s)\n", debugstr_w(file));
             return FALSE;
@@ -1298,22 +1308,26 @@ UINT ACTION_RemoveFiles( MSIPACKAGE *package )
         msi_ui_actiondata( package, szRemoveFiles, uirow );
         msiobj_release( &uirow->hdr );
     }
+
+    msi_init_assembly_caches( package );
     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
     {
-        MSIFOLDER *folder;
-
         comp->Action = msi_get_component_action( package, comp );
         if (comp->Action != INSTALLSTATE_ABSENT) continue;
 
-        if (comp->assembly && !comp->assembly->application) continue;
-
         if (comp->Attributes & msidbComponentAttributesPermanent)
         {
             TRACE("permanent component, not removing directory\n");
             continue;
         }
-        folder = msi_get_loaded_folder( package, comp->Directory );
-        remove_folder( folder );
+        if (comp->assembly && !comp->assembly->application)
+            msi_uninstall_assembly( package, comp );
+        else
+        {
+            MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory );
+            remove_folder( folder );
+        }
     }
+    msi_destroy_assembly_caches( package );
     return ERROR_SUCCESS;
 }
index 86e3979..a6c832a 100644 (file)
@@ -347,16 +347,10 @@ UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder,
     return MSI_GetTargetPath( hInstall, szFolder, &path, pcchPathBuf );
 }
 
-static WCHAR *get_source_root( MSIDATABASE *db )
+static WCHAR *get_source_root( MSIPACKAGE *package )
 {
-    WCHAR *path, *p;
-
-    if ((path = msi_dup_property( db, szSourceDir ))) return path;
-    if ((path = msi_dup_property( db, szDatabase )))
-    {
-        if ((p = strrchrW( path, '\\' ))) p[1] = 0;
-    }
-    return path;
+    msi_set_sourcedir_props( package, FALSE );
+    return msi_dup_property( package->db, szSourceDir );
 }
 
 WCHAR *msi_resolve_source_folder( MSIPACKAGE *package, const WCHAR *name, MSIFOLDER **folder )
@@ -372,7 +366,7 @@ WCHAR *msi_resolve_source_folder( MSIPACKAGE *package, const WCHAR *name, MSIFOL
     /* special resolving for root dir */
     if (!strcmpW( name, szTargetDir ) && !f->ResolvedSource)
     {
-        f->ResolvedSource = get_source_root( package->db );
+        f->ResolvedSource = get_source_root( package );
     }
     if (folder) *folder = f;
     if (f->ResolvedSource)
@@ -388,7 +382,7 @@ WCHAR *msi_resolve_source_folder( MSIPACKAGE *package, const WCHAR *name, MSIFOL
     p = msi_resolve_source_folder( package, parent, NULL );
 
     if (package->WordCount & msidbSumInfoSourceTypeCompressed)
-        path = get_source_root( package->db );
+        path = get_source_root( package );
     else if (package->WordCount & msidbSumInfoSourceTypeSFN)
         path = msi_build_directory_name( 3, p, f->SourceShortPath, NULL );
     else
@@ -559,8 +553,7 @@ static void set_target_path( MSIPACKAGE *package, MSIFOLDER *folder, const WCHAR
     MSIFOLDER *child;
     WCHAR *target_path;
 
-    if (!(target_path = strdupW( path ))) return;
-    msi_clean_path( target_path );
+    if (!(target_path = msi_normalize_path( path ))) return;
     if (strcmpW( target_path, folder->ResolvedTarget ))
     {
         msi_free( folder->ResolvedTarget );
@@ -578,7 +571,7 @@ static void set_target_path( MSIPACKAGE *package, MSIFOLDER *folder, const WCHAR
 
 UINT MSI_SetTargetPathW( MSIPACKAGE *package, LPCWSTR szFolder, LPCWSTR szFolderPath )
 {
-    DWORD attrib, len;
+    DWORD attrib;
     MSIFOLDER *folder;
     MSIFILE *file;
 
@@ -593,17 +586,7 @@ UINT MSI_SetTargetPathW( MSIPACKAGE *package, LPCWSTR szFolder, LPCWSTR szFolder
     }
     if (!(folder = msi_get_loaded_folder( package, szFolder ))) return ERROR_DIRECTORY;
 
-    len = strlenW( szFolderPath );
-    if (len && szFolderPath[len - 1] != '\\')
-    {
-        WCHAR *path = msi_alloc( (len + 2) * sizeof(WCHAR) );
-        memcpy( path, szFolderPath, len * sizeof(WCHAR) );
-        path[len] = '\\';
-        path[len + 1] = 0;
-        set_target_path( package, folder, path );
-        msi_free( path );
-    }
-    else set_target_path( package, folder, szFolderPath );
+    set_target_path( package, folder, szFolderPath );
 
     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
     {
@@ -778,7 +761,11 @@ BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode)
         break;
 
     case MSIRUNMODE_REBOOTATEND:
-        r = package->need_reboot;
+        r = package->need_reboot_at_end;
+        break;
+
+    case MSIRUNMODE_REBOOTNOW:
+        r = package->need_reboot_now;
         break;
 
     case MSIRUNMODE_LOGENABLED:
@@ -831,13 +818,13 @@ UINT WINAPI MsiSetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode, BOOL fState)
     switch (iRunMode)
     {
     case MSIRUNMODE_REBOOTATEND:
-        package->need_reboot = 1;
+        package->need_reboot_at_end = (fState != 0);
         r = ERROR_SUCCESS;
         break;
 
     case MSIRUNMODE_REBOOTNOW:
-        FIXME("unimplemented run mode: %d\n", iRunMode);
-        r = ERROR_FUNCTION_FAILED;
+        package->need_reboot_now = (fState != 0);
+        r = ERROR_SUCCESS;
         break;
 
     default:
index 69bcb91..612624d 100644 (file)
@@ -77,9 +77,8 @@ static UINT msi_change_media(MSIPACKAGE *package, MSIMEDIAINFO *mi)
 
     static const WCHAR error_prop[] = {'E','r','r','o','r','D','i','a','l','o','g',0};
 
-    if ((msi_get_property_int(package->db, szUILevel, 0) & INSTALLUILEVEL_MASK) ==
-         INSTALLUILEVEL_NONE && !gUIHandlerA && !gUIHandlerW && !gUIHandlerRecord)
-        return ERROR_SUCCESS;
+    if ((package->ui_level & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE &&
+        !gUIHandlerA && !gUIHandlerW && !gUIHandlerRecord) return ERROR_SUCCESS;
 
     error = msi_build_error_string(package, 1302, 1, mi->disk_prompt);
     error_dialog = msi_dup_property(package->db, error_prop);
@@ -428,7 +427,7 @@ static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint,
         goto done;
     }
 
-    TRACE("extracting %s\n", debugstr_w(path));
+    TRACE("extracting %s -> %s\n", debugstr_w(data->curfile), debugstr_w(path));
 
     attrs = attrs & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
     if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL;
@@ -478,7 +477,7 @@ static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint,
                 MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
                 MoveFileExW(tmpfileW, path, MOVEFILE_DELAY_UNTIL_REBOOT))
             {
-                data->package->need_reboot = 1;
+                data->package->need_reboot_at_end = 1;
             }
             else
             {
index fd2315f..973bebd 100644 (file)
@@ -42,6 +42,9 @@
 #include "wintrust.h"
 #include "softpub.h"
 
+#include "initguid.h"
+#include "msxml2.h"
+
 #include "wine/debug.h"
 #include "wine/unicode.h"
 
@@ -304,7 +307,6 @@ done:
     return r;
 }
 
-
 static UINT get_patch_product_codes( LPCWSTR szPatchPackage, WCHAR ***product_codes )
 {
     MSIHANDLE patch, info = 0;
@@ -580,17 +582,66 @@ static UINT MSI_ApplicablePatchW( MSIPACKAGE *package, LPCWSTR patch )
     return r;
 }
 
+/* IXMLDOMDocument should be set to XPath mode already */
+static UINT MSI_ApplicablePatchXML( MSIPACKAGE *package, IXMLDOMDocument *desc )
+{
+    static const WCHAR queryW[] = {'M','s','i','P','a','t','c','h','/',
+                                   'T','a','r','g','e','t','P','r','o','d','u','c','t','/',
+                                   'T','a','r','g','e','t','P','r','o','d','u','c','t','C','o','d','e',0};
+    UINT r = ERROR_FUNCTION_FAILED;
+    IXMLDOMNodeList *list;
+    LPWSTR product_code;
+    IXMLDOMNode *node;
+    HRESULT hr;
+    BSTR s;
+
+    product_code = msi_dup_property( package->db, szProductCode );
+    if (!product_code)
+    {
+        /* FIXME: the property ProductCode should be written into the DB somewhere */
+        ERR("no product code to check\n");
+        return ERROR_SUCCESS;
+    }
+
+    s = SysAllocString(queryW);
+    hr = IXMLDOMDocument_selectNodes( desc, s, &list );
+    SysFreeString(s);
+    if (hr != S_OK)
+        return ERROR_INVALID_PATCH_XML;
+
+    while (IXMLDOMNodeList_nextNode( list, &node ) == S_OK && r != ERROR_SUCCESS)
+    {
+        hr = IXMLDOMNode_get_text( node, &s );
+        IXMLDOMNode_Release( node );
+        if (hr == S_OK)
+        {
+            if (!strcmpW( s, product_code )) r = ERROR_SUCCESS;
+            SysFreeString( s );
+        }
+    }
+    IXMLDOMNodeList_Release( list );
+
+    if (r != ERROR_SUCCESS)
+        TRACE("patch not applicable\n");
+
+    msi_free( product_code );
+    return r;
+}
+
 static UINT determine_patch_sequence( MSIPACKAGE *package, DWORD count, MSIPATCHSEQUENCEINFOW *info )
 {
+    IXMLDOMDocument *desc = NULL;
     DWORD i;
 
+    if (count > 1)
+        FIXME("patch ordering not supported\n");
+
     for (i = 0; i < count; i++)
     {
         switch (info[i].ePatchDataType)
         {
         case MSIPATCH_DATATYPE_PATCHFILE:
         {
-            FIXME("patch ordering not supported\n");
             if (MSI_ApplicablePatchW( package, info[i].szPatchData ) != ERROR_SUCCESS)
             {
                 info[i].dwOrder = ~0u;
@@ -603,19 +654,72 @@ static UINT determine_patch_sequence( MSIPACKAGE *package, DWORD count, MSIPATCH
             }
             break;
         }
+        case MSIPATCH_DATATYPE_XMLPATH:
+        case MSIPATCH_DATATYPE_XMLBLOB:
+        {
+            VARIANT_BOOL b;
+            HRESULT hr;
+            BSTR s;
+
+            if (!desc)
+            {
+                hr = CoCreateInstance( &CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IXMLDOMDocument, (void**)&desc );
+                if (hr != S_OK)
+                {
+                    ERR("failed to create DOMDocument30 instance, 0x%08x\n", hr);
+                    return ERROR_FUNCTION_FAILED;
+                }
+            }
+
+            s = SysAllocString( info[i].szPatchData );
+            if (info[i].ePatchDataType == MSIPATCH_DATATYPE_XMLPATH)
+            {
+                VARIANT src;
+
+                V_VT(&src) = VT_BSTR;
+                V_BSTR(&src) = s;
+                hr = IXMLDOMDocument_load( desc, src, &b );
+            }
+            else
+                hr = IXMLDOMDocument_loadXML( desc, s, &b );
+            SysFreeString( s );
+            if ( hr != S_OK )
+            {
+                ERR("failed to parse patch description\n");
+                IXMLDOMDocument_Release( desc );
+                break;
+            }
+
+            if (MSI_ApplicablePatchXML( package, desc ) != ERROR_SUCCESS)
+            {
+                info[i].dwOrder = ~0u;
+                info[i].uStatus = ERROR_PATCH_TARGET_NOT_FOUND;
+            }
+            else
+            {
+                info[i].dwOrder = i;
+                info[i].uStatus = ERROR_SUCCESS;
+            }
+            break;
+        }
         default:
         {
-            FIXME("patch data type %u not supported\n", info[i].ePatchDataType);
+            FIXME("unknown patch data type %u\n", info[i].ePatchDataType);
             info[i].dwOrder = i;
             info[i].uStatus = ERROR_SUCCESS;
             break;
         }
         }
+
         TRACE("szPatchData: %s\n", debugstr_w(info[i].szPatchData));
         TRACE("ePatchDataType: %u\n", info[i].ePatchDataType);
         TRACE("dwOrder: %u\n", info[i].dwOrder);
         TRACE("uStatus: %u\n", info[i].uStatus);
     }
+
+    if (desc) IXMLDOMDocument_Release( desc );
+
     return ERROR_SUCCESS;
 }
 
@@ -2008,7 +2112,7 @@ UINT WINAPI MsiQueryComponentStateA(LPCSTR szProductCode,
 static BOOL msi_comp_find_prod_key(LPCWSTR prodcode, MSIINSTALLCONTEXT context)
 {
     UINT r;
-    HKEY hkey;
+    HKEY hkey = NULL;
 
     r = MSIREG_OpenProductKey(prodcode, NULL, context, &hkey, FALSE);
     RegCloseKey(hkey);
@@ -2044,7 +2148,7 @@ static BOOL msi_comp_find_package(LPCWSTR prodcode, MSIINSTALLCONTEXT context)
     return (res == ERROR_SUCCESS);
 }
 
-static BOOL msi_comp_find_prodcode(LPWSTR squished_pc,
+static UINT msi_comp_find_prodcode(LPWSTR squished_pc,
                                    MSIINSTALLCONTEXT context,
                                    LPCWSTR comp, LPWSTR val, DWORD *sz)
 {
@@ -2058,14 +2162,14 @@ static BOOL msi_comp_find_prodcode(LPWSTR squished_pc,
         r = MSIREG_OpenUserDataComponentKey(comp, NULL, &hkey, FALSE);
 
     if (r != ERROR_SUCCESS)
-        return FALSE;
+        return r;
 
     res = RegQueryValueExW(hkey, squished_pc, NULL, NULL, (BYTE *)val, sz);
     if (res != ERROR_SUCCESS)
-        return FALSE;
+        return res;
 
     RegCloseKey(hkey);
-    return TRUE;
+    return res;
 }
 
 UINT WINAPI MsiQueryComponentStateW(LPCWSTR szProductCode,
@@ -2073,7 +2177,6 @@ UINT WINAPI MsiQueryComponentStateW(LPCWSTR szProductCode,
                                     LPCWSTR szComponent, INSTALLSTATE *pdwState)
 {
     WCHAR squished_pc[GUID_SIZE];
-    WCHAR val[MAX_PATH];
     BOOL found;
     DWORD sz;
 
@@ -2104,21 +2207,29 @@ UINT WINAPI MsiQueryComponentStateW(LPCWSTR szProductCode,
 
     *pdwState = INSTALLSTATE_UNKNOWN;
 
-    sz = MAX_PATH;
-    if (!msi_comp_find_prodcode(squished_pc, dwContext, szComponent, val, &sz))
+    sz = 0;
+    if (msi_comp_find_prodcode(squished_pc, dwContext, szComponent, NULL, &sz))
         return ERROR_UNKNOWN_COMPONENT;
 
     if (sz == 0)
         *pdwState = INSTALLSTATE_NOTUSED;
     else
     {
+        WCHAR *val;
+        UINT r;
+
+        if (!(val = msi_alloc( sz ))) return ERROR_OUTOFMEMORY;
+        if ((r = msi_comp_find_prodcode(squished_pc, dwContext, szComponent, val, &sz)))
+            return r;
+
         if (lstrlenW(val) > 2 &&
-            val[0] >= '0' && val[0] <= '9' && val[1] >= '0' && val[1] <= '9')
+            val[0] >= '0' && val[0] <= '9' && val[1] >= '0' && val[1] <= '9' && val[2] != ':')
         {
             *pdwState = INSTALLSTATE_SOURCE;
         }
         else
             *pdwState = INSTALLSTATE_LOCAL;
+        msi_free( val );
     }
 
     TRACE("-> %d\n", *pdwState);
@@ -2476,6 +2587,7 @@ HRESULT WINAPI MsiGetFileSignatureInformationW( LPCWSTR path, DWORD flags, PCCER
     data.dwProvFlags         = 0;
     data.dwUIContext         = WTD_UICONTEXT_INSTALL;
     hr = WinVerifyTrustEx( INVALID_HANDLE_VALUE, &generic_verify_v2, &data );
+    *cert = NULL;
     if (FAILED(hr)) goto done;
 
     if (!(signer = WTHelperGetProvSignerFromChain( data.hWVTStateData, 0, FALSE, 0 )))
@@ -3021,11 +3133,7 @@ static UINT get_file_version( const WCHAR *path, WCHAR *verbuf, DWORD *verlen,
                               WCHAR *langbuf, DWORD *langlen )
 {
     static const WCHAR szVersionResource[] = {'\\',0};
-    static const WCHAR szVersionFormat[] = {
-        '%','d','.','%','d','.','%','d','.','%','d',0};
-    static const WCHAR szLangResource[] = {
-        '\\','V','a','r','F','i','l','e','I','n','f','o','\\',
-        'T','r','a','n','s','l','a','t','i','o','n',0};
+    static const WCHAR szVersionFormat[] = {'%','d','.','%','d','.','%','d','.','%','d',0};
     static const WCHAR szLangFormat[] = {'%','d',0};
     UINT ret = ERROR_SUCCESS;
     DWORD len, error;
@@ -3100,18 +3208,18 @@ UINT WINAPI MsiGetFileVersionW( LPCWSTR path, LPWSTR verbuf, LPDWORD verlen,
         return ERROR_INVALID_PARAMETER;
 
     ret = get_file_version( path, verbuf, verlen, langbuf, langlen );
-    if (ret == ERROR_RESOURCE_DATA_NOT_FOUND)
+    if (ret == ERROR_RESOURCE_DATA_NOT_FOUND && verlen)
     {
         int len;
         WCHAR *version = msi_font_version_from_file( path );
         if (!version) return ERROR_FILE_INVALID;
         len = strlenW( version );
-        if (*verlen > len)
+        if (len >= *verlen) ret = ERROR_MORE_DATA;
+        else if (verbuf)
         {
             strcpyW( verbuf, version );
             ret = ERROR_SUCCESS;
         }
-        else ret = ERROR_MORE_DATA;
         *verlen = len;
         msi_free( version );
     }
@@ -3909,24 +4017,34 @@ UINT WINAPI MsiGetFileHashW( LPCWSTR szFilePath, DWORD dwOptions,
     }
     length = GetFileSize( handle, NULL );
 
-    mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, 0, NULL );
-    if (mapping)
+    if (length)
     {
-        p = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, length );
-        if (p)
+        mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, 0, NULL );
+        if (mapping)
         {
-            MD5_CTX ctx;
+            p = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, length );
+            if (p)
+            {
+                MD5_CTX ctx;
 
-            MD5Init( &ctx );
-            MD5Update( &ctx, p, length );
-            MD5Final( &ctx );
-            UnmapViewOfFile( p );
+                MD5Init( &ctx );
+                MD5Update( &ctx, p, length );
+                MD5Final( &ctx );
+                UnmapViewOfFile( p );
 
-            memcpy( pHash->dwData, ctx.digest, sizeof pHash->dwData );
-            r = ERROR_SUCCESS;
+                memcpy( pHash->dwData, ctx.digest, sizeof pHash->dwData );
+                r = ERROR_SUCCESS;
+            }
+            CloseHandle( mapping );
         }
-        CloseHandle( mapping );
     }
+    else
+    {
+        /* Empty file -> set hash to 0 */
+        memset( pHash->dwData, 0, sizeof pHash->dwData );
+        r = ERROR_SUCCESS;
+    }
+
     CloseHandle( handle );
 
     return r;
index 333e6d1..6a28423 100644 (file)
@@ -24,7 +24,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
 
 STRINGTABLE
 {
-       4 "The specified installation package could not be opened.  Please check the file path and try again."
+       4 "The specified installation package could not be opened. Please check the file path and try again."
        5 "path %s not found"
        9 "insert disk %s"
        10 "Windows Installer %s\n\n\
@@ -44,12 +44,12 @@ Advertise a product:\n\
 Apply a patch:\n\
 \t/p patch_package [property]\n\
 \t/p patch_package /a package [property]\n\
-Log and UI Modifiers for above commands:\n\
-\t/l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n\
+Log and user interface modifiers for the above commands:\n\
+\t/l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] log_file\n\
 \t/q{|n|b|r|f|n+|b+|b-}\n\
-Register MSI Service:\n\
+Register the MSI Service:\n\
 \t/y\n\
-Unregister MSI Service:\n\
+Unregister the MSI Service:\n\
 \t/z\n\
 Display this help:\n\
 \t/help\n\
@@ -82,9 +82,9 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 
 #define WINE_FILEDESCRIPTION_STR "Wine MSI dll"
 #define WINE_FILENAME_STR "msi.dll"
-#define WINE_FILEVERSION 4,5,6001,22159
-#define WINE_FILEVERSION_STR "4.5.6001.22159"
-#define WINE_PRODUCTVERSION 4,5,6001,22159
-#define WINE_PRODUCTVERSION_STR "4.5.6001.22159"
+#define WINE_FILEVERSION 4,5,6001,22299
+#define WINE_FILEVERSION_STR "4.5.6001.22299"
+#define WINE_PRODUCTVERSION 4,5,6001,22299
+#define WINE_PRODUCTVERSION_STR "4.5.6001.22299"
 
 #include "wine/wine_common_ver.rc"
index 4b91dd4..7bd3efe 100644 (file)
@@ -80,7 +80,7 @@ HKCR
     {
         CLSID = s '{000C101D-0000-0000-C000-000000000046}'
     }
-    
+
     NoRemove Typelib
     {
         NoRemove '{000C1092-0000-0000-C000-000000000046}'
index 5f4a51f..8dc884a 100644 (file)
 286 stdcall MsiEndTransaction(long)
 287 stub MsiJoinTransaction
 288 stub MsiSetOfflineContextW
-289 stub MsiEnumComponentsExA
-290 stub MsiEnumComponentsExW
+289 stdcall MsiEnumComponentsExA(str long long ptr ptr ptr ptr)
+290 stdcall MsiEnumComponentsExW(wstr long long ptr ptr ptr ptr)
 291 stub MsiEnumClientsExA
 292 stub MsiEnumClientsExW
 293 stub MsiGetComponentPathExA
index 589e4a1..80f5414 100644 (file)
@@ -338,7 +338,8 @@ enum platform
 {
     PLATFORM_INTEL,
     PLATFORM_INTEL64,
-    PLATFORM_X64
+    PLATFORM_X64,
+    PLATFORM_ARM
 };
 
 enum clr_version
@@ -388,6 +389,7 @@ typedef struct tagMSIPACKAGE
     LPWSTR localfile;
     BOOL delete_on_close;
 
+    INSTALLUILEVEL ui_level;
     UINT CurrentInstallState;
     msi_dialog *dialog;
     LPWSTR next_dialog;
@@ -405,7 +407,8 @@ typedef struct tagMSIPACKAGE
     unsigned char scheduled_action_running : 1;
     unsigned char commit_action_running : 1;
     unsigned char rollback_action_running : 1;
-    unsigned char need_reboot : 1;
+    unsigned char need_reboot_at_end : 1;
+    unsigned char need_reboot_now : 1;
     unsigned char need_rollback : 1;
 } MSIPACKAGE;
 
@@ -998,7 +1001,7 @@ extern UINT msi_get_property( MSIDATABASE *, LPCWSTR, LPWSTR, LPDWORD ) DECLSPEC
 extern int msi_get_property_int( MSIDATABASE *package, LPCWSTR prop, int def ) DECLSPEC_HIDDEN;
 extern WCHAR *msi_resolve_source_folder(MSIPACKAGE *package, const WCHAR *name, MSIFOLDER **folder) DECLSPEC_HIDDEN;
 extern void msi_resolve_target_folder(MSIPACKAGE *package, const WCHAR *name, BOOL load_prop) DECLSPEC_HIDDEN;
-extern void msi_clean_path( WCHAR *p ) DECLSPEC_HIDDEN;
+extern WCHAR *msi_normalize_path(const WCHAR *) DECLSPEC_HIDDEN;
 extern WCHAR *msi_resolve_file_source(MSIPACKAGE *package, MSIFILE *file) DECLSPEC_HIDDEN;
 extern const WCHAR *msi_get_target_folder(MSIPACKAGE *package, const WCHAR *name) DECLSPEC_HIDDEN;
 extern void msi_reset_folders( MSIPACKAGE *package, BOOL source ) DECLSPEC_HIDDEN;
@@ -1024,6 +1027,7 @@ extern UINT msi_create_empty_local_file(LPWSTR path, LPCWSTR suffix) DECLSPEC_HI
 extern UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace) DECLSPEC_HIDDEN;
 extern MSIASSEMBLY *msi_load_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN;
 extern UINT msi_install_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN;
+extern UINT msi_uninstall_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN;
 extern BOOL msi_init_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN;
 extern void msi_destroy_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN;
 extern WCHAR *msi_font_version_from_file(const WCHAR *) DECLSPEC_HIDDEN;
@@ -1077,6 +1081,7 @@ static const WCHAR szSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0};
 static const WCHAR szRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
 static const WCHAR szTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
 static const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0};
+static const WCHAR szAllSid[] = {'S','-','1','-','1','-','0',0};
 static const WCHAR szEmpty[] = {0};
 static const WCHAR szAll[] = {'A','L','L',0};
 static const WCHAR szOne[] = {'1',0};
@@ -1147,6 +1152,7 @@ static const WCHAR szIntel[] = {'I','n','t','e','l',0};
 static const WCHAR szIntel64[] = {'I','n','t','e','l','6','4',0};
 static const WCHAR szX64[] = {'x','6','4',0};
 static const WCHAR szAMD64[] = {'A','M','D','6','4',0};
+static const WCHAR szARM[] = {'A','r','m',0};
 static const WCHAR szWow6432NodeCLSID[] = {'W','o','w','6','4','3','2','N','o','d','e','\\','C','L','S','I','D',0};
 static const WCHAR szWow6432Node[] = {'W','o','w','6','4','3','2','N','o','d','e',0};
 static const WCHAR szStreams[] = {'_','S','t','r','e','a','m','s',0};
@@ -1167,6 +1173,7 @@ static const WCHAR szAppDataFolder[] = {'A','p','p','D','a','t','a','F','o','l',
 static const WCHAR szRollbackDisabled[] = {'R','o','l','l','b','a','c','k','D','i','s','a','b','l','e','d',0};
 static const WCHAR szName[] = {'N','a','m','e',0};
 static const WCHAR szData[] = {'D','a','t','a',0};
+static const WCHAR szLangResource[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0};
 
 /* memory allocation macro functions */
 static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1);
index 89da1da..2077396 100644 (file)
@@ -884,10 +884,9 @@ static VOID set_installer_properties(MSIPACKAGE *package)
 
     GetNativeSystemInfo( &sys_info );
     sprintfW( bufstr, szIntFormat, sys_info.wProcessorLevel );
+    msi_set_property( package->db, szIntel, bufstr );
     if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
     {
-        msi_set_property( package->db, szIntel, bufstr );
-
         GetSystemDirectoryW( pth, MAX_PATH );
         PathAddBackslashW( pth );
         msi_set_property( package->db, szSystemFolder, pth );
@@ -1154,9 +1153,9 @@ void msi_adjust_privilege_properties( MSIPACKAGE *package )
 
 MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db, LPCWSTR base_url )
 {
-    static const WCHAR szpi[] = {'%','i',0};
+    static const WCHAR fmtW[] = {'%','u',0};
     MSIPACKAGE *package;
-    WCHAR uilevel[10];
+    WCHAR uilevel[11];
     UINT r;
 
     TRACE("%p\n", db);
@@ -1181,7 +1180,8 @@ MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db, LPCWSTR base_url )
         set_installed_prop( package );
         set_installer_properties( package );
 
-        sprintfW(uilevel,szpi,gUILevel);
+        package->ui_level = gUILevel;
+        sprintfW( uilevel, fmtW, gUILevel & INSTALLUILEVEL_MASK );
         msi_set_property(package->db, szUILevel, uilevel);
 
         r = msi_load_summary_properties( package );
@@ -1297,6 +1297,8 @@ static UINT msi_parse_summary( MSISUMMARYINFO *si, MSIPACKAGE *package )
         package->platform = PLATFORM_INTEL64;
     else if (!strcmpW( template, szX64 ) || !strcmpW( template, szAMD64 ))
         package->platform = PLATFORM_X64;
+    else if (!strcmpW( template, szARM ))
+        package->platform = PLATFORM_ARM;
     else
     {
         WARN("unknown platform %s\n", debugstr_w(template));
@@ -1341,9 +1343,11 @@ static UINT validate_package( MSIPACKAGE *package )
     UINT i;
 
     if (package->platform == PLATFORM_INTEL64)
-    {
         return ERROR_INSTALL_PLATFORM_UNSUPPORTED;
-    }
+#ifndef __arm__
+    if (package->platform == PLATFORM_ARM)
+        return ERROR_INSTALL_PLATFORM_UNSUPPORTED;
+#endif
     IsWow64Process( GetCurrentProcess(), &is_wow64 );
     if (package->platform == PLATFORM_X64)
     {
@@ -1451,6 +1455,9 @@ static UINT get_registered_local_package( const WCHAR *product, const WCHAR *pac
     if (!strcmpiW( package, unsquashed ))
     {
         WCHAR *filename = msi_reg_get_val_str( props_key, INSTALLPROPERTY_LOCALPACKAGEW );
+        if (!filename)
+            goto done;
+
         strcpyW( localfile, filename );
         msi_free( filename );
         r = ERROR_SUCCESS;
@@ -1749,13 +1756,11 @@ MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
 
 INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType, MSIRECORD *record )
 {
-    static const WCHAR szActionData[] =
-        {'A','c','t','i','o','n','D','a','t','a',0};
-    static const WCHAR szSetProgress[] =
-        {'S','e','t','P','r','o','g','r','e','s','s',0};
-    static const WCHAR szActionText[] =
-        {'A','c','t','i','o','n','T','e','x','t',0};
-    LPWSTR message;
+    static const WCHAR szActionData[] = {'A','c','t','i','o','n','D','a','t','a',0};
+    static const WCHAR szSetProgress[] = {'S','e','t','P','r','o','g','r','e','s','s',0};
+    static const WCHAR szActionText[] = {'A','c','t','i','o','n','T','e','x','t',0};
+    MSIRECORD *uirow;
+    LPWSTR deformated, message;
     DWORD i, len, total_len, log_type = 0;
     INT rc = 0;
     char *msg;
@@ -1894,27 +1899,27 @@ INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType, MSIREC
     switch (eMessageType & 0xff000000)
     {
     case INSTALLMESSAGE_ACTIONDATA:
-        /* FIXME: format record here instead of in ui_actiondata to get the
-         * correct action data for external scripts */
-        ControlEvent_FireSubscribedEvent(package, szActionData, record);
+        deformat_string(package, MSI_RecordGetString(record, 2), &deformated);
+        uirow = MSI_CreateRecord(1);
+        MSI_RecordSetStringW(uirow, 1, deformated);
+        msi_free(deformated);
+
+        ControlEvent_FireSubscribedEvent(package, szActionData, uirow);
+
+        msiobj_release(&uirow->hdr);
         break;
-    case INSTALLMESSAGE_ACTIONSTART:
-    {
-        MSIRECORD *uirow;
-        LPWSTR deformated;
-        LPCWSTR action_text = MSI_RecordGetString(record, 2);
 
-        deformat_string(package, action_text, &deformated);
+    case INSTALLMESSAGE_ACTIONSTART:
+        deformat_string(package, MSI_RecordGetString(record, 2), &deformated);
         uirow = MSI_CreateRecord(1);
         MSI_RecordSetStringW(uirow, 1, deformated);
-        TRACE("INSTALLMESSAGE_ACTIONSTART: %s\n", debugstr_w(deformated));
         msi_free(deformated);
 
         ControlEvent_FireSubscribedEvent(package, szActionText, uirow);
 
         msiobj_release(&uirow->hdr);
         break;
-    }
+
     case INSTALLMESSAGE_PROGRESS:
         ControlEvent_FireSubscribedEvent(package, szSetProgress, record);
         break;
index e5d2c51..4143a79 100644 (file)
@@ -1139,101 +1139,13 @@ UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
 
 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
 {
-    static const WCHAR pathW[] = {
-        'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
-        'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
-    UINT r;
-    WCHAR szKeyName[SQUISH_GUID_SIZE];
-    HKEY key;
-    REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
-    DWORD machine_count, managed_count, unmanaged_count;
-    WCHAR keypath[MAX_PATH];
-    LPWSTR usersid = NULL;
-
-    static DWORD last_index;
-
     TRACE("%d %p\n", index, lpguid);
 
     if (NULL == lpguid)
         return ERROR_INVALID_PARAMETER;
 
-    if (index && index - last_index != 1)
-        return ERROR_INVALID_PARAMETER;
-
-    key = 0;
-    r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szInstaller_LocalClassesProducts, 0, NULL, 0, access, NULL, &key, NULL);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    r = RegQueryInfoKeyW(key, NULL, NULL, NULL, &machine_count, NULL, NULL,
-                         NULL, NULL, NULL, NULL, NULL);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    if (machine_count && index <= machine_count)
-    {
-        r = RegEnumKeyW(key, index, szKeyName, SQUISH_GUID_SIZE);
-        if( r == ERROR_SUCCESS )
-        {
-            unsquash_guid(szKeyName, lpguid);
-            last_index = index;
-            RegCloseKey(key);
-            return ERROR_SUCCESS;
-        }
-    }
-    RegCloseKey(key);
-
-    key = 0;
-    if (!(usersid = get_user_sid()))
-    {
-        ERR("Failed to retrieve user SID\n");
-        last_index = 0;
-        return ERROR_FUNCTION_FAILED;
-    }
-    sprintfW(keypath, szInstaller_LocalManaged_fmt, usersid);
-    LocalFree(usersid);
-
-    r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &key, NULL);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    r = RegQueryInfoKeyW(key, NULL, NULL, NULL, &managed_count, NULL, NULL,
-                         NULL, NULL, NULL, NULL, NULL);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    if (managed_count && index <= machine_count + managed_count)
-    {
-        r = RegEnumKeyW(key, index - machine_count, szKeyName, SQUISH_GUID_SIZE);
-        if( r == ERROR_SUCCESS )
-        {
-            unsquash_guid(szKeyName, lpguid);
-            last_index = index;
-            RegCloseKey(key);
-            return ERROR_SUCCESS;
-        }
-    }
-    RegCloseKey(key);
-
-    key = 0;
-    r = RegCreateKeyW(HKEY_CURRENT_USER, pathW, &key);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    r = RegQueryInfoKeyW(key, NULL, NULL, NULL, &unmanaged_count, NULL, NULL,
-                         NULL, NULL, NULL, NULL, NULL);
-    if( r != ERROR_SUCCESS ) goto failed;
-
-    if (unmanaged_count && index <= machine_count + managed_count + unmanaged_count)
-    {
-        r = RegEnumKeyW(key, index - machine_count - managed_count, szKeyName, SQUISH_GUID_SIZE);
-        if( r == ERROR_SUCCESS )
-        {
-            unsquash_guid(szKeyName, lpguid);
-            last_index = index;
-            RegCloseKey(key);
-            return ERROR_SUCCESS;
-        }
-    }
-failed:
-    RegCloseKey(key);
-    last_index = 0;
-    return ERROR_NO_MORE_ITEMS;
+    return MsiEnumProductsExW( NULL, szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid,
+                               NULL, NULL, NULL );
 }
 
 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
@@ -1293,7 +1205,9 @@ UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
     DWORD r;
     WCHAR szwGuid[GUID_SIZE];
 
-    TRACE("%d %p\n", index, lpguid);
+    TRACE("%u, %p\n", index, lpguid);
+
+    if (!lpguid) return ERROR_INVALID_PARAMETER;
 
     r = MsiEnumComponentsW(index, szwGuid);
     if( r == ERROR_SUCCESS )
@@ -1304,22 +1218,222 @@ UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
 
 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
 {
-    HKEY hkey;
-    REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
-    WCHAR szKeyName[SQUISH_GUID_SIZE];
-    DWORD r;
+    TRACE("%u, %p\n", index, lpguid);
 
-    TRACE("%d %p\n", index, lpguid);
+    if (!lpguid) return ERROR_INVALID_PARAMETER;
 
-    r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szInstaller_Components, 0, NULL, 0, access, NULL, &hkey, NULL);
-    if( r != ERROR_SUCCESS )
+    return MsiEnumComponentsExW( szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid, NULL, NULL, NULL );
+}
+
+UINT WINAPI MsiEnumComponentsExA( LPCSTR user_sid, DWORD ctx, DWORD index, CHAR guid[39],
+                                  MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len )
+{
+    UINT r;
+    WCHAR *user_sidW = NULL, *sidW = NULL, guidW[GUID_SIZE];
+
+    TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_a(user_sid), ctx, index, guid, installed_ctx,
+          sid, sid_len);
+
+    if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
+    if (user_sid && !(user_sidW = strdupAtoW( user_sid ))) return ERROR_OUTOFMEMORY;
+    if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
+    {
+        msi_free( user_sidW );
+        return ERROR_OUTOFMEMORY;
+    }
+    r = MsiEnumComponentsExW( user_sidW, ctx, index, guidW, installed_ctx, sidW, sid_len );
+    if (r == ERROR_SUCCESS)
+    {
+        if (guid) WideCharToMultiByte( CP_ACP, 0, guidW, GUID_SIZE, guid, GUID_SIZE, NULL, NULL );
+        if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
+    }
+    msi_free( user_sidW );
+    msi_free( sidW );
+    return r;
+}
+
+static UINT fetch_machine_component( DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
+                                     MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
+{
+    static const WCHAR componentsW[] =
+        {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+         'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
+         'S','-','1','-','5','-','1','8','\\','C','o','m','p','o','n','e','n','t','s',0};
+    UINT r = ERROR_SUCCESS;
+    WCHAR component[GUID_SIZE];
+    DWORD i = 0, len_component;
+    REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
+    HKEY key_components;
+
+    if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, componentsW, 0, access, &key_components ))
         return ERROR_NO_MORE_ITEMS;
 
-    r = RegEnumKeyW(hkey, index, szKeyName, SQUISH_GUID_SIZE);
-    if( r == ERROR_SUCCESS )
-        unsquash_guid(szKeyName, lpguid);
+    len_component = sizeof(component)/sizeof(component[0]);
+    while (!RegEnumKeyExW( key_components, i, component, &len_component, NULL, NULL, NULL, NULL ))
+    {
+        if (*idx == index) goto found;
+        (*idx)++;
+        len_component = sizeof(component)/sizeof(component[0]);
+        i++;
+    }
+    RegCloseKey( key_components );
+    return ERROR_NO_MORE_ITEMS;
+
+found:
+    if (sid_len)
+    {
+        if (*sid_len < 1)
+        {
+            *sid_len = 1;
+            r = ERROR_MORE_DATA;
+        }
+        else if (sid)
+        {
+            *sid_len = 0;
+            sid[0] = 0;
+        }
+    }
+    if (guid) unsquash_guid( component, guid );
+    if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
+    RegCloseKey( key_components );
+    return r;
+}
+
+static UINT fetch_user_component( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx,
+                                  WCHAR guid[39], MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid,
+                                  LPDWORD sid_len )
+{
+    static const WCHAR userdataW[] =
+        {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+         'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a',0};
+    static const WCHAR componentsW[] = {'\\','C','o','m','p','o','n','e','n','t','s',0};
+    UINT r = ERROR_SUCCESS;
+    WCHAR path[MAX_PATH], component[GUID_SIZE], user[128];
+    DWORD i = 0, j = 0, len_component, len_user;
+    REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
+    HKEY key_users, key_components;
+
+    if (ctx == MSIINSTALLCONTEXT_USERMANAGED) /* FIXME: were to find these? */
+        return ERROR_NO_MORE_ITEMS;
+
+    if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, userdataW, 0, access, &key_users ))
+        return ERROR_NO_MORE_ITEMS;
+
+    len_user = sizeof(user)/sizeof(user[0]);
+    while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
+    {
+        if ((strcmpW( usersid, szAllSid ) && strcmpW( usersid, user )) ||
+            !strcmpW( szLocalSid, user ))
+        {
+            i++;
+            len_user = sizeof(user)/sizeof(user[0]);
+            continue;
+        }
+        strcpyW( path, user );
+        strcatW( path, componentsW );
+        if (RegOpenKeyExW( key_users, path, 0, access, &key_components ))
+        {
+            i++;
+            len_user = sizeof(user)/sizeof(user[0]);
+            continue;
+        }
+        len_component = sizeof(component)/sizeof(component[0]);
+        while (!RegEnumKeyExW( key_components, j, component, &len_component, NULL, NULL, NULL, NULL ))
+        {
+            if (*idx == index) goto found;
+            (*idx)++;
+            len_component = sizeof(component)/sizeof(component[0]);
+            j++;
+        }
+        RegCloseKey( key_components );
+        len_user = sizeof(user)/sizeof(user[0]);
+        i++;
+    }
+    RegCloseKey( key_users );
+    return ERROR_NO_MORE_ITEMS;
+
+found:
+    if (sid_len)
+    {
+        if (*sid_len < len_user + 1)
+        {
+            *sid_len = len_user + 1;
+            r = ERROR_MORE_DATA;
+        }
+        else if (sid)
+        {
+            *sid_len = len_user;
+            strcpyW( sid, user );
+        }
+    }
+    if (guid) unsquash_guid( component, guid );
+    if (installed_ctx) *installed_ctx = ctx;
+    RegCloseKey( key_components );
+    RegCloseKey( key_users );
+    return r;
+}
+
+static UINT enum_components( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
+                             MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
+{
+    UINT r = ERROR_NO_MORE_ITEMS;
+    WCHAR *user = NULL;
+
+    if (!usersid)
+    {
+        usersid = user = get_user_sid();
+        if (!user) return ERROR_FUNCTION_FAILED;
+    }
+    if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
+    {
+        r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERMANAGED, index, idx, guid,
+                                  installed_ctx, sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+    if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
+    {
+        r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index, idx, guid,
+                                  installed_ctx, sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+    if (ctx & MSIINSTALLCONTEXT_MACHINE)
+    {
+        r = fetch_machine_component( MSIINSTALLCONTEXT_MACHINE, index, idx, guid, installed_ctx,
+                                     sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+
+done:
+    LocalFree( user );
+    return r;
+}
+
+UINT WINAPI MsiEnumComponentsExW( LPCWSTR user_sid, DWORD ctx, DWORD index, WCHAR guid[39],
+                                  MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
+{
+    UINT r;
+    DWORD idx = 0;
+    static DWORD last_index;
+
+    TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_w(user_sid), ctx, index, guid, installed_ctx,
+          sid, sid_len);
+
+    if ((sid && !sid_len) || !ctx || (user_sid && ctx == MSIINSTALLCONTEXT_MACHINE))
+        return ERROR_INVALID_PARAMETER;
+
+    if (index && index - last_index != 1)
+        return ERROR_INVALID_PARAMETER;
+
+    if (!index) last_index = 0;
+
+    r = enum_components( user_sid, ctx, index, &idx, guid, installed_ctx, sid, sid_len );
+    if (r == ERROR_SUCCESS)
+        last_index = index;
+    else
+        last_index = 0;
 
-    RegCloseKey(hkey);
     return r;
 }
 
@@ -2113,22 +2227,252 @@ done:
     return r;
 }
 
-UINT WINAPI MsiEnumProductsExA( LPCSTR szProductCode, LPCSTR szUserSid,
-        DWORD dwContext, DWORD dwIndex, CHAR szInstalledProductCode[39],
-        MSIINSTALLCONTEXT* pdwInstalledContext, LPSTR szSid, LPDWORD pcchSid)
+UINT WINAPI MsiEnumProductsExA( LPCSTR product, LPCSTR usersid, DWORD ctx, DWORD index,
+                                CHAR installed_product[GUID_SIZE],
+                                MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len )
 {
-    FIXME("%s %s %d %d %p %p %p %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
-          dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
-          szSid, pcchSid);
+    UINT r;
+    WCHAR installed_productW[GUID_SIZE], *productW = NULL, *usersidW = NULL, *sidW = NULL;
+
+    TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_a(product), debugstr_a(usersid),
+          ctx, index, installed_product, installed_ctx, sid, sid_len);
+
+    if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
+    if (product && !(productW = strdupAtoW( product ))) return ERROR_OUTOFMEMORY;
+    if (usersid && !(usersidW = strdupAtoW( usersid )))
+    {
+        msi_free( productW );
+        return ERROR_OUTOFMEMORY;
+    }
+    if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
+    {
+        msi_free( usersidW );
+        msi_free( productW );
+        return ERROR_OUTOFMEMORY;
+    }
+    r = MsiEnumProductsExW( productW, usersidW, ctx, index, installed_productW,
+                            installed_ctx, sidW, sid_len );
+    if (r == ERROR_SUCCESS)
+    {
+        if (installed_product) WideCharToMultiByte( CP_ACP, 0, installed_productW, GUID_SIZE,
+                                                    installed_product, GUID_SIZE, NULL, NULL );
+        if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
+    }
+    msi_free( productW );
+    msi_free( usersidW );
+    msi_free( sidW );
+    return r;
+}
+
+static UINT fetch_machine_product( const WCHAR *match, DWORD index, DWORD *idx,
+                                   WCHAR installed_product[GUID_SIZE],
+                                   MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
+{
+    static const WCHAR productsW[] =
+        {'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
+         'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
+    UINT r;
+    WCHAR product[GUID_SIZE];
+    DWORD i = 0, len;
+    REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
+    HKEY key;
+
+    if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, productsW, 0, access, &key ))
+        return ERROR_NO_MORE_ITEMS;
+
+    len = sizeof(product)/sizeof(product[0]);
+    while (!RegEnumKeyExW( key, i, product, &len, NULL, NULL, NULL, NULL ))
+    {
+        if (match && strcmpW( match, product ))
+        {
+            i++;
+            len = sizeof(product)/sizeof(product[0]);
+            continue;
+        }
+        if (*idx == index) goto found;
+        (*idx)++;
+        len = sizeof(product)/sizeof(product[0]);
+        i++;
+    }
+    RegCloseKey( key );
     return ERROR_NO_MORE_ITEMS;
+
+found:
+    if (sid_len && *sid_len < 1)
+    {
+        *sid_len = 1;
+        r = ERROR_MORE_DATA;
+    }
+    else
+    {
+        if (installed_product) unsquash_guid( product, installed_product );
+        if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
+        if (sid)
+        {
+            sid[0] = 0;
+            *sid_len = 0;
+        }
+        r = ERROR_SUCCESS;
+    }
+    RegCloseKey( key );
+    return r;
 }
 
-UINT WINAPI MsiEnumProductsExW( LPCWSTR szProductCode, LPCWSTR szUserSid,
-        DWORD dwContext, DWORD dwIndex, WCHAR szInstalledProductCode[39],
-        MSIINSTALLCONTEXT* pdwInstalledContext, LPWSTR szSid, LPDWORD pcchSid)
+static UINT fetch_user_product( const WCHAR *match, const WCHAR *usersid, DWORD ctx, DWORD index,
+                                DWORD *idx, WCHAR installed_product[GUID_SIZE],
+                                MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
 {
-    FIXME("%s %s %d %d %p %p %p %p\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
-          dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
-          szSid, pcchSid);
+    static const WCHAR managedW[] =
+        {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s',
+         'i','o','n','\\','I','n','s','t','a','l','l','e','r','\\','M','a','n','a','g','e','d',0};
+    static const WCHAR managed_productsW[] =
+        {'\\','I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
+    static const WCHAR unmanaged_productsW[] =
+        {'\\','S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+         'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
+    UINT r;
+    const WCHAR *subkey;
+    WCHAR path[MAX_PATH], product[GUID_SIZE], user[128];
+    DWORD i = 0, j = 0, len_product, len_user;
+    REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
+    HKEY key_users, key_products;
+
+    if (ctx == MSIINSTALLCONTEXT_USERMANAGED)
+    {
+        subkey = managed_productsW;
+        if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, managedW, 0, access, &key_users ))
+            return ERROR_NO_MORE_ITEMS;
+    }
+    else if (ctx == MSIINSTALLCONTEXT_USERUNMANAGED)
+    {
+        subkey = unmanaged_productsW;
+        if (RegOpenKeyExW( HKEY_USERS, NULL, 0, access, &key_users ))
+            return ERROR_NO_MORE_ITEMS;
+    }
+    else return ERROR_INVALID_PARAMETER;
+
+    len_user = sizeof(user)/sizeof(user[0]);
+    while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
+    {
+        if (strcmpW( usersid, user ) && strcmpW( usersid, szAllSid ))
+        {
+            i++;
+            len_user = sizeof(user)/sizeof(user[0]);
+            continue;
+        }
+        strcpyW( path, user );
+        strcatW( path, subkey );
+        if (RegOpenKeyExW( key_users, path, 0, access, &key_products ))
+        {
+            i++;
+            len_user = sizeof(user)/sizeof(user[0]);
+            continue;
+        }
+        len_product = sizeof(product)/sizeof(product[0]);
+        while (!RegEnumKeyExW( key_products, j, product, &len_product, NULL, NULL, NULL, NULL ))
+        {
+            if (match && strcmpW( match, product ))
+            {
+                j++;
+                len_product = sizeof(product)/sizeof(product[0]);
+                continue;
+            }
+            if (*idx == index) goto found;
+            (*idx)++;
+            len_product = sizeof(product)/sizeof(product[0]);
+            j++;
+        }
+        RegCloseKey( key_products );
+        len_user = sizeof(user)/sizeof(user[0]);
+        i++;
+    }
+    RegCloseKey( key_users );
     return ERROR_NO_MORE_ITEMS;
+
+found:
+    if (sid_len && *sid_len <= len_user)
+    {
+        *sid_len = len_user;
+        r = ERROR_MORE_DATA;
+    }
+    else
+    {
+        if (installed_product) unsquash_guid( product, installed_product );
+        if (installed_ctx) *installed_ctx = ctx;
+        if (sid)
+        {
+            strcpyW( sid, user );
+            *sid_len = len_user;
+        }
+        r = ERROR_SUCCESS;
+    }
+    RegCloseKey( key_products );
+    RegCloseKey( key_users );
+    return r;
+}
+
+static UINT enum_products( const WCHAR *product, const WCHAR *usersid, DWORD ctx, DWORD index,
+                           DWORD *idx, WCHAR installed_product[GUID_SIZE],
+                           MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
+{
+    UINT r = ERROR_NO_MORE_ITEMS;
+    WCHAR *user = NULL;
+
+    if (!usersid)
+    {
+        usersid = user = get_user_sid();
+        if (!user) return ERROR_FUNCTION_FAILED;
+    }
+    if (ctx & MSIINSTALLCONTEXT_MACHINE)
+    {
+        r = fetch_machine_product( product, index, idx, installed_product, installed_ctx,
+                                   sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+    if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
+    {
+        r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index,
+                                idx, installed_product, installed_ctx, sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+    if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
+    {
+        r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERMANAGED, index,
+                                idx, installed_product, installed_ctx, sid, sid_len );
+        if (r != ERROR_NO_MORE_ITEMS) goto done;
+    }
+
+done:
+    LocalFree( user );
+    return r;
+}
+
+UINT WINAPI MsiEnumProductsExW( LPCWSTR product, LPCWSTR usersid, DWORD ctx, DWORD index,
+                                WCHAR installed_product[GUID_SIZE],
+                                MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
+{
+    UINT r;
+    DWORD idx = 0;
+    static DWORD last_index;
+
+    TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_w(product), debugstr_w(usersid),
+          ctx, index, installed_product, installed_ctx, sid, sid_len);
+
+    if ((sid && !sid_len) || !ctx || (usersid && ctx == MSIINSTALLCONTEXT_MACHINE))
+        return ERROR_INVALID_PARAMETER;
+
+    if (index && index - last_index != 1)
+        return ERROR_INVALID_PARAMETER;
+
+    if (!index) last_index = 0;
+
+    r = enum_products( product, usersid, ctx, index, &idx, installed_product, installed_ctx,
+                       sid, sid_len );
+    if (r == ERROR_SUCCESS)
+        last_index = index;
+    else
+        last_index = 0;
+
+    return r;
 }
index 27bbee1..236b60b 100644 (file)
@@ -88,10 +88,7 @@ DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function
     DISPID dispid;
     CLSID clsid;
     VARIANT var;
-
-    /* Return success by default (if Windows Script not installed) - not native behavior. This
-     * should be here until we implement wine scripting. */
-    DWORD ret = ERROR_SUCCESS;
+    DWORD ret = ERROR_INSTALL_FAILURE;
 
     CoInitialize(NULL);
 
@@ -126,9 +123,6 @@ DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function
         goto done;
     }
 
-    /* If we got this far, Windows Script is installed, so don't return success by default anymore */
-    ret = ERROR_INSTALL_FAILURE;
-
     /* Get the IActiveScriptParse engine interface */
     hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse);
     if (FAILED(hr)) goto done;
@@ -142,7 +136,7 @@ DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function
     if (FAILED(hr)) goto done;
 
     /* Add the session object */
-    hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_ISVISIBLE);
+    hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_GLOBALMEMBERS);
     if (FAILED(hr)) goto done;
 
     /* Pass the script to the engine */
index f2f0b77..2ff29c5 100644 (file)
@@ -156,7 +156,7 @@ static struct expr * EXPR_wildcard( void *info );
 
 
 /* Line 189 of yacc.c  */
-#line 153 "sql.tab.c"
+#line 161 "sql.tab.c"
 
 /* Enabling traces.  */
 #ifndef YYDEBUG
@@ -267,7 +267,7 @@ typedef union YYSTYPE
 
 
 /* Line 214 of yacc.c  */
-#line 264 "sql.tab.c"
+#line 272 "sql.tab.c"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
index d3c6714..dd25fbb 100644 (file)
@@ -1766,7 +1766,7 @@ static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
                           MSIRECORD *rec, UINT row)
 {
     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
-    UINT r, column;
+    UINT r, frow, column;
 
     TRACE("%p %d %p\n", view, eModifyMode, rec );
 
@@ -1811,8 +1811,18 @@ static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
         r = msi_table_assign( view, rec );
         break;
 
-    case MSIMODIFY_REPLACE:
     case MSIMODIFY_MERGE:
+        /* check row that matches this record */
+        r = msi_table_find_row( tv, rec, &frow, &column );
+        if (r != ERROR_SUCCESS)
+        {
+            r = table_validate_new( tv, rec, NULL );
+            if (r == ERROR_SUCCESS)
+                r = TABLE_insert_row( view, rec, -1, FALSE );
+        }
+        break;
+
+    case MSIMODIFY_REPLACE:
     case MSIMODIFY_VALIDATE:
     case MSIMODIFY_VALIDATE_FIELD:
     case MSIMODIFY_VALIDATE_DELETE:
@@ -2185,9 +2195,6 @@ UINT MSI_CommitTables( MSIDATABASE *db )
         }
     }
 
-    /* force everything to reload next time */
-    free_cached_tables( db );
-
     hr = IStorage_Commit( db->storage, 0 );
     if (FAILED( hr ))
     {
@@ -2501,14 +2508,12 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
                                       string_table *st, TRANSFORMDATA *transform,
                                       UINT bytes_per_strref )
 {
-    UINT rawsize = 0;
     BYTE *rawdata = NULL;
     MSITABLEVIEW *tv = NULL;
-    UINT r, n, sz, i, mask;
+    UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0;
     MSIRECORD *rec = NULL;
-    UINT colcol = 0;
     WCHAR coltable[32];
-    LPWSTR name;
+    const WCHAR *name;
 
     if (!transform)
         return ERROR_SUCCESS;
@@ -2539,18 +2544,18 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
 
     /* interpret the data */
-    for( n=0; n < rawsize;  )
+    for (n = 0; n < rawsize;)
     {
-        mask = rawdata[n] | (rawdata[n+1] << 8);
-
-        if (mask&1)
+        mask = rawdata[n] | (rawdata[n + 1] << 8);
+        if (mask & 1)
         {
             /*
              * if the low bit is set, columns are continuous and
              * the number of columns is specified in the high byte
              */
             sz = 2;
-            for( i=0; i<tv->num_cols; i++ )
+            num_cols = mask >> 8;
+            for (i = 0; i < num_cols; i++)
             {
                 if( (tv->columns[i].type & MSITYPE_STRING) &&
                     ! MSITYPE_IS_BINARY(tv->columns[i].type) )
@@ -2570,12 +2575,13 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
              * and it means that this row should be deleted.
              */
             sz = 2;
-            for( i=0; i<tv->num_cols; i++ )
+            num_cols = tv->num_cols;
+            for (i = 0; i < num_cols; i++)
             {
-                if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
+                if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
                 {
-                    if(tv->columns[i].type & MSITYPE_STRING) &&
-                        ! MSITYPE_IS_BINARY(tv->columns[i].type) )
+                    if ((tv->columns[i].type & MSITYPE_STRING) &&
+                        !MSITYPE_IS_BINARY(tv->columns[i].type))
                         sz += bytes_per_strref;
                     else
                         sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
@@ -2584,7 +2590,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
         }
 
         /* check we didn't run of the end of the table */
-        if ( (n+sz) > rawsize )
+        if (n + sz > rawsize)
         {
             ERR("borked.\n");
             dump_table( st, (USHORT *)rawdata, rawsize );
index 6d283af..9d8fae6 100644 (file)
@@ -453,6 +453,12 @@ UINT WINAPI MsiEnumComponentsA(DWORD, LPSTR);
 UINT WINAPI MsiEnumComponentsW(DWORD, LPWSTR);
 #define     MsiEnumComponents WINELIB_NAME_AW(MsiEnumComponents)
 
+UINT WINAPI MsiEnumComponentsExA(LPCSTR, DWORD, DWORD, CHAR[39],
+                                 MSIINSTALLCONTEXT *, LPSTR, LPDWORD);
+UINT WINAPI MsiEnumComponentsExW(LPCWSTR, DWORD, DWORD, WCHAR[39],
+                                 MSIINSTALLCONTEXT *, LPWSTR, LPDWORD);
+#define     MsiEnumComponentsEx WINELIB_NAME_AW(MsiEnumComponentsEx)
+
 UINT WINAPI MsiEnumClientsA(LPCSTR, DWORD, LPSTR);
 UINT WINAPI MsiEnumClientsW(LPCWSTR, DWORD, LPWSTR);
 #define     MsiEnumClients WINELIB_NAME_AW(MsiEnumClients)
index af8f9cf..2f41432 100644 (file)
@@ -102,7 +102,7 @@ reactos/dll/win32/msg711.acm      # Synced to Wine-1.3.37
 reactos/dll/win32/msgsm32.acm     # Synced to Wine-1.3.37
 reactos/dll/win32/mshtml          # Autosync
 reactos/dll/win32/mshtml.tlb      # Autosync
-reactos/dll/win32/msi             # Synced to Wine-1.3.37
+reactos/dll/win32/msi             # Synced to Wine-1.5.4
 reactos/dll/win32/msimg32         # Synced to Wine-1.3.37
 reactos/dll/win32/msimtf          # Synced to Wine-1.3.37
 reactos/dll/win32/msisip          # Synced to Wine-1.3.37