TODO: - make awstring version of MsiProvideComponent() and use that. - Actually implement MsiProvideAssemblyA() using the above. - Write conformance test?
Dependency: - Have ACTION_PublishAssemblies() record feature (https://gitlab.winehq.org/wine/wine/-/merge_requests/9037) - Have MsiEnumFeatures() fixed. Currently it seems to just look for features at the wrong place and thus is useless for this purpose. (https://gitlab.winehq.org/wine/wine/-/merge_requests/9041)
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58578
-- v3: [WIP] msi: implement MsiProvideAssembly[AW]()
From: Ratchanan Srirattanamet peathot+winehq@hotmail.com
TODO: - make awstring version of MsiProvideComponent() and use that. - Actually implement MsiProvideAssemblyA() using the above. - Write conformance test?
Dependency: - Have ACTION_PublishAssemblies() record feature - Have MsiEnumFeatures() fixed. Currently it seems to just look for features at the wrong place and thus is useless for this purpose.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58578 --- dlls/msi/assembly.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ dlls/msi/msi.c | 58 +++++++++++++++++++++- dlls/msi/msipriv.h | 2 + include/msi.h | 5 ++ 4 files changed, 179 insertions(+), 2 deletions(-)
diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index 6f386d64a88..19dbedace05 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -685,3 +685,119 @@ UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) } return ERROR_SUCCESS; } + +UINT msi_lookup_published_assembly(UINT install_context, + const WCHAR * displayname, + const WCHAR * app_context, + BOOL win32, + WCHAR * out_product_id, + WCHAR * out_feature, + WCHAR * out_component_id) +{ + UINT rc = ERROR_FUNCTION_FAILED; + LONG res; + HRESULT hr; + HKEY hkey; + IAssemblyName * name; + + DWORD num_values; + DWORD max_value_name_len; + DWORD max_value_data_len; + WCHAR * value_name; + WCHAR * value_data; + DWORD index; + BOOL found; + + if (!init_assembly_caches() || !pCreateAssemblyNameObject) + return ERROR_FUNCTION_FAILED; + + hr = pCreateAssemblyNameObject( &name, displayname, CANOF_PARSE_DISPLAY_NAME, NULL ); + if (FAILED( hr )) return ERROR_INVALID_PARAMETER; + + if (app_context) + { + if ((res = open_local_assembly_key( install_context, win32, app_context, &hkey ))) + { + WARN( "failed to open local assembly key %ld\n", res ); + rc = ERROR_FUNCTION_FAILED; + goto out_name; + } + } + else + { + if ((res = open_global_assembly_key( install_context, win32, &hkey ))) + { + WARN( "failed to open global assembly key %ld\n", res ); + rc = ERROR_FUNCTION_FAILED; + goto out_name; + } + } + + rc = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, + &num_values, &max_value_name_len, &max_value_data_len, + NULL, NULL); + if (rc != ERROR_SUCCESS) + goto out_reg; + if (num_values == 0) { + rc = ERROR_UNKNOWN_COMPONENT; + goto out_reg; + } + + value_name = malloc((max_value_name_len + 1) * sizeof(WCHAR)); + value_data = malloc((max_value_data_len + 1) * sizeof(WCHAR)); + found = FALSE; + + for (index = 0; index < num_values && !found; index++) + { + DWORD value_type; + DWORD len_value_name = max_value_name_len + 1; + DWORD len_value_data = max_value_data_len + 1; + IAssemblyName * value_asmname; + + rc = RegEnumValueW(hkey, index, value_name, &len_value_name, NULL, + &value_type, (BYTE *) value_data, &len_value_data); + if (rc != ERROR_SUCCESS) { + rc = ERROR_BAD_CONFIGURATION; + break; + } + + if (value_type != REG_MULTI_SZ) { + rc = ERROR_BAD_CONFIGURATION;; + break; + } + + hr = pCreateAssemblyNameObject( &value_asmname, value_name, CANOF_PARSE_DISPLAY_NAME, NULL ); + if (FAILED( hr )) { + rc = ERROR_BAD_CONFIGURATION; + break; + } + + /* IsEqual will do the right thing if `name` does not specify all + components of a display name by just skipping them. */ + hr = IAssemblyName_IsEqual(name, value_asmname, ASM_CMPF_IL_ALL); + + if (hr == S_OK) { + found = TRUE; + + rc = MsiDecomposeDescriptorW( + value_data, out_product_id, out_feature, out_component_id, NULL); + if (rc == ERROR_INVALID_PARAMETER) + rc = ERROR_BAD_CONFIGURATION; + } else { + index++; + } + + IAssemblyName_Release(value_asmname); + } + + if (!found && rc == ERROR_SUCCESS) + rc = ERROR_UNKNOWN_COMPONENT; + + free(value_name); + free(value_data); +out_reg: + RegCloseKey(hkey); +out_name: + IAssemblyName_Release(name); + return rc; +} diff --git a/dlls/msi/msi.c b/dlls/msi/msi.c index 6edee9c8185..a76c12f2d7b 100644 --- a/dlls/msi/msi.c +++ b/dlls/msi/msi.c @@ -2486,12 +2486,66 @@ UINT WINAPI MsiProvideAssemblyA( const char *szAssemblyName, const char *szAppCo return ERROR_CALL_NOT_IMPLEMENTED; }
+// TODO: make awstring version of MsiProvideComponent() and use that. UINT WINAPI MsiProvideAssemblyW( const WCHAR *szAssemblyName, const WCHAR *szAppContext, DWORD dwInstallMode, DWORD dwAssemblyInfo, WCHAR *lpPathBuf, DWORD *pcchPathBuf ) { - FIXME( "%s, %s, %#lx, %#lx, %p, %p\n", debugstr_w(szAssemblyName), debugstr_w(szAppContext), dwInstallMode, + UINT rc; + BOOL win32; + WCHAR product[MAX_FEATURE_CHARS+1]; + WCHAR feature[MAX_FEATURE_CHARS+1]; + WCHAR feature_parent[MAX_FEATURE_CHARS+1]; + WCHAR component[MAX_FEATURE_CHARS+1]; + + TRACE( "%s, %s, %#lx, %#lx, %p, %p\n", debugstr_w(szAssemblyName), debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, pcchPathBuf ); - return ERROR_CALL_NOT_IMPLEMENTED; + + win32 = (dwAssemblyInfo == MSIASSEMBLYINFO_WIN32ASSEMBLY); + + rc = msi_lookup_published_assembly(MSIINSTALLCONTEXT_USERMANAGED, szAssemblyName, szAppContext, + win32, product, feature, component); + + if (rc != ERROR_SUCCESS) { + rc = msi_lookup_published_assembly(MSIINSTALLCONTEXT_MACHINE, szAssemblyName, szAppContext, + win32, product, feature, component); + } + + if (rc != ERROR_SUCCESS) { + rc = msi_lookup_published_assembly(MSIINSTALLCONTEXT_USERUNMANAGED, szAssemblyName, szAppContext, + win32, product, feature, component); + } + + if (rc != ERROR_SUCCESS) + return rc; + + /* If the product contain only 1 feature, it won't be recorded in assembly publication. + * Look it up to use as a parameter in MSIProvideComponentW(). + */ + if (feature[0] == '\0') { + // TODO: fix MsiEnumFeaturesW() so that it's useful. Please. + + /* First, verify that this product indeed has single feature. */ + rc = MsiEnumFeaturesW(product, 1, feature, feature_parent); + if (rc == ERROR_SUCCESS) { + ERR("Product for assembly '%ls' has > 1 feature, but the published assembly record " + "doesn't have feature. This can happen if package is installed in older version " + "of Wine. Try re-installing this package.\n", szAssemblyName); + return ERROR_BAD_CONFIGURATION; + } + + /* Now obtain the feature name to use with MsiProvideComponentW(). */ + rc = MsiEnumFeaturesW(product, 0, feature, feature_parent); + if (rc != ERROR_SUCCESS) + return rc; + } + + if (dwInstallMode == INSTALLMODE_NODETECTION_ANY) { + FIXME("INSTALLMODE_NODETECTION_ANY currently behave the same way " + "as INSTALLMODE_NODETECTION\n"); + dwInstallMode = INSTALLMODE_NODETECTION; + } + + return MsiProvideComponentW(product, feature, component, dwInstallMode, lpPathBuf, pcchPathBuf); }
UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 0efc367c76d..4ffc14c30e9 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -1080,6 +1080,8 @@ extern void msi_destroy_assembly_caches(void); extern BOOL msi_is_global_assembly(MSICOMPONENT *); extern IAssemblyEnum *msi_create_assembly_enum(const WCHAR *); extern WCHAR *msi_get_assembly_path(const WCHAR *) __WINE_DEALLOC(free) __WINE_MALLOC; +extern UINT msi_lookup_published_assembly(UINT, const WCHAR *, const WCHAR *, + BOOL, WCHAR *, WCHAR *, WCHAR *); extern WCHAR **msi_split_string(const WCHAR *, WCHAR); extern UINT msi_set_original_database_property(MSIDATABASE *, const WCHAR *); extern WCHAR *msi_get_error_message(MSIDATABASE *, int) __WINE_DEALLOC(free) __WINE_MALLOC; diff --git a/include/msi.h b/include/msi.h index 4012d365579..a9d5790e389 100644 --- a/include/msi.h +++ b/include/msi.h @@ -252,6 +252,11 @@ typedef struct tagMSIPATCHSEQUENCEINFOW UINT uStatus; } MSIPATCHSEQUENCEINFOW, *PMSIPATCHSEQUENCEINFOW;
+typedef enum tagMSIASSEMBLYINFO { + MSIASSEMBLYINFO_NETASSEMBLY = 0, + MSIASSEMBLYINFO_WIN32ASSEMBLY = 1, +} MSIASSEMBLYINFO; + #define MAX_FEATURE_CHARS 38
#define ERROR_PATCH_TARGET_NOT_FOUND 1642