| /* |
| * Implementation of the Microsoft Installer (msi.dll) |
| * |
| * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "winnls.h" |
| #include "shlwapi.h" |
| #include "wine/debug.h" |
| #include "msi.h" |
| #include "msidefs.h" |
| #include "msiquery.h" |
| #include "msipriv.h" |
| #include "wincrypt.h" |
| #include "winver.h" |
| #include "winuser.h" |
| #include "shlobj.h" |
| #include "shobjidl.h" |
| #include "objidl.h" |
| #include "wine/unicode.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); |
| |
| static const WCHAR installerW[] = {'\\','I','n','s','t','a','l','l','e','r',0}; |
| |
| static UINT msi_locate_product(LPCWSTR szProduct, MSIINSTALLCONTEXT *context) |
| { |
| HKEY hkey = NULL; |
| |
| *context = MSIINSTALLCONTEXT_NONE; |
| |
| if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, |
| &hkey, FALSE) == ERROR_SUCCESS) |
| *context = MSIINSTALLCONTEXT_USERMANAGED; |
| else if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, |
| &hkey, FALSE) == ERROR_SUCCESS) |
| *context = MSIINSTALLCONTEXT_MACHINE; |
| else if (MSIREG_OpenProductKey(szProduct, NULL, |
| MSIINSTALLCONTEXT_USERUNMANAGED, |
| &hkey, FALSE) == ERROR_SUCCESS) |
| *context = MSIINSTALLCONTEXT_USERUNMANAGED; |
| |
| RegCloseKey(hkey); |
| |
| if (*context == MSIINSTALLCONTEXT_NONE) |
| return ERROR_UNKNOWN_PRODUCT; |
| |
| return ERROR_SUCCESS; |
| } |
| |
| UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct) |
| { |
| UINT r; |
| LPWSTR szwProd = NULL; |
| |
| TRACE("%s %p\n",debugstr_a(szProduct), phProduct); |
| |
| if( szProduct ) |
| { |
| szwProd = strdupAtoW( szProduct ); |
| if( !szwProd ) |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| r = MsiOpenProductW( szwProd, phProduct ); |
| |
| msi_free( szwProd ); |
| |
| return r; |
| } |
| |
| static UINT MSI_OpenProductW(LPCWSTR szProduct, MSIPACKAGE **package) |
| { |
| UINT r; |
| HKEY props; |
| LPWSTR path; |
| MSIINSTALLCONTEXT context; |
| |
| static const WCHAR managed[] = { |
| 'M','a','n','a','g','e','d','L','o','c','a','l','P','a','c','k','a','g','e',0}; |
| static const WCHAR local[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; |
| |
| TRACE("%s %p\n", debugstr_w(szProduct), package); |
| |
| r = msi_locate_product(szProduct, &context); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| r = MSIREG_OpenInstallProps(szProduct, context, NULL, &props, FALSE); |
| if (r != ERROR_SUCCESS) |
| return ERROR_UNKNOWN_PRODUCT; |
| |
| if (context == MSIINSTALLCONTEXT_USERMANAGED) |
| path = msi_reg_get_val_str(props, managed); |
| else |
| path = msi_reg_get_val_str(props, local); |
| |
| r = ERROR_UNKNOWN_PRODUCT; |
| |
| if (!path || GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) |
| goto done; |
| |
| if (PathIsRelativeW(path)) |
| { |
| r = ERROR_INSTALL_PACKAGE_OPEN_FAILED; |
| goto done; |
| } |
| |
| r = MSI_OpenPackageW(path, package); |
| |
| done: |
| RegCloseKey(props); |
| msi_free(path); |
| return r; |
| } |
| |
| UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct) |
| { |
| MSIPACKAGE *package = NULL; |
| WCHAR squished_pc[GUID_SIZE]; |
| UINT r; |
| |
| if (!szProduct || !squash_guid(szProduct, squished_pc)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!phProduct) |
| return ERROR_INVALID_PARAMETER; |
| |
| r = MSI_OpenProductW(szProduct, &package); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| *phProduct = alloc_msihandle(&package->hdr); |
| if (!*phProduct) |
| r = ERROR_NOT_ENOUGH_MEMORY; |
| |
| msiobj_release(&package->hdr); |
| return r; |
| } |
| |
| UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, |
| LPCSTR szTransforms, LANGID lgidLanguage) |
| { |
| FIXME("%s %s %s %08x\n",debugstr_a(szPackagePath), |
| debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiAdvertiseProductW(LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, |
| LPCWSTR szTransforms, LANGID lgidLanguage) |
| { |
| FIXME("%s %s %s %08x\n",debugstr_w(szPackagePath), |
| debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiAdvertiseProductExA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, |
| LPCSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) |
| { |
| FIXME("%s %s %s %08x %08x %08x\n", debugstr_a(szPackagePath), |
| debugstr_a(szScriptfilePath), debugstr_a(szTransforms), |
| lgidLanguage, dwPlatform, dwOptions); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiAdvertiseProductExW( LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, |
| LPCWSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) |
| { |
| FIXME("%s %s %s %08x %08x %08x\n", debugstr_w(szPackagePath), |
| debugstr_w(szScriptfilePath), debugstr_w(szTransforms), |
| lgidLanguage, dwPlatform, dwOptions); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine) |
| { |
| LPWSTR szwPath = NULL, szwCommand = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| |
| TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine)); |
| |
| if( szPackagePath ) |
| { |
| szwPath = strdupAtoW( szPackagePath ); |
| if( !szwPath ) |
| goto end; |
| } |
| |
| if( szCommandLine ) |
| { |
| szwCommand = strdupAtoW( szCommandLine ); |
| if( !szwCommand ) |
| goto end; |
| } |
| |
| r = MsiInstallProductW( szwPath, szwCommand ); |
| |
| end: |
| msi_free( szwPath ); |
| msi_free( szwCommand ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine) |
| { |
| MSIPACKAGE *package = NULL; |
| UINT r; |
| |
| TRACE("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine)); |
| |
| if (!szPackagePath) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!*szPackagePath) |
| return ERROR_PATH_NOT_FOUND; |
| |
| r = MSI_OpenPackageW( szPackagePath, &package ); |
| if (r == ERROR_SUCCESS) |
| { |
| r = MSI_InstallPackage( package, szPackagePath, szCommandLine ); |
| msiobj_release( &package->hdr ); |
| } |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode) |
| { |
| LPWSTR wszProduct; |
| UINT rc; |
| |
| TRACE("%s %08x\n", debugstr_a(szProduct), dwReinstallMode); |
| |
| wszProduct = strdupAtoW(szProduct); |
| |
| rc = MsiReinstallProductW(wszProduct, dwReinstallMode); |
| |
| msi_free(wszProduct); |
| return rc; |
| } |
| |
| UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode) |
| { |
| TRACE("%s %08x\n", debugstr_w(szProduct), dwReinstallMode); |
| |
| return MsiReinstallFeatureW(szProduct, szAll, dwReinstallMode); |
| } |
| |
| UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage, |
| INSTALLTYPE eInstallType, LPCSTR szCommandLine) |
| { |
| LPWSTR patch_package = NULL; |
| LPWSTR install_package = NULL; |
| LPWSTR command_line = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| |
| TRACE("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage), |
| eInstallType, debugstr_a(szCommandLine)); |
| |
| if (szPatchPackage && !(patch_package = strdupAtoW(szPatchPackage))) |
| goto done; |
| |
| if (szInstallPackage && !(install_package = strdupAtoW(szInstallPackage))) |
| goto done; |
| |
| if (szCommandLine && !(command_line = strdupAtoW(szCommandLine))) |
| goto done; |
| |
| r = MsiApplyPatchW(patch_package, install_package, eInstallType, command_line); |
| |
| done: |
| msi_free(patch_package); |
| msi_free(install_package); |
| msi_free(command_line); |
| |
| return r; |
| } |
| |
| static UINT MSI_ApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szProductCode, LPCWSTR szCommandLine) |
| { |
| MSIHANDLE patch = 0, info = 0; |
| UINT r = ERROR_SUCCESS, type; |
| DWORD size = 0; |
| LPCWSTR cmd_ptr = szCommandLine; |
| LPWSTR beg, end, cmd = NULL, codes = NULL; |
| BOOL succeeded = FALSE; |
| |
| static const WCHAR patcheq[] = {'P','A','T','C','H','=',0}; |
| static WCHAR empty[] = {0}; |
| |
| if (!szPatchPackage || !szPatchPackage[0]) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szProductCode) |
| { |
| r = MsiOpenDatabaseW(szPatchPackage, MSIDBOPEN_READONLY, &patch); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| r = MsiGetSummaryInformationW(patch, NULL, 0, &info); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| r = MsiSummaryInfoGetPropertyW(info, PID_TEMPLATE, &type, NULL, NULL, empty, &size); |
| if (r != ERROR_MORE_DATA || !size || type != VT_LPSTR) |
| { |
| ERR("Failed to read product codes from patch\n"); |
| goto done; |
| } |
| |
| codes = msi_alloc(++size * sizeof(WCHAR)); |
| if (!codes) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| r = MsiSummaryInfoGetPropertyW(info, PID_TEMPLATE, &type, NULL, NULL, codes, &size); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| } |
| |
| if (!szCommandLine) |
| cmd_ptr = empty; |
| |
| size = lstrlenW(cmd_ptr) + lstrlenW(patcheq) + lstrlenW(szPatchPackage) + 1; |
| cmd = msi_alloc(size * sizeof(WCHAR)); |
| if (!cmd) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| lstrcpyW(cmd, cmd_ptr); |
| if (szCommandLine) lstrcatW(cmd, szSpace); |
| lstrcatW(cmd, patcheq); |
| lstrcatW(cmd, szPatchPackage); |
| |
| if (szProductCode) |
| r = MsiConfigureProductExW(szProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, cmd); |
| else |
| { |
| beg = codes; |
| while ((end = strchrW(beg, '}'))) |
| { |
| *(end + 1) = '\0'; |
| r = MsiConfigureProductExW(beg, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, cmd); |
| if (r == ERROR_SUCCESS) |
| { |
| TRACE("patch applied\n"); |
| succeeded = TRUE; |
| } |
| beg = end + 2; |
| } |
| |
| if (succeeded) |
| r = ERROR_SUCCESS; |
| } |
| |
| done: |
| msi_free(cmd); |
| msi_free(codes); |
| |
| MsiCloseHandle(info); |
| MsiCloseHandle(patch); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage, |
| INSTALLTYPE eInstallType, LPCWSTR szCommandLine) |
| { |
| TRACE("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage), |
| eInstallType, debugstr_w(szCommandLine)); |
| |
| if (szInstallPackage || eInstallType == INSTALLTYPE_NETWORK_IMAGE || |
| eInstallType == INSTALLTYPE_SINGLE_INSTANCE) |
| { |
| FIXME("Only reading target products from patch\n"); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| return MSI_ApplyPatchW(szPatchPackage, NULL, szCommandLine); |
| } |
| |
| UINT WINAPI MsiApplyMultiplePatchesA(LPCSTR szPatchPackages, |
| LPCSTR szProductCode, LPCSTR szPropertiesList) |
| { |
| LPWSTR patch_packages = NULL; |
| LPWSTR product_code = NULL; |
| LPWSTR properties_list = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| |
| TRACE("%s %s %s\n", debugstr_a(szPatchPackages), debugstr_a(szProductCode), |
| debugstr_a(szPropertiesList)); |
| |
| if (!szPatchPackages || !szPatchPackages[0]) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!(patch_packages = strdupAtoW(szPatchPackages))) |
| return ERROR_OUTOFMEMORY; |
| |
| if (szProductCode && !(product_code = strdupAtoW(szProductCode))) |
| goto done; |
| |
| if (szPropertiesList && !(properties_list = strdupAtoW(szPropertiesList))) |
| goto done; |
| |
| r = MsiApplyMultiplePatchesW(patch_packages, product_code, properties_list); |
| |
| done: |
| msi_free(patch_packages); |
| msi_free(product_code); |
| msi_free(properties_list); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiApplyMultiplePatchesW(LPCWSTR szPatchPackages, |
| LPCWSTR szProductCode, LPCWSTR szPropertiesList) |
| { |
| UINT r = ERROR_SUCCESS; |
| LPCWSTR beg, end; |
| |
| TRACE("%s %s %s\n", debugstr_w(szPatchPackages), debugstr_w(szProductCode), |
| debugstr_w(szPropertiesList)); |
| |
| if (!szPatchPackages || !szPatchPackages[0]) |
| return ERROR_INVALID_PARAMETER; |
| |
| beg = end = szPatchPackages; |
| while (*beg) |
| { |
| DWORD len; |
| LPWSTR patch; |
| |
| while (*beg == ' ') beg++; |
| while (*end && *end != ';') end++; |
| |
| len = end - beg; |
| while (len && beg[len - 1] == ' ') len--; |
| |
| if (!len) return ERROR_INVALID_NAME; |
| |
| patch = msi_alloc((len + 1) * sizeof(WCHAR)); |
| if (!patch) |
| return ERROR_OUTOFMEMORY; |
| |
| memcpy(patch, beg, len * sizeof(WCHAR)); |
| patch[len] = '\0'; |
| |
| r = MSI_ApplyPatchW(patch, szProductCode, szPropertiesList); |
| msi_free(patch); |
| |
| if (r != ERROR_SUCCESS) |
| break; |
| |
| beg = ++end; |
| } |
| return r; |
| } |
| |
| UINT WINAPI MsiDetermineApplicablePatchesA(LPCSTR szProductPackagePath, |
| DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOA pPatchInfo) |
| { |
| UINT i, r; |
| WCHAR *package_path = NULL; |
| MSIPATCHSEQUENCEINFOW *psi; |
| |
| TRACE("(%s, %d, %p)\n", debugstr_a(szProductPackagePath), cPatchInfo, pPatchInfo); |
| |
| if (szProductPackagePath && !(package_path = strdupAtoW( szProductPackagePath ))) |
| return ERROR_OUTOFMEMORY; |
| |
| psi = msi_alloc( cPatchInfo * sizeof(*psi) ); |
| if (!psi) |
| { |
| msi_free( package_path ); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| for (i = 0; i < cPatchInfo; i++) |
| { |
| psi[i].szPatchData = strdupAtoW( pPatchInfo[i].szPatchData ); |
| psi[i].ePatchDataType = pPatchInfo[i].ePatchDataType; |
| } |
| |
| r = MsiDetermineApplicablePatchesW( package_path, cPatchInfo, psi ); |
| if (r == ERROR_SUCCESS) |
| { |
| for (i = 0; i < cPatchInfo; i++) |
| { |
| pPatchInfo[i].dwOrder = psi[i].dwOrder; |
| pPatchInfo[i].uStatus = psi[i].uStatus; |
| } |
| } |
| |
| msi_free( package_path ); |
| for (i = 0; i < cPatchInfo; i++) |
| msi_free( (WCHAR *)psi[i].szPatchData ); |
| msi_free( psi ); |
| return r; |
| } |
| |
| static UINT MSI_ApplicablePatchW( MSIPACKAGE *package, LPCWSTR patch ) |
| { |
| MSISUMMARYINFO *si; |
| MSIDATABASE *patch_db; |
| UINT r = ERROR_SUCCESS; |
| |
| r = MSI_OpenDatabaseW( patch, MSIDBOPEN_READONLY, &patch_db ); |
| if (r != ERROR_SUCCESS) |
| { |
| WARN("failed to open patch file %s\n", debugstr_w(patch)); |
| return r; |
| } |
| |
| si = MSI_GetSummaryInformationW( patch_db->storage, 0 ); |
| if (!si) |
| { |
| r = ERROR_FUNCTION_FAILED; |
| goto done; |
| } |
| |
| r = msi_check_patch_applicable( package, si ); |
| if (r != ERROR_SUCCESS) |
| TRACE("patch not applicable\n"); |
| |
| done: |
| msiobj_release( &patch_db->hdr ); |
| msiobj_release( &si->hdr ); |
| return r; |
| } |
| |
| UINT WINAPI MsiDetermineApplicablePatchesW(LPCWSTR szProductPackagePath, |
| DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOW pPatchInfo) |
| { |
| UINT i, r, ret = ERROR_FUNCTION_FAILED; |
| MSIPACKAGE *package; |
| |
| TRACE("(%s, %d, %p)\n", debugstr_w(szProductPackagePath), cPatchInfo, pPatchInfo); |
| |
| r = MSI_OpenPackageW( szProductPackagePath, &package ); |
| if (r != ERROR_SUCCESS) |
| { |
| ERR("failed to open package %u\n", r); |
| return r; |
| } |
| |
| for (i = 0; i < cPatchInfo; i++) |
| { |
| switch (pPatchInfo[i].ePatchDataType) |
| { |
| case MSIPATCH_DATATYPE_PATCHFILE: |
| { |
| FIXME("patch ordering not supported\n"); |
| r = MSI_ApplicablePatchW( package, pPatchInfo[i].szPatchData ); |
| if (r != ERROR_SUCCESS) |
| { |
| pPatchInfo[i].dwOrder = ~0u; |
| pPatchInfo[i].uStatus = ERROR_PATCH_TARGET_NOT_FOUND; |
| } |
| else |
| { |
| pPatchInfo[i].dwOrder = i; |
| pPatchInfo[i].uStatus = ret = ERROR_SUCCESS; |
| } |
| break; |
| } |
| default: |
| { |
| FIXME("patch data type %u not supported\n", pPatchInfo[i].ePatchDataType); |
| pPatchInfo[i].dwOrder = ~0u; |
| pPatchInfo[i].uStatus = ERROR_PATCH_TARGET_NOT_FOUND; |
| break; |
| } |
| } |
| |
| TRACE(" szPatchData: %s\n", debugstr_w(pPatchInfo[i].szPatchData)); |
| TRACE("ePatchDataType: %u\n", pPatchInfo[i].ePatchDataType); |
| TRACE(" dwOrder: %u\n", pPatchInfo[i].dwOrder); |
| TRACE(" uStatus: %u\n", pPatchInfo[i].uStatus); |
| } |
| return ret; |
| } |
| |
| UINT WINAPI MsiDeterminePatchSequenceA(LPCSTR szProductCode, LPCSTR szUserSid, |
| MSIINSTALLCONTEXT dwContext, DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOA pPatchInfo) |
| { |
| FIXME("(%s, %s, %d, %d, %p): stub!\n", debugstr_a(szProductCode), |
| debugstr_a(szUserSid), dwContext, cPatchInfo, pPatchInfo); |
| |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiDeterminePatchSequenceW(LPCWSTR szProductCode, LPCWSTR szUserSid, |
| MSIINSTALLCONTEXT dwContext, DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOW pPatchInfo) |
| { |
| FIXME("(%s, %s, %d, %d, %p): stub!\n", debugstr_w(szProductCode), |
| debugstr_w(szUserSid), dwContext, cPatchInfo, pPatchInfo); |
| |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| static UINT msi_open_package(LPCWSTR product, MSIINSTALLCONTEXT context, |
| MSIPACKAGE **package) |
| { |
| UINT r; |
| DWORD sz; |
| HKEY props; |
| LPWSTR localpack; |
| WCHAR sourcepath[MAX_PATH]; |
| WCHAR filename[MAX_PATH]; |
| |
| r = MSIREG_OpenInstallProps(product, context, NULL, &props, FALSE); |
| if (r != ERROR_SUCCESS) |
| return ERROR_BAD_CONFIGURATION; |
| |
| localpack = msi_reg_get_val_str(props, szLocalPackage); |
| if (localpack) |
| { |
| lstrcpyW(sourcepath, localpack); |
| msi_free(localpack); |
| } |
| |
| if (!localpack || GetFileAttributesW(sourcepath) == INVALID_FILE_ATTRIBUTES) |
| { |
| sz = sizeof(sourcepath); |
| MsiSourceListGetInfoW(product, NULL, context, MSICODE_PRODUCT, |
| INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); |
| |
| sz = sizeof(filename); |
| MsiSourceListGetInfoW(product, NULL, context, MSICODE_PRODUCT, |
| INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); |
| |
| lstrcatW(sourcepath, filename); |
| } |
| |
| if (GetFileAttributesW(sourcepath) == INVALID_FILE_ATTRIBUTES) |
| return ERROR_INSTALL_SOURCE_ABSENT; |
| |
| return MSI_OpenPackageW(sourcepath, package); |
| } |
| |
| UINT WINAPI MsiConfigureProductExW(LPCWSTR szProduct, int iInstallLevel, |
| INSTALLSTATE eInstallState, LPCWSTR szCommandLine) |
| { |
| MSIPACKAGE* package = NULL; |
| MSIINSTALLCONTEXT context; |
| UINT r; |
| DWORD sz; |
| WCHAR sourcepath[MAX_PATH], filename[MAX_PATH]; |
| LPWSTR commandline; |
| |
| static const WCHAR szInstalled[] = { |
| ' ','I','n','s','t','a','l','l','e','d','=','1',0}; |
| static const WCHAR szRemoveAll[] = { |
| ' ','R','E','M','O','V','E','=','A','L','L',0}; |
| static const WCHAR szMachine[] = { |
| ' ','A','L','L','U','S','E','R','S','=','1',0}; |
| |
| TRACE("%s %d %d %s\n",debugstr_w(szProduct), iInstallLevel, eInstallState, |
| debugstr_w(szCommandLine)); |
| |
| if (!szProduct || lstrlenW(szProduct) != GUID_SIZE - 1) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (eInstallState == INSTALLSTATE_ADVERTISED || |
| eInstallState == INSTALLSTATE_SOURCE) |
| { |
| FIXME("State %d not implemented\n", eInstallState); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| r = msi_locate_product(szProduct, &context); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| r = msi_open_package(szProduct, context, &package); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| sz = lstrlenW(szInstalled) + 1; |
| |
| if (szCommandLine) |
| sz += lstrlenW(szCommandLine); |
| |
| if (eInstallState == INSTALLSTATE_ABSENT) |
| sz += lstrlenW(szRemoveAll); |
| |
| if (context == MSIINSTALLCONTEXT_MACHINE) |
| sz += lstrlenW(szMachine); |
| |
| commandline = msi_alloc(sz * sizeof(WCHAR)); |
| if (!commandline) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto end; |
| } |
| |
| commandline[0] = 0; |
| if (szCommandLine) |
| lstrcpyW(commandline,szCommandLine); |
| |
| if (eInstallState == INSTALLSTATE_ABSENT) |
| lstrcatW(commandline, szRemoveAll); |
| |
| if (context == MSIINSTALLCONTEXT_MACHINE) |
| lstrcatW(commandline, szMachine); |
| |
| sz = sizeof(sourcepath); |
| MsiSourceListGetInfoW(szProduct, NULL, context, MSICODE_PRODUCT, |
| INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); |
| |
| sz = sizeof(filename); |
| MsiSourceListGetInfoW(szProduct, NULL, context, MSICODE_PRODUCT, |
| INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); |
| |
| strcatW(sourcepath, filename); |
| |
| r = MSI_InstallPackage( package, sourcepath, commandline ); |
| |
| msi_free(commandline); |
| |
| end: |
| msiobj_release( &package->hdr ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiConfigureProductExA(LPCSTR szProduct, int iInstallLevel, |
| INSTALLSTATE eInstallState, LPCSTR szCommandLine) |
| { |
| LPWSTR szwProduct = NULL; |
| LPWSTR szwCommandLine = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| |
| if( szProduct ) |
| { |
| szwProduct = strdupAtoW( szProduct ); |
| if( !szwProduct ) |
| goto end; |
| } |
| |
| if( szCommandLine) |
| { |
| szwCommandLine = strdupAtoW( szCommandLine ); |
| if( !szwCommandLine) |
| goto end; |
| } |
| |
| r = MsiConfigureProductExW( szwProduct, iInstallLevel, eInstallState, |
| szwCommandLine ); |
| end: |
| msi_free( szwProduct ); |
| msi_free( szwCommandLine); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, |
| INSTALLSTATE eInstallState) |
| { |
| LPWSTR szwProduct = NULL; |
| UINT r; |
| |
| TRACE("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState); |
| |
| if( szProduct ) |
| { |
| szwProduct = strdupAtoW( szProduct ); |
| if( !szwProduct ) |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| r = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState ); |
| msi_free( szwProduct ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiConfigureProductW(LPCWSTR szProduct, int iInstallLevel, |
| INSTALLSTATE eInstallState) |
| { |
| return MsiConfigureProductExW(szProduct, iInstallLevel, eInstallState, NULL); |
| } |
| |
| UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer) |
| { |
| LPWSTR szwComponent = NULL; |
| UINT r; |
| WCHAR szwBuffer[GUID_SIZE]; |
| |
| TRACE("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer)); |
| |
| if( szComponent ) |
| { |
| szwComponent = strdupAtoW( szComponent ); |
| if( !szwComponent ) |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| *szwBuffer = '\0'; |
| r = MsiGetProductCodeW( szwComponent, szwBuffer ); |
| |
| if(*szwBuffer) |
| WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL); |
| |
| msi_free( szwComponent ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer) |
| { |
| UINT rc, index; |
| HKEY compkey, prodkey; |
| WCHAR squished_comp[GUID_SIZE]; |
| WCHAR squished_prod[GUID_SIZE]; |
| DWORD sz = GUID_SIZE; |
| |
| TRACE("%s %p\n", debugstr_w(szComponent), szBuffer); |
| |
| if (!szComponent || !*szComponent) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!squash_guid(szComponent, squished_comp)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (MSIREG_OpenUserDataComponentKey(szComponent, NULL, &compkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &compkey, FALSE) != ERROR_SUCCESS) |
| { |
| return ERROR_UNKNOWN_COMPONENT; |
| } |
| |
| rc = RegEnumValueW(compkey, 0, squished_prod, &sz, NULL, NULL, NULL, NULL); |
| if (rc != ERROR_SUCCESS) |
| { |
| RegCloseKey(compkey); |
| return ERROR_UNKNOWN_COMPONENT; |
| } |
| |
| /* check simple case, only one product */ |
| rc = RegEnumValueW(compkey, 1, squished_prod, &sz, NULL, NULL, NULL, NULL); |
| if (rc == ERROR_NO_MORE_ITEMS) |
| { |
| rc = ERROR_SUCCESS; |
| goto done; |
| } |
| |
| index = 0; |
| while ((rc = RegEnumValueW(compkey, index, squished_prod, &sz, |
| NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS) |
| { |
| index++; |
| sz = GUID_SIZE; |
| unsquash_guid(squished_prod, szBuffer); |
| |
| if (MSIREG_OpenProductKey(szBuffer, NULL, |
| MSIINSTALLCONTEXT_USERMANAGED, |
| &prodkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenProductKey(szBuffer, NULL, |
| MSIINSTALLCONTEXT_USERUNMANAGED, |
| &prodkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenProductKey(szBuffer, NULL, |
| MSIINSTALLCONTEXT_MACHINE, |
| &prodkey, FALSE) == ERROR_SUCCESS) |
| { |
| RegCloseKey(prodkey); |
| rc = ERROR_SUCCESS; |
| goto done; |
| } |
| } |
| |
| rc = ERROR_INSTALL_FAILURE; |
| |
| done: |
| RegCloseKey(compkey); |
| unsquash_guid(squished_prod, szBuffer); |
| return rc; |
| } |
| |
| static LPWSTR msi_reg_get_value(HKEY hkey, LPCWSTR name, DWORD *type) |
| { |
| DWORD dval; |
| LONG res; |
| WCHAR temp[20]; |
| |
| static const WCHAR format[] = {'%','d',0}; |
| |
| res = RegQueryValueExW(hkey, name, NULL, type, NULL, NULL); |
| if (res != ERROR_SUCCESS) |
| return NULL; |
| |
| if (*type == REG_SZ) |
| return msi_reg_get_val_str(hkey, name); |
| |
| if (!msi_reg_get_val_dword(hkey, name, &dval)) |
| return NULL; |
| |
| sprintfW(temp, format, dval); |
| return strdupW(temp); |
| } |
| |
| static UINT MSI_GetProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, |
| awstring *szValue, LPDWORD pcchValueBuf) |
| { |
| MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_USERUNMANAGED; |
| UINT r = ERROR_UNKNOWN_PROPERTY; |
| HKEY prodkey, userdata, source; |
| LPWSTR val = NULL; |
| WCHAR squished_pc[GUID_SIZE]; |
| WCHAR packagecode[GUID_SIZE]; |
| BOOL badconfig = FALSE; |
| LONG res; |
| DWORD type = REG_NONE; |
| |
| static WCHAR empty[] = {0}; |
| static const WCHAR sourcelist[] = { |
| 'S','o','u','r','c','e','L','i','s','t',0}; |
| static const WCHAR display_name[] = { |
| 'D','i','s','p','l','a','y','N','a','m','e',0}; |
| static const WCHAR display_version[] = { |
| 'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; |
| static const WCHAR assignment[] = { |
| 'A','s','s','i','g','n','m','e','n','t',0}; |
| |
| TRACE("%s %s %p %p\n", debugstr_w(szProduct), |
| debugstr_w(szAttribute), szValue, pcchValueBuf); |
| |
| if ((szValue->str.w && !pcchValueBuf) || !szProduct || !szAttribute) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!squash_guid(szProduct, squished_pc)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if ((r = MSIREG_OpenProductKey(szProduct, NULL, |
| MSIINSTALLCONTEXT_USERMANAGED, |
| &prodkey, FALSE)) != ERROR_SUCCESS && |
| (r = MSIREG_OpenProductKey(szProduct, NULL, |
| MSIINSTALLCONTEXT_USERUNMANAGED, |
| &prodkey, FALSE)) != ERROR_SUCCESS && |
| (r = MSIREG_OpenProductKey(szProduct, NULL, |
| MSIINSTALLCONTEXT_MACHINE, |
| &prodkey, FALSE)) == ERROR_SUCCESS) |
| { |
| context = MSIINSTALLCONTEXT_MACHINE; |
| } |
| |
| MSIREG_OpenInstallProps(szProduct, context, NULL, &userdata, FALSE); |
| |
| if (!lstrcmpW(szAttribute, INSTALLPROPERTY_HELPLINKW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_HELPTELEPHONEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_INSTALLDATEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_INSTALLLOCATIONW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_INSTALLSOURCEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_LOCALPACKAGEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PUBLISHERW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_URLINFOABOUTW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_URLUPDATEINFOW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_VERSIONMINORW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_VERSIONMAJORW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_VERSIONSTRINGW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PRODUCTIDW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_REGCOMPANYW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_REGOWNERW)) |
| { |
| if (!prodkey) |
| { |
| r = ERROR_UNKNOWN_PRODUCT; |
| goto done; |
| } |
| |
| if (!userdata) |
| return ERROR_UNKNOWN_PROPERTY; |
| |
| if (!lstrcmpW(szAttribute, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW)) |
| szAttribute = display_name; |
| else if (!lstrcmpW(szAttribute, INSTALLPROPERTY_VERSIONSTRINGW)) |
| szAttribute = display_version; |
| |
| val = msi_reg_get_value(userdata, szAttribute, &type); |
| if (!val) |
| val = empty; |
| } |
| else if (!lstrcmpW(szAttribute, INSTALLPROPERTY_INSTANCETYPEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_TRANSFORMSW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_LANGUAGEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PRODUCTNAMEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PACKAGECODEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_VERSIONW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PRODUCTICONW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PACKAGENAMEW) || |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_AUTHORIZED_LUA_APPW)) |
| { |
| if (!prodkey) |
| { |
| r = ERROR_UNKNOWN_PRODUCT; |
| goto done; |
| } |
| |
| if (!lstrcmpW(szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW)) |
| szAttribute = assignment; |
| |
| if (!lstrcmpW(szAttribute, INSTALLPROPERTY_PACKAGENAMEW)) |
| { |
| res = RegOpenKeyW(prodkey, sourcelist, &source); |
| if (res != ERROR_SUCCESS) |
| { |
| r = ERROR_UNKNOWN_PRODUCT; |
| goto done; |
| } |
| |
| val = msi_reg_get_value(source, szAttribute, &type); |
| if (!val) |
| val = empty; |
| |
| RegCloseKey(source); |
| } |
| else |
| { |
| val = msi_reg_get_value(prodkey, szAttribute, &type); |
| if (!val) |
| val = empty; |
| } |
| |
| if (val != empty && type != REG_DWORD && |
| !lstrcmpW(szAttribute, INSTALLPROPERTY_PACKAGECODEW)) |
| { |
| if (lstrlenW(val) != SQUISH_GUID_SIZE - 1) |
| badconfig = TRUE; |
| else |
| { |
| unsquash_guid(val, packagecode); |
| msi_free(val); |
| val = strdupW(packagecode); |
| } |
| } |
| } |
| |
| if (!val) |
| { |
| r = ERROR_UNKNOWN_PROPERTY; |
| goto done; |
| } |
| |
| if (pcchValueBuf) |
| { |
| /* If szBuffer (szValue->str) is NULL, there's no need to copy the value |
| * out. Also, *pcchValueBuf may be uninitialized in this case, so we |
| * can't rely on its value. |
| */ |
| if (szValue->str.a || szValue->str.w) |
| { |
| DWORD size = *pcchValueBuf; |
| if (strlenW(val) < size) |
| r = msi_strcpy_to_awstring(val, szValue, &size); |
| else |
| { |
| r = ERROR_MORE_DATA; |
| } |
| } |
| |
| if (!badconfig) |
| *pcchValueBuf = lstrlenW(val); |
| } |
| |
| if (badconfig) |
| r = ERROR_BAD_CONFIGURATION; |
| |
| if (val != empty) |
| msi_free(val); |
| |
| done: |
| RegCloseKey(prodkey); |
| RegCloseKey(userdata); |
| return r; |
| } |
| |
| UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, |
| LPSTR szBuffer, LPDWORD pcchValueBuf) |
| { |
| LPWSTR szwProduct, szwAttribute = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| awstring buffer; |
| |
| TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szAttribute), |
| szBuffer, pcchValueBuf); |
| |
| szwProduct = strdupAtoW( szProduct ); |
| if( szProduct && !szwProduct ) |
| goto end; |
| |
| szwAttribute = strdupAtoW( szAttribute ); |
| if( szAttribute && !szwAttribute ) |
| goto end; |
| |
| buffer.unicode = FALSE; |
| buffer.str.a = szBuffer; |
| |
| r = MSI_GetProductInfo( szwProduct, szwAttribute, |
| &buffer, pcchValueBuf ); |
| |
| end: |
| msi_free( szwProduct ); |
| msi_free( szwAttribute ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, |
| LPWSTR szBuffer, LPDWORD pcchValueBuf) |
| { |
| awstring buffer; |
| |
| TRACE("%s %s %p %p\n", debugstr_w(szProduct), debugstr_w(szAttribute), |
| szBuffer, pcchValueBuf); |
| |
| buffer.unicode = TRUE; |
| buffer.str.w = szBuffer; |
| |
| return MSI_GetProductInfo( szProduct, szAttribute, |
| &buffer, pcchValueBuf ); |
| } |
| |
| UINT WINAPI MsiGetProductInfoExA(LPCSTR szProductCode, LPCSTR szUserSid, |
| MSIINSTALLCONTEXT dwContext, LPCSTR szProperty, |
| LPSTR szValue, LPDWORD pcchValue) |
| { |
| LPWSTR product = NULL; |
| LPWSTR usersid = NULL; |
| LPWSTR property = NULL; |
| LPWSTR value = NULL; |
| DWORD len = 0; |
| UINT r; |
| |
| TRACE("(%s, %s, %d, %s, %p, %p)\n", debugstr_a(szProductCode), |
| debugstr_a(szUserSid), dwContext, debugstr_a(szProperty), |
| szValue, pcchValue); |
| |
| if (szValue && !pcchValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szProductCode) product = strdupAtoW(szProductCode); |
| if (szUserSid) usersid = strdupAtoW(szUserSid); |
| if (szProperty) property = strdupAtoW(szProperty); |
| |
| r = MsiGetProductInfoExW(product, usersid, dwContext, property, |
| NULL, &len); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| value = msi_alloc(++len * sizeof(WCHAR)); |
| if (!value) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| r = MsiGetProductInfoExW(product, usersid, dwContext, property, |
| value, &len); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| if (!pcchValue) |
| goto done; |
| |
| len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL); |
| if (*pcchValue >= len) |
| WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL); |
| else if (szValue) |
| { |
| r = ERROR_MORE_DATA; |
| if (*pcchValue > 0) |
| *szValue = '\0'; |
| } |
| |
| if (*pcchValue <= len || !szValue) |
| len = len * sizeof(WCHAR) - 1; |
| |
| *pcchValue = len - 1; |
| |
| done: |
| msi_free(product); |
| msi_free(usersid); |
| msi_free(property); |
| msi_free(value); |
| |
| return r; |
| } |
| |
| static UINT msi_copy_outval(LPWSTR val, LPWSTR out, LPDWORD size) |
| { |
| UINT r = ERROR_SUCCESS; |
| |
| if (!val) |
| return ERROR_UNKNOWN_PROPERTY; |
| |
| if (out) |
| { |
| if (strlenW(val) >= *size) |
| { |
| r = ERROR_MORE_DATA; |
| if (*size > 0) |
| *out = '\0'; |
| } |
| else |
| lstrcpyW(out, val); |
| } |
| |
| if (size) |
| *size = lstrlenW(val); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetProductInfoExW(LPCWSTR szProductCode, LPCWSTR szUserSid, |
| MSIINSTALLCONTEXT dwContext, LPCWSTR szProperty, |
| LPWSTR szValue, LPDWORD pcchValue) |
| { |
| WCHAR squished_pc[GUID_SIZE]; |
| LPWSTR val = NULL; |
| LPCWSTR package = NULL; |
| HKEY props = NULL, prod; |
| HKEY classes = NULL, managed; |
| HKEY hkey = NULL; |
| DWORD type; |
| UINT r = ERROR_UNKNOWN_PRODUCT; |
| |
| static const WCHAR five[] = {'5',0}; |
| static const WCHAR displayname[] = { |
| 'D','i','s','p','l','a','y','N','a','m','e',0}; |
| static const WCHAR displayversion[] = { |
| 'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; |
| static const WCHAR managed_local_package[] = { |
| 'M','a','n','a','g','e','d','L','o','c','a','l', |
| 'P','a','c','k','a','g','e',0}; |
| |
| TRACE("(%s, %s, %d, %s, %p, %p)\n", debugstr_w(szProductCode), |
| debugstr_w(szUserSid), dwContext, debugstr_w(szProperty), |
| szValue, pcchValue); |
| |
| if (!szProductCode || !squash_guid(szProductCode, squished_pc)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szValue && !pcchValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (dwContext != MSIINSTALLCONTEXT_USERUNMANAGED && |
| dwContext != MSIINSTALLCONTEXT_USERMANAGED && |
| dwContext != MSIINSTALLCONTEXT_MACHINE) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szProperty || !*szProperty) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) |
| return ERROR_INVALID_PARAMETER; |
| |
| /* FIXME: dwContext is provided, no need to search for it */ |
| MSIREG_OpenProductKey(szProductCode, NULL,MSIINSTALLCONTEXT_USERMANAGED, |
| &managed, FALSE); |
| MSIREG_OpenProductKey(szProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| &prod, FALSE); |
| |
| MSIREG_OpenInstallProps(szProductCode, dwContext, NULL, &props, FALSE); |
| |
| if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) |
| { |
| package = INSTALLPROPERTY_LOCALPACKAGEW; |
| |
| if (!props && !prod) |
| goto done; |
| } |
| else if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) |
| { |
| package = managed_local_package; |
| |
| if (!props && !managed) |
| goto done; |
| } |
| else if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| { |
| package = INSTALLPROPERTY_LOCALPACKAGEW; |
| MSIREG_OpenProductKey(szProductCode, NULL, dwContext, &classes, FALSE); |
| |
| if (!props && !classes) |
| goto done; |
| } |
| |
| if (!lstrcmpW(szProperty, INSTALLPROPERTY_HELPLINKW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_HELPTELEPHONEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLDATEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLLOCATIONW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLSOURCEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_LOCALPACKAGEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PUBLISHERW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_URLINFOABOUTW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_URLUPDATEINFOW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_VERSIONMINORW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_VERSIONMAJORW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_VERSIONSTRINGW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PRODUCTIDW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_REGCOMPANYW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_REGOWNERW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_INSTANCETYPEW)) |
| { |
| val = msi_reg_get_value(props, package, &type); |
| if (!val) |
| { |
| if (prod || classes) |
| r = ERROR_UNKNOWN_PROPERTY; |
| |
| goto done; |
| } |
| |
| msi_free(val); |
| |
| if (!lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW)) |
| szProperty = displayname; |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_VERSIONSTRINGW)) |
| szProperty = displayversion; |
| |
| val = msi_reg_get_value(props, szProperty, &type); |
| if (!val) |
| val = strdupW(szEmpty); |
| |
| r = msi_copy_outval(val, szValue, pcchValue); |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_TRANSFORMSW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_LANGUAGEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PRODUCTNAMEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PACKAGECODEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_VERSIONW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PRODUCTICONW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PACKAGENAMEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_AUTHORIZED_LUA_APPW)) |
| { |
| if (!prod && !classes) |
| goto done; |
| |
| if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) |
| hkey = prod; |
| else if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) |
| hkey = managed; |
| else if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| hkey = classes; |
| |
| val = msi_reg_get_value(hkey, szProperty, &type); |
| if (!val) |
| val = strdupW(szEmpty); |
| |
| r = msi_copy_outval(val, szValue, pcchValue); |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_PRODUCTSTATEW)) |
| { |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| { |
| if (props) |
| { |
| val = msi_reg_get_value(props, package, &type); |
| if (!val) |
| goto done; |
| |
| msi_free(val); |
| val = strdupW(five); |
| } |
| else |
| val = strdupW(szOne); |
| |
| r = msi_copy_outval(val, szValue, pcchValue); |
| goto done; |
| } |
| else if (props && (val = msi_reg_get_value(props, package, &type))) |
| { |
| msi_free(val); |
| val = strdupW(five); |
| r = msi_copy_outval(val, szValue, pcchValue); |
| goto done; |
| } |
| |
| if (prod || managed) |
| val = strdupW(szOne); |
| else |
| goto done; |
| |
| r = msi_copy_outval(val, szValue, pcchValue); |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_ASSIGNMENTTYPEW)) |
| { |
| if (!prod && !classes) |
| goto done; |
| |
| /* FIXME */ |
| val = strdupW(szEmpty); |
| r = msi_copy_outval(val, szValue, pcchValue); |
| } |
| else |
| r = ERROR_UNKNOWN_PROPERTY; |
| |
| done: |
| RegCloseKey(props); |
| RegCloseKey(prod); |
| RegCloseKey(managed); |
| RegCloseKey(classes); |
| msi_free(val); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetPatchInfoExA(LPCSTR szPatchCode, LPCSTR szProductCode, |
| LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, |
| LPCSTR szProperty, LPSTR lpValue, DWORD *pcchValue) |
| { |
| LPWSTR patch = NULL, product = NULL, usersid = NULL; |
| LPWSTR property = NULL, val = NULL; |
| DWORD len; |
| UINT r; |
| |
| TRACE("(%s, %s, %s, %d, %s, %p, %p)\n", debugstr_a(szPatchCode), |
| debugstr_a(szProductCode), debugstr_a(szUserSid), dwContext, |
| debugstr_a(szProperty), lpValue, pcchValue); |
| |
| if (lpValue && !pcchValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szPatchCode) patch = strdupAtoW(szPatchCode); |
| if (szProductCode) product = strdupAtoW(szProductCode); |
| if (szUserSid) usersid = strdupAtoW(szUserSid); |
| if (szProperty) property = strdupAtoW(szProperty); |
| |
| len = 0; |
| r = MsiGetPatchInfoExW(patch, product, usersid, dwContext, property, |
| NULL, &len); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| val = msi_alloc(++len * sizeof(WCHAR)); |
| if (!val) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| r = MsiGetPatchInfoExW(patch, product, usersid, dwContext, property, |
| val, &len); |
| if (r != ERROR_SUCCESS || !pcchValue) |
| goto done; |
| |
| if (lpValue) |
| WideCharToMultiByte(CP_ACP, 0, val, -1, lpValue, |
| *pcchValue - 1, NULL, NULL); |
| |
| len = lstrlenW(val); |
| if ((*val && *pcchValue < len + 1) || !lpValue) |
| { |
| if (lpValue) |
| { |
| r = ERROR_MORE_DATA; |
| lpValue[*pcchValue - 1] = '\0'; |
| } |
| |
| *pcchValue = len * sizeof(WCHAR); |
| } |
| else |
| *pcchValue = len; |
| |
| done: |
| msi_free(val); |
| msi_free(patch); |
| msi_free(product); |
| msi_free(usersid); |
| msi_free(property); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetPatchInfoExW(LPCWSTR szPatchCode, LPCWSTR szProductCode, |
| LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, |
| LPCWSTR szProperty, LPWSTR lpValue, DWORD *pcchValue) |
| { |
| WCHAR squished_pc[GUID_SIZE]; |
| WCHAR squished_patch[GUID_SIZE]; |
| HKEY udprod = 0, prod = 0, props = 0; |
| HKEY patch = 0, patches = 0; |
| HKEY udpatch = 0, datakey = 0; |
| HKEY prodpatches = 0; |
| LPWSTR val = NULL; |
| UINT r = ERROR_UNKNOWN_PRODUCT; |
| DWORD len; |
| LONG res; |
| |
| static const WCHAR szManagedPackage[] = {'M','a','n','a','g','e','d', |
| 'L','o','c','a','l','P','a','c','k','a','g','e',0}; |
| |
| TRACE("(%s, %s, %s, %d, %s, %p, %p)\n", debugstr_w(szPatchCode), |
| debugstr_w(szProductCode), debugstr_w(szUserSid), dwContext, |
| debugstr_w(szProperty), lpValue, pcchValue); |
| |
| if (!szProductCode || !squash_guid(szProductCode, squished_pc)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szPatchCode || !squash_guid(szPatchCode, squished_patch)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szProperty) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (lpValue && !pcchValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (dwContext != MSIINSTALLCONTEXT_USERMANAGED && |
| dwContext != MSIINSTALLCONTEXT_USERUNMANAGED && |
| dwContext != MSIINSTALLCONTEXT_MACHINE) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!lstrcmpW(szUserSid, szLocalSid)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (MSIREG_OpenUserDataProductKey(szProductCode, dwContext, NULL, |
| &udprod, FALSE) != ERROR_SUCCESS) |
| goto done; |
| |
| if (MSIREG_OpenInstallProps(szProductCode, dwContext, NULL, |
| &props, FALSE) != ERROR_SUCCESS) |
| goto done; |
| |
| r = ERROR_UNKNOWN_PATCH; |
| |
| res = RegOpenKeyExW(udprod, szPatches, 0, KEY_READ, &patches); |
| if (res != ERROR_SUCCESS) |
| goto done; |
| |
| res = RegOpenKeyExW(patches, squished_patch, 0, KEY_READ, &patch); |
| if (res != ERROR_SUCCESS) |
| goto done; |
| |
| if (!lstrcmpW(szProperty, INSTALLPROPERTY_TRANSFORMSW)) |
| { |
| if (MSIREG_OpenProductKey(szProductCode, NULL, dwContext, |
| &prod, FALSE) != ERROR_SUCCESS) |
| goto done; |
| |
| res = RegOpenKeyExW(prod, szPatches, 0, KEY_ALL_ACCESS, &prodpatches); |
| if (res != ERROR_SUCCESS) |
| goto done; |
| |
| datakey = prodpatches; |
| szProperty = squished_patch; |
| } |
| else |
| { |
| if (MSIREG_OpenUserDataPatchKey(szPatchCode, dwContext, |
| &udpatch, FALSE) != ERROR_SUCCESS) |
| goto done; |
| |
| if (!lstrcmpW(szProperty, INSTALLPROPERTY_LOCALPACKAGEW)) |
| { |
| if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) |
| szProperty = szManagedPackage; |
| datakey = udpatch; |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_INSTALLDATEW)) |
| { |
| datakey = patch; |
| szProperty = szInstalled; |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_LOCALPACKAGEW)) |
| { |
| datakey = udpatch; |
| } |
| else if (!lstrcmpW(szProperty, INSTALLPROPERTY_UNINSTALLABLEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_PATCHSTATEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_DISPLAYNAMEW) || |
| !lstrcmpW(szProperty, INSTALLPROPERTY_MOREINFOURLW)) |
| { |
| datakey = patch; |
| } |
| else |
| { |
| r = ERROR_UNKNOWN_PROPERTY; |
| goto done; |
| } |
| } |
| |
| val = msi_reg_get_val_str(datakey, szProperty); |
| if (!val) |
| val = strdupW(szEmpty); |
| |
| r = ERROR_SUCCESS; |
| |
| if (!pcchValue) |
| goto done; |
| |
| if (lpValue) |
| lstrcpynW(lpValue, val, *pcchValue); |
| |
| len = lstrlenW(val); |
| if ((*val && *pcchValue < len + 1) || !lpValue) |
| { |
| if (lpValue) |
| r = ERROR_MORE_DATA; |
| |
| *pcchValue = len * sizeof(WCHAR); |
| } |
| |
| *pcchValue = len; |
| |
| done: |
| msi_free(val); |
| RegCloseKey(prodpatches); |
| RegCloseKey(prod); |
| RegCloseKey(patch); |
| RegCloseKey(patches); |
| RegCloseKey(udpatch); |
| RegCloseKey(props); |
| RegCloseKey(udprod); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiGetPatchInfoA( LPCSTR patch, LPCSTR attr, LPSTR buffer, LPDWORD buflen ) |
| { |
| UINT r = ERROR_OUTOFMEMORY; |
| DWORD size; |
| LPWSTR patchW = NULL, attrW = NULL, bufferW = NULL; |
| |
| TRACE("%s %s %p %p\n", debugstr_a(patch), debugstr_a(attr), buffer, buflen); |
| |
| if (!patch || !attr) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!(patchW = strdupAtoW( patch ))) |
| goto done; |
| |
| if (!(attrW = strdupAtoW( attr ))) |
| goto done; |
| |
| size = 0; |
| r = MsiGetPatchInfoW( patchW, attrW, NULL, &size ); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| size++; |
| if (!(bufferW = msi_alloc( size * sizeof(WCHAR) ))) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| r = MsiGetPatchInfoW( patchW, attrW, bufferW, &size ); |
| if (r == ERROR_SUCCESS) |
| { |
| int len = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL ); |
| if (len > *buflen) |
| r = ERROR_MORE_DATA; |
| else if (buffer) |
| WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, *buflen, NULL, NULL ); |
| |
| *buflen = len - 1; |
| } |
| |
| done: |
| msi_free( patchW ); |
| msi_free( attrW ); |
| msi_free( bufferW ); |
| return r; |
| } |
| |
| UINT WINAPI MsiGetPatchInfoW( LPCWSTR patch, LPCWSTR attr, LPWSTR buffer, LPDWORD buflen ) |
| { |
| UINT r; |
| WCHAR product[GUID_SIZE]; |
| DWORD index; |
| |
| TRACE("%s %s %p %p\n", debugstr_w(patch), debugstr_w(attr), buffer, buflen); |
| |
| if (!patch || !attr) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (strcmpW( INSTALLPROPERTY_LOCALPACKAGEW, attr )) |
| return ERROR_UNKNOWN_PROPERTY; |
| |
| index = 0; |
| while (1) |
| { |
| r = MsiEnumProductsW( index, product ); |
| if (r != ERROR_SUCCESS) |
| break; |
| |
| r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_USERMANAGED, attr, buffer, buflen ); |
| if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) |
| return r; |
| |
| r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, attr, buffer, buflen ); |
| if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) |
| return r; |
| |
| r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_MACHINE, attr, buffer, buflen ); |
| if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) |
| return r; |
| |
| index++; |
| } |
| |
| return ERROR_UNKNOWN_PRODUCT; |
| } |
| |
| UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, DWORD attributes) |
| { |
| LPWSTR szwLogFile = NULL; |
| UINT r; |
| |
| TRACE("%08x %s %08x\n", dwLogMode, debugstr_a(szLogFile), attributes); |
| |
| if( szLogFile ) |
| { |
| szwLogFile = strdupAtoW( szLogFile ); |
| if( !szwLogFile ) |
| return ERROR_OUTOFMEMORY; |
| } |
| r = MsiEnableLogW( dwLogMode, szwLogFile, attributes ); |
| msi_free( szwLogFile ); |
| return r; |
| } |
| |
| UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes) |
| { |
| HANDLE file = INVALID_HANDLE_VALUE; |
| |
| TRACE("%08x %s %08x\n", dwLogMode, debugstr_w(szLogFile), attributes); |
| |
| if (szLogFile) |
| { |
| lstrcpyW(gszLogFile,szLogFile); |
| if (!(attributes & INSTALLLOGATTRIBUTES_APPEND)) |
| DeleteFileW(szLogFile); |
| file = CreateFileW(szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| if (file != INVALID_HANDLE_VALUE) |
| CloseHandle(file); |
| else |
| ERR("Unable to enable log %s\n",debugstr_w(szLogFile)); |
| } |
| else |
| gszLogFile[0] = '\0'; |
| |
| return ERROR_SUCCESS; |
| } |
| |
| UINT WINAPI MsiEnumComponentCostsW(MSIHANDLE hInstall, LPCWSTR szComponent, |
| DWORD dwIndex, INSTALLSTATE iState, |
| LPWSTR lpDriveBuf, DWORD *pcchDriveBuf, |
| int *piCost, int *pTempCost) |
| { |
| FIXME("(%d, %s, %d, %d, %p, %p, %p %p): stub!\n", hInstall, |
| debugstr_w(szComponent), dwIndex, iState, lpDriveBuf, |
| pcchDriveBuf, piCost, pTempCost); |
| |
| return ERROR_NO_MORE_ITEMS; |
| } |
| |
| UINT WINAPI MsiQueryComponentStateA(LPCSTR szProductCode, |
| LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, |
| LPCSTR szComponent, INSTALLSTATE *pdwState) |
| { |
| LPWSTR prodcode = NULL, usersid = NULL, comp = NULL; |
| UINT r; |
| |
| TRACE("(%s, %s, %d, %s, %p)\n", debugstr_a(szProductCode), |
| debugstr_a(szUserSid), dwContext, debugstr_a(szComponent), pdwState); |
| |
| if (szProductCode && !(prodcode = strdupAtoW(szProductCode))) |
| return ERROR_OUTOFMEMORY; |
| |
| if (szUserSid && !(usersid = strdupAtoW(szUserSid))) |
| return ERROR_OUTOFMEMORY; |
| |
| if (szComponent && !(comp = strdupAtoW(szComponent))) |
| return ERROR_OUTOFMEMORY; |
| |
| r = MsiQueryComponentStateW(prodcode, usersid, dwContext, comp, pdwState); |
| |
| msi_free(prodcode); |
| msi_free(usersid); |
| msi_free(comp); |
| |
| return r; |
| } |
| |
| static BOOL msi_comp_find_prod_key(LPCWSTR prodcode, MSIINSTALLCONTEXT context) |
| { |
| UINT r; |
| HKEY hkey; |
| |
| r = MSIREG_OpenProductKey(prodcode, NULL, context, &hkey, FALSE); |
| RegCloseKey(hkey); |
| return (r == ERROR_SUCCESS); |
| } |
| |
| static BOOL msi_comp_find_package(LPCWSTR prodcode, MSIINSTALLCONTEXT context) |
| { |
| LPCWSTR package; |
| HKEY hkey; |
| DWORD sz; |
| LONG res; |
| UINT r; |
| |
| static const WCHAR local_package[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; |
| static const WCHAR managed_local_package[] = { |
| 'M','a','n','a','g','e','d','L','o','c','a','l','P','a','c','k','a','g','e',0 |
| }; |
| |
| r = MSIREG_OpenInstallProps(prodcode, context, NULL, &hkey, FALSE); |
| if (r != ERROR_SUCCESS) |
| return FALSE; |
| |
| if (context == MSIINSTALLCONTEXT_USERMANAGED) |
| package = managed_local_package; |
| else |
| package = local_package; |
| |
| sz = 0; |
| res = RegQueryValueExW(hkey, package, NULL, NULL, NULL, &sz); |
| RegCloseKey(hkey); |
| |
| return (res == ERROR_SUCCESS); |
| } |
| |
| static BOOL msi_comp_find_prodcode(LPWSTR squished_pc, |
| MSIINSTALLCONTEXT context, |
| LPCWSTR comp, LPWSTR val, DWORD *sz) |
| { |
| HKEY hkey; |
| LONG res; |
| UINT r; |
| |
| if (context == MSIINSTALLCONTEXT_MACHINE) |
| r = MSIREG_OpenUserDataComponentKey(comp, szLocalSid, &hkey, FALSE); |
| else |
| r = MSIREG_OpenUserDataComponentKey(comp, NULL, &hkey, FALSE); |
| |
| if (r != ERROR_SUCCESS) |
| return FALSE; |
| |
| res = RegQueryValueExW(hkey, squished_pc, NULL, NULL, (BYTE *)val, sz); |
| if (res != ERROR_SUCCESS) |
| return FALSE; |
| |
| RegCloseKey(hkey); |
| return TRUE; |
| } |
| |
| UINT WINAPI MsiQueryComponentStateW(LPCWSTR szProductCode, |
| LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, |
| LPCWSTR szComponent, INSTALLSTATE *pdwState) |
| { |
| WCHAR squished_pc[GUID_SIZE]; |
| WCHAR val[MAX_PATH]; |
| BOOL found; |
| DWORD sz; |
| |
| TRACE("(%s, %s, %d, %s, %p)\n", debugstr_w(szProductCode), |
| debugstr_w(szUserSid), dwContext, debugstr_w(szComponent), pdwState); |
| |
| if (!pdwState || !szComponent) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szProductCode || !*szProductCode || lstrlenW(szProductCode) != GUID_SIZE - 1) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!squash_guid(szProductCode, squished_pc)) |
| return ERROR_INVALID_PARAMETER; |
| |
| found = msi_comp_find_prod_key(szProductCode, dwContext); |
| |
| if (!msi_comp_find_package(szProductCode, dwContext)) |
| { |
| if (found) |
| { |
| *pdwState = INSTALLSTATE_UNKNOWN; |
| return ERROR_UNKNOWN_COMPONENT; |
| } |
| |
| return ERROR_UNKNOWN_PRODUCT; |
| } |
| |
| *pdwState = INSTALLSTATE_UNKNOWN; |
| |
| sz = MAX_PATH; |
| if (!msi_comp_find_prodcode(squished_pc, dwContext, szComponent, val, &sz)) |
| return ERROR_UNKNOWN_COMPONENT; |
| |
| if (sz == 0) |
| *pdwState = INSTALLSTATE_NOTUSED; |
| else |
| { |
| if (lstrlenW(val) > 2 && |
| val[0] >= '0' && val[0] <= '9' && val[1] >= '0' && val[1] <= '9') |
| { |
| *pdwState = INSTALLSTATE_SOURCE; |
| } |
| else |
| *pdwState = INSTALLSTATE_LOCAL; |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct) |
| { |
| LPWSTR szwProduct = NULL; |
| INSTALLSTATE r; |
| |
| if( szProduct ) |
| { |
| szwProduct = strdupAtoW( szProduct ); |
| if( !szwProduct ) |
| return ERROR_OUTOFMEMORY; |
| } |
| r = MsiQueryProductStateW( szwProduct ); |
| msi_free( szwProduct ); |
| return r; |
| } |
| |
| INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct) |
| { |
| MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_USERUNMANAGED; |
| INSTALLSTATE state = INSTALLSTATE_ADVERTISED; |
| HKEY prodkey = 0, userdata = 0; |
| DWORD val; |
| UINT r; |
| |
| static const WCHAR szWindowsInstaller[] = { |
| 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0}; |
| |
| TRACE("%s\n", debugstr_w(szProduct)); |
| |
| if (!szProduct || !*szProduct) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (lstrlenW(szProduct) != GUID_SIZE - 1) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, |
| &prodkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| &prodkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, |
| &prodkey, FALSE) == ERROR_SUCCESS) |
| { |
| context = MSIINSTALLCONTEXT_MACHINE; |
| } |
| |
| r = MSIREG_OpenInstallProps(szProduct, context, NULL, &userdata, FALSE); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| if (!msi_reg_get_val_dword(userdata, szWindowsInstaller, &val)) |
| goto done; |
| |
| if (val) |
| state = INSTALLSTATE_DEFAULT; |
| else |
| state = INSTALLSTATE_UNKNOWN; |
| |
| done: |
| if (!prodkey) |
| { |
| state = INSTALLSTATE_UNKNOWN; |
| |
| if (userdata) |
| state = INSTALLSTATE_ABSENT; |
| } |
| |
| RegCloseKey(prodkey); |
| RegCloseKey(userdata); |
| return state; |
| } |
| |
| INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd) |
| { |
| INSTALLUILEVEL old = gUILevel; |
| HWND oldwnd = gUIhwnd; |
| |
| TRACE("%08x %p\n", dwUILevel, phWnd); |
| |
| gUILevel = dwUILevel; |
| if (phWnd) |
| { |
| gUIhwnd = *phWnd; |
| *phWnd = oldwnd; |
| } |
| return old; |
| } |
| |
| INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler, |
| DWORD dwMessageFilter, LPVOID pvContext) |
| { |
| INSTALLUI_HANDLERA prev = gUIHandlerA; |
| |
| TRACE("%p %08x %p\n", puiHandler, dwMessageFilter, pvContext); |
| |
| gUIHandlerA = puiHandler; |
| gUIHandlerW = NULL; |
| gUIFilter = dwMessageFilter; |
| gUIContext = pvContext; |
| |
| return prev; |
| } |
| |
| INSTALLUI_HANDLERW WINAPI MsiSetExternalUIW(INSTALLUI_HANDLERW puiHandler, |
| DWORD dwMessageFilter, LPVOID pvContext) |
| { |
| INSTALLUI_HANDLERW prev = gUIHandlerW; |
| |
| TRACE("%p %08x %p\n", puiHandler, dwMessageFilter, pvContext); |
| |
| gUIHandlerA = NULL; |
| gUIHandlerW = puiHandler; |
| gUIFilter = dwMessageFilter; |
| gUIContext = pvContext; |
| |
| return prev; |
| } |
| |
| /****************************************************************** |
| * MsiLoadStringW [MSI.@] |
| * |
| * Loads a string from MSI's string resources. |
| * |
| * PARAMS |
| * |
| * handle [I] only -1 is handled currently |
| * id [I] id of the string to be loaded |
| * lpBuffer [O] buffer for the string to be written to |
| * nBufferMax [I] maximum size of the buffer in characters |
| * lang [I] the preferred language for the string |
| * |
| * RETURNS |
| * |
| * If successful, this function returns the language id of the string loaded |
| * If the function fails, the function returns zero. |
| * |
| * NOTES |
| * |
| * The type of the first parameter is unknown. LoadString's prototype |
| * suggests that it might be a module handle. I have made it an MSI handle |
| * for starters, as -1 is an invalid MSI handle, but not an invalid module |
| * handle. Maybe strings can be stored in an MSI database somehow. |
| */ |
| LANGID WINAPI MsiLoadStringW( MSIHANDLE handle, UINT id, LPWSTR lpBuffer, |
| int nBufferMax, LANGID lang ) |
| { |
| HRSRC hres; |
| HGLOBAL hResData; |
| LPWSTR p; |
| DWORD i, len; |
| |
| TRACE("%d %u %p %d %d\n", handle, id, lpBuffer, nBufferMax, lang); |
| |
| if( handle != -1 ) |
| FIXME("don't know how to deal with handle = %08x\n", handle); |
| |
| if( !lang ) |
| lang = GetUserDefaultLangID(); |
| |
| hres = FindResourceExW( msi_hInstance, (LPCWSTR) RT_STRING, |
| (LPWSTR)1, lang ); |
| if( !hres ) |
| return 0; |
| hResData = LoadResource( msi_hInstance, hres ); |
| if( !hResData ) |
| return 0; |
| p = LockResource( hResData ); |
| if( !p ) |
| return 0; |
| |
| for (i = 0; i < (id&0xf); i++) |
| p += *p + 1; |
| len = *p; |
| |
| if( nBufferMax <= len ) |
| return 0; |
| |
| memcpy( lpBuffer, p+1, len * sizeof(WCHAR)); |
| lpBuffer[ len ] = 0; |
| |
| TRACE("found -> %s\n", debugstr_w(lpBuffer)); |
| |
| return lang; |
| } |
| |
| LANGID WINAPI MsiLoadStringA( MSIHANDLE handle, UINT id, LPSTR lpBuffer, |
| int nBufferMax, LANGID lang ) |
| { |
| LPWSTR bufW; |
| LANGID r; |
| INT len; |
| |
| bufW = msi_alloc(nBufferMax*sizeof(WCHAR)); |
| r = MsiLoadStringW(handle, id, bufW, nBufferMax, lang); |
| if( r ) |
| { |
| len = WideCharToMultiByte(CP_ACP, 0, bufW, -1, NULL, 0, NULL, NULL ); |
| if( len <= nBufferMax ) |
| WideCharToMultiByte( CP_ACP, 0, bufW, -1, |
| lpBuffer, nBufferMax, NULL, NULL ); |
| else |
| r = 0; |
| } |
| msi_free(bufW); |
| return r; |
| } |
| |
| INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf, |
| LPDWORD pcchBuf) |
| { |
| char szProduct[GUID_SIZE]; |
| |
| TRACE("%s %p %p\n", debugstr_a(szComponent), lpPathBuf, pcchBuf); |
| |
| if (MsiGetProductCodeA( szComponent, szProduct ) != ERROR_SUCCESS) |
| return INSTALLSTATE_UNKNOWN; |
| |
| return MsiGetComponentPathA( szProduct, szComponent, lpPathBuf, pcchBuf ); |
| } |
| |
| INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPWSTR lpPathBuf, |
| LPDWORD pcchBuf) |
| { |
| WCHAR szProduct[GUID_SIZE]; |
| |
| TRACE("%s %p %p\n", debugstr_w(szComponent), lpPathBuf, pcchBuf); |
| |
| if (MsiGetProductCodeW( szComponent, szProduct ) != ERROR_SUCCESS) |
| return INSTALLSTATE_UNKNOWN; |
| |
| return MsiGetComponentPathW( szProduct, szComponent, lpPathBuf, pcchBuf ); |
| } |
| |
| UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, |
| WORD wLanguageId, DWORD f) |
| { |
| FIXME("%p %s %s %u %08x %08x\n", hWnd, debugstr_a(lpText), debugstr_a(lpCaption), |
| uType, wLanguageId, f); |
| return MessageBoxExA(hWnd,lpText,lpCaption,uType,wLanguageId); |
| } |
| |
| UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, |
| WORD wLanguageId, DWORD f) |
| { |
| FIXME("%p %s %s %u %08x %08x\n", hWnd, debugstr_w(lpText), debugstr_w(lpCaption), |
| uType, wLanguageId, f); |
| return MessageBoxExW(hWnd,lpText,lpCaption,uType,wLanguageId); |
| } |
| |
| UINT WINAPI MsiMessageBoxExA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, |
| DWORD unknown, WORD wLanguageId, DWORD f) |
| { |
| FIXME("(%p, %s, %s, %u, 0x%08x, 0x%08x, 0x%08x): semi-stub\n", hWnd, debugstr_a(lpText), |
| debugstr_a(lpCaption), uType, unknown, wLanguageId, f); |
| return MessageBoxExA(hWnd, lpText, lpCaption, uType, wLanguageId); |
| } |
| |
| UINT WINAPI MsiMessageBoxExW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, |
| DWORD unknown, WORD wLanguageId, DWORD f) |
| { |
| FIXME("(%p, %s, %s, %u, 0x%08x, 0x%08x, 0x%08x): semi-stub\n", hWnd, debugstr_w(lpText), |
| debugstr_w(lpCaption), uType, unknown, wLanguageId, f); |
| return MessageBoxExW(hWnd, lpText, lpCaption, uType, wLanguageId); |
| } |
| |
| UINT WINAPI MsiProvideAssemblyA( LPCSTR szAssemblyName, LPCSTR szAppContext, |
| DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf, |
| LPDWORD pcchPathBuf ) |
| { |
| FIXME("%s %s %08x %08x %p %p\n", debugstr_a(szAssemblyName), |
| debugstr_a(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, |
| pcchPathBuf); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiProvideAssemblyW( LPCWSTR szAssemblyName, LPCWSTR szAppContext, |
| DWORD dwInstallMode, DWORD dwAssemblyInfo, LPWSTR lpPathBuf, |
| LPDWORD pcchPathBuf ) |
| { |
| FIXME("%s %s %08x %08x %p %p\n", debugstr_w(szAssemblyName), |
| debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, |
| pcchPathBuf); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, |
| LPSTR szPath, LPDWORD pcchPath, LPDWORD pcchArgs ) |
| { |
| FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs ); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor, |
| LPWSTR szPath, LPDWORD pcchPath, LPDWORD pcchArgs ) |
| { |
| FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs ); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| HRESULT WINAPI MsiGetFileSignatureInformationA( LPCSTR szSignedObjectPath, |
| DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, LPBYTE pbHashData, |
| LPDWORD pcbHashData) |
| { |
| FIXME("%s %08x %p %p %p\n", debugstr_a(szSignedObjectPath), dwFlags, |
| ppcCertContext, pbHashData, pcbHashData); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| HRESULT WINAPI MsiGetFileSignatureInformationW( LPCWSTR szSignedObjectPath, |
| DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, LPBYTE pbHashData, |
| LPDWORD pcbHashData) |
| { |
| FIXME("%s %08x %p %p %p\n", debugstr_w(szSignedObjectPath), dwFlags, |
| ppcCertContext, pbHashData, pcbHashData); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| /****************************************************************** |
| * MsiGetProductPropertyA [MSI.@] |
| */ |
| UINT WINAPI MsiGetProductPropertyA(MSIHANDLE hProduct, LPCSTR szProperty, |
| LPSTR szValue, LPDWORD pccbValue) |
| { |
| LPWSTR prop = NULL, val = NULL; |
| DWORD len; |
| UINT r; |
| |
| TRACE("(%d, %s, %p, %p)\n", hProduct, debugstr_a(szProperty), |
| szValue, pccbValue); |
| |
| if (szValue && !pccbValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szProperty) prop = strdupAtoW(szProperty); |
| |
| len = 0; |
| r = MsiGetProductPropertyW(hProduct, prop, NULL, &len); |
| if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) |
| goto done; |
| |
| if (r == ERROR_SUCCESS) |
| { |
| if (szValue) *szValue = '\0'; |
| if (pccbValue) *pccbValue = 0; |
| goto done; |
| } |
| |
| val = msi_alloc(++len * sizeof(WCHAR)); |
| if (!val) |
| { |
| r = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| r = MsiGetProductPropertyW(hProduct, prop, val, &len); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| len = WideCharToMultiByte(CP_ACP, 0, val, -1, NULL, 0, NULL, NULL); |
| |
| if (szValue) |
| WideCharToMultiByte(CP_ACP, 0, val, -1, szValue, |
| *pccbValue, NULL, NULL); |
| |
| if (pccbValue) |
| { |
| if (len > *pccbValue) |
| r = ERROR_MORE_DATA; |
| |
| *pccbValue = len - 1; |
| } |
| |
| done: |
| msi_free(prop); |
| msi_free(val); |
| |
| return r; |
| } |
| |
| /****************************************************************** |
| * MsiGetProductPropertyW [MSI.@] |
| */ |
| UINT WINAPI MsiGetProductPropertyW(MSIHANDLE hProduct, LPCWSTR szProperty, |
| LPWSTR szValue, LPDWORD pccbValue) |
| { |
| MSIPACKAGE *package; |
| MSIQUERY *view = NULL; |
| MSIRECORD *rec = NULL; |
| LPCWSTR val; |
| UINT r; |
| |
| static const WCHAR query[] = { |
| 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', |
| '`','P','r','o','p','e','r','t','y','`',' ','W','H','E','R','E',' ', |
| '`','P','r','o','p','e','r','t','y','`','=','\'','%','s','\'',0}; |
| |
| TRACE("(%d, %s, %p, %p)\n", hProduct, debugstr_w(szProperty), |
| szValue, pccbValue); |
| |
| if (!szProperty) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szValue && !pccbValue) |
| return ERROR_INVALID_PARAMETER; |
| |
| package = msihandle2msiinfo(hProduct, MSIHANDLETYPE_PACKAGE); |
| if (!package) |
| return ERROR_INVALID_HANDLE; |
| |
| r = MSI_OpenQuery(package->db, &view, query, szProperty); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| r = MSI_ViewExecute(view, 0); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| r = MSI_ViewFetch(view, &rec); |
| if (r != ERROR_SUCCESS) |
| goto done; |
| |
| val = MSI_RecordGetString(rec, 2); |
| if (!val) |
| goto done; |
| |
| if (lstrlenW(val) >= *pccbValue) |
| { |
| lstrcpynW(szValue, val, *pccbValue); |
| *pccbValue = lstrlenW(val); |
| r = ERROR_MORE_DATA; |
| } |
| else |
| { |
| lstrcpyW(szValue, val); |
| *pccbValue = lstrlenW(val); |
| r = ERROR_SUCCESS; |
| } |
| |
| done: |
| if (view) |
| { |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| if (rec) msiobj_release(&rec->hdr); |
| } |
| |
| if (!rec) |
| { |
| if (szValue) *szValue = '\0'; |
| if (pccbValue) *pccbValue = 0; |
| r = ERROR_SUCCESS; |
| } |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage ) |
| { |
| UINT r; |
| LPWSTR szPack = NULL; |
| |
| TRACE("%s\n", debugstr_a(szPackage) ); |
| |
| if( szPackage ) |
| { |
| szPack = strdupAtoW( szPackage ); |
| if( !szPack ) |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| r = MsiVerifyPackageW( szPack ); |
| |
| msi_free( szPack ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiVerifyPackageW( LPCWSTR szPackage ) |
| { |
| MSIHANDLE handle; |
| UINT r; |
| |
| TRACE("%s\n", debugstr_w(szPackage) ); |
| |
| r = MsiOpenDatabaseW( szPackage, MSIDBOPEN_READONLY, &handle ); |
| MsiCloseHandle( handle ); |
| |
| return r; |
| } |
| |
| static INSTALLSTATE MSI_GetComponentPath(LPCWSTR szProduct, LPCWSTR szComponent, |
| awstring* lpPathBuf, LPDWORD pcchBuf) |
| { |
| WCHAR squished_pc[GUID_SIZE]; |
| WCHAR squished_comp[GUID_SIZE]; |
| HKEY hkey; |
| LPWSTR path = NULL; |
| INSTALLSTATE state; |
| DWORD version; |
| |
| static const WCHAR wininstaller[] = { |
| 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0}; |
| |
| TRACE("%s %s %p %p\n", debugstr_w(szProduct), |
| debugstr_w(szComponent), lpPathBuf->str.w, pcchBuf); |
| |
| if (!szProduct || !szComponent) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (lpPathBuf->str.w && !pcchBuf) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (!squash_guid(szProduct, squished_pc) || |
| !squash_guid(szComponent, squished_comp)) |
| return INSTALLSTATE_INVALIDARG; |
| |
| state = INSTALLSTATE_UNKNOWN; |
| |
| if (MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkey, FALSE) == ERROR_SUCCESS) |
| { |
| path = msi_reg_get_val_str(hkey, squished_pc); |
| RegCloseKey(hkey); |
| |
| state = INSTALLSTATE_ABSENT; |
| |
| if ((MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_MACHINE, NULL, |
| &hkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenUserDataProductKey(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, |
| NULL, &hkey, FALSE) == ERROR_SUCCESS) && |
| msi_reg_get_val_dword(hkey, wininstaller, &version) && |
| GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) |
| { |
| RegCloseKey(hkey); |
| state = INSTALLSTATE_LOCAL; |
| } |
| } |
| |
| if (state != INSTALLSTATE_LOCAL && |
| (MSIREG_OpenProductKey(szProduct, NULL, |
| MSIINSTALLCONTEXT_USERUNMANAGED, |
| &hkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, |
| &hkey, FALSE) == ERROR_SUCCESS)) |
| { |
| RegCloseKey(hkey); |
| |
| if (MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkey, FALSE) == ERROR_SUCCESS || |
| MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkey, FALSE) == ERROR_SUCCESS) |
| { |
| msi_free(path); |
| path = msi_reg_get_val_str(hkey, squished_pc); |
| RegCloseKey(hkey); |
| |
| state = INSTALLSTATE_ABSENT; |
| |
| if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) |
| state = INSTALLSTATE_LOCAL; |
| } |
| } |
| |
| if (!path) |
| return INSTALLSTATE_UNKNOWN; |
| |
| if (state == INSTALLSTATE_LOCAL && !*path) |
| state = INSTALLSTATE_NOTUSED; |
| |
| msi_strcpy_to_awstring(path, lpPathBuf, pcchBuf); |
| msi_free(path); |
| return state; |
| } |
| |
| /****************************************************************** |
| * MsiGetComponentPathW [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent, |
| LPWSTR lpPathBuf, LPDWORD pcchBuf) |
| { |
| awstring path; |
| |
| path.unicode = TRUE; |
| path.str.w = lpPathBuf; |
| |
| return MSI_GetComponentPath( szProduct, szComponent, &path, pcchBuf ); |
| } |
| |
| /****************************************************************** |
| * MsiGetComponentPathA [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent, |
| LPSTR lpPathBuf, LPDWORD pcchBuf) |
| { |
| LPWSTR szwProduct, szwComponent = NULL; |
| INSTALLSTATE r = INSTALLSTATE_UNKNOWN; |
| awstring path; |
| |
| szwProduct = strdupAtoW( szProduct ); |
| if( szProduct && !szwProduct) |
| goto end; |
| |
| szwComponent = strdupAtoW( szComponent ); |
| if( szComponent && !szwComponent ) |
| goto end; |
| |
| path.unicode = FALSE; |
| path.str.a = lpPathBuf; |
| |
| r = MSI_GetComponentPath( szwProduct, szwComponent, &path, pcchBuf ); |
| |
| end: |
| msi_free( szwProduct ); |
| msi_free( szwComponent ); |
| |
| return r; |
| } |
| |
| /****************************************************************** |
| * MsiQueryFeatureStateA [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature) |
| { |
| LPWSTR szwProduct = NULL, szwFeature= NULL; |
| INSTALLSTATE rc = INSTALLSTATE_UNKNOWN; |
| |
| szwProduct = strdupAtoW( szProduct ); |
| if ( szProduct && !szwProduct ) |
| goto end; |
| |
| szwFeature = strdupAtoW( szFeature ); |
| if ( szFeature && !szwFeature ) |
| goto end; |
| |
| rc = MsiQueryFeatureStateW(szwProduct, szwFeature); |
| |
| end: |
| msi_free( szwProduct); |
| msi_free( szwFeature); |
| |
| return rc; |
| } |
| |
| /****************************************************************** |
| * MsiQueryFeatureStateW [MSI.@] |
| * |
| * Checks the state of a feature |
| * |
| * PARAMS |
| * szProduct [I] Product's GUID string |
| * szFeature [I] Feature's GUID string |
| * |
| * RETURNS |
| * INSTALLSTATE_LOCAL Feature is installed and usable |
| * INSTALLSTATE_ABSENT Feature is absent |
| * INSTALLSTATE_ADVERTISED Feature should be installed on demand |
| * INSTALLSTATE_UNKNOWN An error occurred |
| * INSTALLSTATE_INVALIDARG One of the GUIDs was invalid |
| * |
| */ |
| INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature) |
| { |
| WCHAR squishProduct[33], comp[GUID_SIZE]; |
| GUID guid; |
| LPWSTR components, p, parent_feature, path; |
| UINT rc; |
| HKEY hkey; |
| INSTALLSTATE r; |
| BOOL missing = FALSE; |
| BOOL machine = FALSE; |
| BOOL source = FALSE; |
| |
| TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature)); |
| |
| if (!szProduct || !szFeature) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (!squash_guid( szProduct, squishProduct )) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_USERMANAGED, |
| &hkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, |
| &hkey, FALSE) != ERROR_SUCCESS) |
| { |
| rc = MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_MACHINE, |
| &hkey, FALSE); |
| if (rc != ERROR_SUCCESS) |
| return INSTALLSTATE_UNKNOWN; |
| |
| machine = TRUE; |
| } |
| |
| parent_feature = msi_reg_get_val_str( hkey, szFeature ); |
| RegCloseKey(hkey); |
| |
| if (!parent_feature) |
| return INSTALLSTATE_UNKNOWN; |
| |
| r = (parent_feature[0] == 6) ? INSTALLSTATE_ABSENT : INSTALLSTATE_LOCAL; |
| msi_free(parent_feature); |
| if (r == INSTALLSTATE_ABSENT) |
| return r; |
| |
| if (machine) |
| rc = MSIREG_OpenUserDataFeaturesKey(szProduct, |
| MSIINSTALLCONTEXT_MACHINE, |
| &hkey, FALSE); |
| else |
| rc = MSIREG_OpenUserDataFeaturesKey(szProduct, |
| MSIINSTALLCONTEXT_USERUNMANAGED, |
| &hkey, FALSE); |
| |
| if (rc != ERROR_SUCCESS) |
| return INSTALLSTATE_ADVERTISED; |
| |
| components = msi_reg_get_val_str( hkey, szFeature ); |
| RegCloseKey(hkey); |
| |
| TRACE("rc = %d buffer = %s\n", rc, debugstr_w(components)); |
| |
| if (!components) |
| return INSTALLSTATE_ADVERTISED; |
| |
| for( p = components; *p && *p != 2 ; p += 20) |
| { |
| if (!decode_base85_guid( p, &guid )) |
| { |
| if (p != components) |
| break; |
| |
| msi_free(components); |
| return INSTALLSTATE_BADCONFIG; |
| } |
| |
| StringFromGUID2(&guid, comp, GUID_SIZE); |
| |
| if (machine) |
| rc = MSIREG_OpenUserDataComponentKey(comp, szLocalSid, &hkey, FALSE); |
| else |
| rc = MSIREG_OpenUserDataComponentKey(comp, NULL, &hkey, FALSE); |
| |
| if (rc != ERROR_SUCCESS) |
| { |
| msi_free(components); |
| return INSTALLSTATE_ADVERTISED; |
| } |
| |
| path = msi_reg_get_val_str(hkey, squishProduct); |
| if (!path) |
| missing = TRUE; |
| else if (lstrlenW(path) > 2 && |
| path[0] >= '0' && path[0] <= '9' && |
| path[1] >= '0' && path[1] <= '9') |
| { |
| source = TRUE; |
| } |
| |
| msi_free(path); |
| } |
| |
| TRACE("%s %s -> %d\n", debugstr_w(szProduct), debugstr_w(szFeature), r); |
| msi_free(components); |
| |
| if (missing) |
| return INSTALLSTATE_ADVERTISED; |
| |
| if (source) |
| return INSTALLSTATE_SOURCE; |
| |
| return INSTALLSTATE_LOCAL; |
| } |
| |
| /****************************************************************** |
| * MsiGetFileVersionA [MSI.@] |
| */ |
| UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf, |
| LPDWORD pcchVersionBuf, LPSTR lpLangBuf, LPDWORD pcchLangBuf) |
| { |
| LPWSTR szwFilePath = NULL, lpwVersionBuff = NULL, lpwLangBuff = NULL; |
| UINT ret = ERROR_OUTOFMEMORY; |
| |
| if ((lpVersionBuf && !pcchVersionBuf) || |
| (lpLangBuf && !pcchLangBuf)) |
| return ERROR_INVALID_PARAMETER; |
| |
| if( szFilePath ) |
| { |
| szwFilePath = strdupAtoW( szFilePath ); |
| if( !szwFilePath ) |
| goto end; |
| } |
| |
| if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf ) |
| { |
| lpwVersionBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR)); |
| if( !lpwVersionBuff ) |
| goto end; |
| } |
| |
| if( lpLangBuf && pcchLangBuf && *pcchLangBuf ) |
| { |
| lpwLangBuff = msi_alloc(*pcchLangBuf*sizeof(WCHAR)); |
| if( !lpwLangBuff ) |
| goto end; |
| } |
| |
| ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf, |
| lpwLangBuff, pcchLangBuf); |
| |
| if( (ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && lpwVersionBuff ) |
| WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1, |
| lpVersionBuf, *pcchVersionBuf + 1, NULL, NULL); |
| if( (ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && lpwLangBuff ) |
| WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1, |
| lpLangBuf, *pcchLangBuf + 1, NULL, NULL); |
| |
| end: |
| msi_free(szwFilePath); |
| msi_free(lpwVersionBuff); |
| msi_free(lpwLangBuff); |
| |
| return ret; |
| } |
| |
| /****************************************************************** |
| * MsiGetFileVersionW [MSI.@] |
| */ |
| UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf, |
| LPDWORD pcchVersionBuf, LPWSTR lpLangBuf, LPDWORD pcchLangBuf) |
| { |
| 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 szLangFormat[] = {'%','d',0}; |
| UINT ret = 0; |
| DWORD dwVerLen, gle; |
| LPVOID lpVer = NULL; |
| VS_FIXEDFILEINFO *ffi; |
| USHORT *lang; |
| UINT puLen; |
| WCHAR tmp[32]; |
| |
| TRACE("%s %p %d %p %d\n", debugstr_w(szFilePath), |
| lpVersionBuf, pcchVersionBuf?*pcchVersionBuf:0, |
| lpLangBuf, pcchLangBuf?*pcchLangBuf:0); |
| |
| if ((lpVersionBuf && !pcchVersionBuf) || |
| (lpLangBuf && !pcchLangBuf)) |
| return ERROR_INVALID_PARAMETER; |
| |
| dwVerLen = GetFileVersionInfoSizeW(szFilePath, NULL); |
| if( !dwVerLen ) |
| { |
| gle = GetLastError(); |
| if (gle == ERROR_BAD_PATHNAME) |
| return ERROR_FILE_NOT_FOUND; |
| else if (gle == ERROR_RESOURCE_DATA_NOT_FOUND) |
| return ERROR_FILE_INVALID; |
| |
| return gle; |
| } |
| |
| lpVer = msi_alloc(dwVerLen); |
| if( !lpVer ) |
| { |
| ret = ERROR_OUTOFMEMORY; |
| goto end; |
| } |
| |
| if( !GetFileVersionInfoW(szFilePath, 0, dwVerLen, lpVer) ) |
| { |
| ret = GetLastError(); |
| goto end; |
| } |
| |
| if (pcchVersionBuf) |
| { |
| if( VerQueryValueW(lpVer, szVersionResource, (LPVOID*)&ffi, &puLen) && |
| (puLen > 0) ) |
| { |
| wsprintfW(tmp, szVersionFormat, |
| HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS), |
| HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS)); |
| if (lpVersionBuf) lstrcpynW(lpVersionBuf, tmp, *pcchVersionBuf); |
| |
| if (strlenW(tmp) >= *pcchVersionBuf) |
| ret = ERROR_MORE_DATA; |
| |
| *pcchVersionBuf = lstrlenW(tmp); |
| } |
| else |
| { |
| if (lpVersionBuf) *lpVersionBuf = 0; |
| *pcchVersionBuf = 0; |
| } |
| } |
| |
| if (pcchLangBuf) |
| { |
| if (VerQueryValueW(lpVer, szLangResource, (LPVOID*)&lang, &puLen) && |
| (puLen > 0)) |
| { |
| wsprintfW(tmp, szLangFormat, *lang); |
| if (lpLangBuf) lstrcpynW(lpLangBuf, tmp, *pcchLangBuf); |
| |
| if (strlenW(tmp) >= *pcchLangBuf) |
| ret = ERROR_MORE_DATA; |
| |
| *pcchLangBuf = lstrlenW(tmp); |
| } |
| else |
| { |
| if (lpLangBuf) *lpLangBuf = 0; |
| *pcchLangBuf = 0; |
| } |
| } |
| |
| end: |
| msi_free(lpVer); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * MsiGetFeatureUsageW [MSI.@] |
| */ |
| UINT WINAPI MsiGetFeatureUsageW( LPCWSTR szProduct, LPCWSTR szFeature, |
| LPDWORD pdwUseCount, LPWORD pwDateUsed ) |
| { |
| FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szFeature), |
| pdwUseCount, pwDateUsed); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| /*********************************************************************** |
| * MsiGetFeatureUsageA [MSI.@] |
| */ |
| UINT WINAPI MsiGetFeatureUsageA( LPCSTR szProduct, LPCSTR szFeature, |
| LPDWORD pdwUseCount, LPWORD pwDateUsed ) |
| { |
| LPWSTR prod = NULL, feat = NULL; |
| UINT ret = ERROR_OUTOFMEMORY; |
| |
| TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szFeature), |
| pdwUseCount, pwDateUsed); |
| |
| prod = strdupAtoW( szProduct ); |
| if (szProduct && !prod) |
| goto end; |
| |
| feat = strdupAtoW( szFeature ); |
| if (szFeature && !feat) |
| goto end; |
| |
| ret = MsiGetFeatureUsageW( prod, feat, pdwUseCount, pwDateUsed ); |
| |
| end: |
| msi_free( prod ); |
| msi_free( feat ); |
| |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * MsiUseFeatureExW [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiUseFeatureExW( LPCWSTR szProduct, LPCWSTR szFeature, |
| DWORD dwInstallMode, DWORD dwReserved ) |
| { |
| INSTALLSTATE state; |
| |
| TRACE("%s %s %i %i\n", debugstr_w(szProduct), debugstr_w(szFeature), |
| dwInstallMode, dwReserved); |
| |
| state = MsiQueryFeatureStateW( szProduct, szFeature ); |
| |
| if (dwReserved) |
| return INSTALLSTATE_INVALIDARG; |
| |
| if (state == INSTALLSTATE_LOCAL && dwInstallMode != INSTALLMODE_NODETECTION) |
| { |
| FIXME("mark product %s feature %s as used\n", |
| debugstr_w(szProduct), debugstr_w(szFeature) ); |
| } |
| |
| return state; |
| } |
| |
| /*********************************************************************** |
| * MsiUseFeatureExA [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiUseFeatureExA( LPCSTR szProduct, LPCSTR szFeature, |
| DWORD dwInstallMode, DWORD dwReserved ) |
| { |
| INSTALLSTATE ret = INSTALLSTATE_UNKNOWN; |
| LPWSTR prod = NULL, feat = NULL; |
| |
| TRACE("%s %s %i %i\n", debugstr_a(szProduct), debugstr_a(szFeature), |
| dwInstallMode, dwReserved); |
| |
| prod = strdupAtoW( szProduct ); |
| if (szProduct && !prod) |
| goto end; |
| |
| feat = strdupAtoW( szFeature ); |
| if (szFeature && !feat) |
| goto end; |
| |
| ret = MsiUseFeatureExW( prod, feat, dwInstallMode, dwReserved ); |
| |
| end: |
| msi_free( prod ); |
| msi_free( feat ); |
| |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * MsiUseFeatureW [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiUseFeatureW( LPCWSTR szProduct, LPCWSTR szFeature ) |
| { |
| return MsiUseFeatureExW(szProduct, szFeature, 0, 0); |
| } |
| |
| /*********************************************************************** |
| * MsiUseFeatureA [MSI.@] |
| */ |
| INSTALLSTATE WINAPI MsiUseFeatureA( LPCSTR szProduct, LPCSTR szFeature ) |
| { |
| return MsiUseFeatureExA(szProduct, szFeature, 0, 0); |
| } |
| |
| /*********************************************************************** |
| * MSI_ProvideQualifiedComponentEx [internal] |
| */ |
| static UINT MSI_ProvideQualifiedComponentEx(LPCWSTR szComponent, |
| LPCWSTR szQualifier, DWORD dwInstallMode, LPCWSTR szProduct, |
| DWORD Unused1, DWORD Unused2, awstring *lpPathBuf, |
| LPDWORD pcchPathBuf) |
| { |
| WCHAR product[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1], |
| feature[MAX_FEATURE_CHARS+1]; |
| LPWSTR info; |
| HKEY hkey; |
| DWORD sz; |
| UINT rc; |
| |
| TRACE("%s %s %i %s %i %i %p %p\n", debugstr_w(szComponent), |
| debugstr_w(szQualifier), dwInstallMode, debugstr_w(szProduct), |
| Unused1, Unused2, lpPathBuf, pcchPathBuf); |
| |
| rc = MSIREG_OpenUserComponentsKey(szComponent, &hkey, FALSE); |
| if (rc != ERROR_SUCCESS) |
| return ERROR_INDEX_ABSENT; |
| |
| info = msi_reg_get_val_str( hkey, szQualifier ); |
| RegCloseKey(hkey); |
| |
| if (!info) |
| return ERROR_INDEX_ABSENT; |
| |
| MsiDecomposeDescriptorW(info, product, feature, component, &sz); |
| |
| if (!szProduct) |
| rc = MSI_GetComponentPath(product, component, lpPathBuf, pcchPathBuf); |
| else |
| rc = MSI_GetComponentPath(szProduct, component, lpPathBuf, pcchPathBuf); |
| |
| msi_free( info ); |
| |
| if (rc != INSTALLSTATE_LOCAL) |
| return ERROR_FILE_NOT_FOUND; |
| |
| return ERROR_SUCCESS; |
| } |
| |
| /*********************************************************************** |
| * MsiProvideQualifiedComponentExW [MSI.@] |
| */ |
| UINT WINAPI MsiProvideQualifiedComponentExW(LPCWSTR szComponent, |
| LPCWSTR szQualifier, DWORD dwInstallMode, LPCWSTR szProduct, |
| DWORD Unused1, DWORD Unused2, LPWSTR lpPathBuf, |
| LPDWORD pcchPathBuf) |
| { |
| awstring path; |
| |
| path.unicode = TRUE; |
| path.str.w = lpPathBuf; |
| |
| return MSI_ProvideQualifiedComponentEx(szComponent, szQualifier, |
| dwInstallMode, szProduct, Unused1, Unused2, &path, pcchPathBuf); |
| } |
| |
| /*********************************************************************** |
| * MsiProvideQualifiedComponentExA [MSI.@] |
| */ |
| UINT WINAPI MsiProvideQualifiedComponentExA(LPCSTR szComponent, |
| LPCSTR szQualifier, DWORD dwInstallMode, LPCSTR szProduct, |
| DWORD Unused1, DWORD Unused2, LPSTR lpPathBuf, |
| LPDWORD pcchPathBuf) |
| { |
| LPWSTR szwComponent, szwQualifier = NULL, szwProduct = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| awstring path; |
| |
| TRACE("%s %s %u %s %u %u %p %p\n", debugstr_a(szComponent), |
| debugstr_a(szQualifier), dwInstallMode, debugstr_a(szProduct), |
| Unused1, Unused2, lpPathBuf, pcchPathBuf); |
| |
| szwComponent = strdupAtoW( szComponent ); |
| if (szComponent && !szwComponent) |
| goto end; |
| |
| szwQualifier = strdupAtoW( szQualifier ); |
| if (szQualifier && !szwQualifier) |
| goto end; |
| |
| szwProduct = strdupAtoW( szProduct ); |
| if (szProduct && !szwProduct) |
| goto end; |
| |
| path.unicode = FALSE; |
| path.str.a = lpPathBuf; |
| |
| r = MSI_ProvideQualifiedComponentEx(szwComponent, szwQualifier, |
| dwInstallMode, szwProduct, Unused1, |
| Unused2, &path, pcchPathBuf); |
| end: |
| msi_free(szwProduct); |
| msi_free(szwComponent); |
| msi_free(szwQualifier); |
| |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiProvideQualifiedComponentW [MSI.@] |
| */ |
| UINT WINAPI MsiProvideQualifiedComponentW( LPCWSTR szComponent, |
| LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR lpPathBuf, |
| LPDWORD pcchPathBuf) |
| { |
| return MsiProvideQualifiedComponentExW(szComponent, szQualifier, |
| dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf); |
| } |
| |
| /*********************************************************************** |
| * MsiProvideQualifiedComponentA [MSI.@] |
| */ |
| UINT WINAPI MsiProvideQualifiedComponentA( LPCSTR szComponent, |
| LPCSTR szQualifier, DWORD dwInstallMode, LPSTR lpPathBuf, |
| LPDWORD pcchPathBuf) |
| { |
| return MsiProvideQualifiedComponentExA(szComponent, szQualifier, |
| dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf); |
| } |
| |
| /*********************************************************************** |
| * MSI_GetUserInfo [internal] |
| */ |
| static USERINFOSTATE MSI_GetUserInfo(LPCWSTR szProduct, |
| awstring *lpUserNameBuf, LPDWORD pcchUserNameBuf, |
| awstring *lpOrgNameBuf, LPDWORD pcchOrgNameBuf, |
| awstring *lpSerialBuf, LPDWORD pcchSerialBuf) |
| { |
| WCHAR squished_pc[SQUISH_GUID_SIZE]; |
| LPWSTR user, org, serial; |
| USERINFOSTATE state; |
| HKEY hkey, props; |
| LPCWSTR orgptr; |
| UINT r; |
| |
| TRACE("%s %p %p %p %p %p %p\n", debugstr_w(szProduct), lpUserNameBuf, |
| pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf, |
| pcchSerialBuf); |
| |
| if (!szProduct || !squash_guid(szProduct, squished_pc)) |
| return USERINFOSTATE_INVALIDARG; |
| |
| if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, |
| &hkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| &hkey, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, |
| &hkey, FALSE) != ERROR_SUCCESS) |
| { |
| return USERINFOSTATE_UNKNOWN; |
| } |
| |
| if (MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, |
| NULL, &props, FALSE) != ERROR_SUCCESS && |
| MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_MACHINE, |
| NULL, &props, FALSE) != ERROR_SUCCESS) |
| { |
| RegCloseKey(hkey); |
| return USERINFOSTATE_ABSENT; |
| } |
| |
| user = msi_reg_get_val_str(props, INSTALLPROPERTY_REGOWNERW); |
| org = msi_reg_get_val_str(props, INSTALLPROPERTY_REGCOMPANYW); |
| serial = msi_reg_get_val_str(props, INSTALLPROPERTY_PRODUCTIDW); |
| state = USERINFOSTATE_ABSENT; |
| |
| RegCloseKey(hkey); |
| RegCloseKey(props); |
| |
| if (user && serial) |
| state = USERINFOSTATE_PRESENT; |
| |
| if (pcchUserNameBuf) |
| { |
| if (lpUserNameBuf && !user) |
| { |
| (*pcchUserNameBuf)--; |
| goto done; |
| } |
| |
| r = msi_strcpy_to_awstring(user, lpUserNameBuf, pcchUserNameBuf); |
| if (r == ERROR_MORE_DATA) |
| { |
| state = USERINFOSTATE_MOREDATA; |
| goto done; |
| } |
| } |
| |
| if (pcchOrgNameBuf) |
| { |
| orgptr = org; |
| if (!orgptr) orgptr = szEmpty; |
| |
| r = msi_strcpy_to_awstring(orgptr, lpOrgNameBuf, pcchOrgNameBuf); |
| if (r == ERROR_MORE_DATA) |
| { |
| state = USERINFOSTATE_MOREDATA; |
| goto done; |
| } |
| } |
| |
| if (pcchSerialBuf) |
| { |
| if (!serial) |
| { |
| (*pcchSerialBuf)--; |
| goto done; |
| } |
| |
| r = msi_strcpy_to_awstring(serial, lpSerialBuf, pcchSerialBuf); |
| if (r == ERROR_MORE_DATA) |
| state = USERINFOSTATE_MOREDATA; |
| } |
| |
| done: |
| msi_free(user); |
| msi_free(org); |
| msi_free(serial); |
| |
| return state; |
| } |
| |
| /*********************************************************************** |
| * MsiGetUserInfoW [MSI.@] |
| */ |
| USERINFOSTATE WINAPI MsiGetUserInfoW(LPCWSTR szProduct, |
| LPWSTR lpUserNameBuf, LPDWORD pcchUserNameBuf, |
| LPWSTR lpOrgNameBuf, LPDWORD pcchOrgNameBuf, |
| LPWSTR lpSerialBuf, LPDWORD pcchSerialBuf) |
| { |
| awstring user, org, serial; |
| |
| if ((lpUserNameBuf && !pcchUserNameBuf) || |
| (lpOrgNameBuf && !pcchOrgNameBuf) || |
| (lpSerialBuf && !pcchSerialBuf)) |
| return USERINFOSTATE_INVALIDARG; |
| |
| user.unicode = TRUE; |
| user.str.w = lpUserNameBuf; |
| org.unicode = TRUE; |
| org.str.w = lpOrgNameBuf; |
| serial.unicode = TRUE; |
| serial.str.w = lpSerialBuf; |
| |
| return MSI_GetUserInfo( szProduct, &user, pcchUserNameBuf, |
| &org, pcchOrgNameBuf, |
| &serial, pcchSerialBuf ); |
| } |
| |
| USERINFOSTATE WINAPI MsiGetUserInfoA(LPCSTR szProduct, |
| LPSTR lpUserNameBuf, LPDWORD pcchUserNameBuf, |
| LPSTR lpOrgNameBuf, LPDWORD pcchOrgNameBuf, |
| LPSTR lpSerialBuf, LPDWORD pcchSerialBuf) |
| { |
| awstring user, org, serial; |
| LPWSTR prod; |
| UINT r; |
| |
| if ((lpUserNameBuf && !pcchUserNameBuf) || |
| (lpOrgNameBuf && !pcchOrgNameBuf) || |
| (lpSerialBuf && !pcchSerialBuf)) |
| return USERINFOSTATE_INVALIDARG; |
| |
| prod = strdupAtoW( szProduct ); |
| if (szProduct && !prod) |
| return ERROR_OUTOFMEMORY; |
| |
| user.unicode = FALSE; |
| user.str.a = lpUserNameBuf; |
| org.unicode = FALSE; |
| org.str.a = lpOrgNameBuf; |
| serial.unicode = FALSE; |
| serial.str.a = lpSerialBuf; |
| |
| r = MSI_GetUserInfo( prod, &user, pcchUserNameBuf, |
| &org, pcchOrgNameBuf, |
| &serial, pcchSerialBuf ); |
| |
| msi_free( prod ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiCollectUserInfoW(LPCWSTR szProduct) |
| { |
| MSIHANDLE handle; |
| UINT rc; |
| MSIPACKAGE *package; |
| static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; |
| |
| TRACE("(%s)\n",debugstr_w(szProduct)); |
| |
| rc = MsiOpenProductW(szProduct,&handle); |
| if (rc != ERROR_SUCCESS) |
| return ERROR_INVALID_PARAMETER; |
| |
| /* MsiCollectUserInfo cannot be called from a custom action. */ |
| package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); |
| if (!package) |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| |
| rc = ACTION_PerformUIAction(package, szFirstRun, -1); |
| msiobj_release( &package->hdr ); |
| |
| MsiCloseHandle(handle); |
| |
| return rc; |
| } |
| |
| UINT WINAPI MsiCollectUserInfoA(LPCSTR szProduct) |
| { |
| MSIHANDLE handle; |
| UINT rc; |
| MSIPACKAGE *package; |
| static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; |
| |
| TRACE("(%s)\n",debugstr_a(szProduct)); |
| |
| rc = MsiOpenProductA(szProduct,&handle); |
| if (rc != ERROR_SUCCESS) |
| return ERROR_INVALID_PARAMETER; |
| |
| /* MsiCollectUserInfo cannot be called from a custom action. */ |
| package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); |
| if (!package) |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| |
| rc = ACTION_PerformUIAction(package, szFirstRun, -1); |
| msiobj_release( &package->hdr ); |
| |
| MsiCloseHandle(handle); |
| |
| return rc; |
| } |
| |
| /*********************************************************************** |
| * MsiConfigureFeatureA [MSI.@] |
| */ |
| UINT WINAPI MsiConfigureFeatureA(LPCSTR szProduct, LPCSTR szFeature, INSTALLSTATE eInstallState) |
| { |
| LPWSTR prod, feat = NULL; |
| UINT r = ERROR_OUTOFMEMORY; |
| |
| TRACE("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), eInstallState); |
| |
| prod = strdupAtoW( szProduct ); |
| if (szProduct && !prod) |
| goto end; |
| |
| feat = strdupAtoW( szFeature ); |
| if (szFeature && !feat) |
| goto end; |
| |
| r = MsiConfigureFeatureW(prod, feat, eInstallState); |
| |
| end: |
| msi_free(feat); |
| msi_free(prod); |
| |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiConfigureFeatureW [MSI.@] |
| */ |
| UINT WINAPI MsiConfigureFeatureW(LPCWSTR szProduct, LPCWSTR szFeature, INSTALLSTATE eInstallState) |
| { |
| static const WCHAR szCostInit[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 }; |
| MSIPACKAGE *package = NULL; |
| UINT r; |
| WCHAR sourcepath[MAX_PATH], filename[MAX_PATH]; |
| DWORD sz; |
| |
| TRACE("%s %s %i\n", debugstr_w(szProduct), debugstr_w(szFeature), eInstallState); |
| |
| if (!szProduct || !szFeature) |
| return ERROR_INVALID_PARAMETER; |
| |
| switch (eInstallState) |
| { |
| case INSTALLSTATE_DEFAULT: |
| /* FIXME: how do we figure out the default location? */ |
| eInstallState = INSTALLSTATE_LOCAL; |
| break; |
| case INSTALLSTATE_LOCAL: |
| case INSTALLSTATE_SOURCE: |
| case INSTALLSTATE_ABSENT: |
| case INSTALLSTATE_ADVERTISED: |
| break; |
| default: |
| return ERROR_INVALID_PARAMETER; |
| } |
| |
| r = MSI_OpenProductW( szProduct, &package ); |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| sz = sizeof(sourcepath); |
| MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); |
| |
| sz = sizeof(filename); |
| MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); |
| |
| lstrcatW( sourcepath, filename ); |
| |
| MsiSetInternalUI( INSTALLUILEVEL_BASIC, NULL ); |
| |
| r = ACTION_PerformUIAction( package, szCostInit, -1 ); |
| if (r != ERROR_SUCCESS) |
| goto end; |
| |
| r = MSI_SetFeatureStateW( package, szFeature, eInstallState); |
| if (r != ERROR_SUCCESS) |
| goto end; |
| |
| r = MSI_InstallPackage( package, sourcepath, NULL ); |
| |
| end: |
| msiobj_release( &package->hdr ); |
| |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiCreateAndVerifyInstallerDirectory [MSI.@] |
| * |
| * Notes: undocumented |
| */ |
| UINT WINAPI MsiCreateAndVerifyInstallerDirectory(DWORD dwReserved) |
| { |
| WCHAR path[MAX_PATH]; |
| |
| TRACE("%d\n", dwReserved); |
| |
| if (dwReserved) |
| { |
| FIXME("dwReserved=%d\n", dwReserved); |
| return ERROR_INVALID_PARAMETER; |
| } |
| |
| if (!GetWindowsDirectoryW(path, MAX_PATH)) |
| return ERROR_FUNCTION_FAILED; |
| |
| lstrcatW(path, installerW); |
| |
| if (!CreateDirectoryW(path, NULL)) |
| return ERROR_FUNCTION_FAILED; |
| |
| return ERROR_SUCCESS; |
| } |
| |
| /*********************************************************************** |
| * MsiGetShortcutTargetA [MSI.@] |
| */ |
| UINT WINAPI MsiGetShortcutTargetA( LPCSTR szShortcutTarget, |
| LPSTR szProductCode, LPSTR szFeatureId, |
| LPSTR szComponentCode ) |
| { |
| LPWSTR target; |
| const int len = MAX_FEATURE_CHARS+1; |
| WCHAR product[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1]; |
| UINT r; |
| |
| target = strdupAtoW( szShortcutTarget ); |
| if (szShortcutTarget && !target ) |
| return ERROR_OUTOFMEMORY; |
| product[0] = 0; |
| feature[0] = 0; |
| component[0] = 0; |
| r = MsiGetShortcutTargetW( target, product, feature, component ); |
| msi_free( target ); |
| if (r == ERROR_SUCCESS) |
| { |
| WideCharToMultiByte( CP_ACP, 0, product, -1, szProductCode, len, NULL, NULL ); |
| WideCharToMultiByte( CP_ACP, 0, feature, -1, szFeatureId, len, NULL, NULL ); |
| WideCharToMultiByte( CP_ACP, 0, component, -1, szComponentCode, len, NULL, NULL ); |
| } |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiGetShortcutTargetW [MSI.@] |
| */ |
| UINT WINAPI MsiGetShortcutTargetW( LPCWSTR szShortcutTarget, |
| LPWSTR szProductCode, LPWSTR szFeatureId, |
| LPWSTR szComponentCode ) |
| { |
| IShellLinkDataList *dl = NULL; |
| IPersistFile *pf = NULL; |
| LPEXP_DARWIN_LINK darwin = NULL; |
| HRESULT r, init; |
| |
| TRACE("%s %p %p %p\n", debugstr_w(szShortcutTarget), |
| szProductCode, szFeatureId, szComponentCode ); |
| |
| init = CoInitialize(NULL); |
| |
| r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, |
| &IID_IPersistFile, (LPVOID*) &pf ); |
| if( SUCCEEDED( r ) ) |
| { |
| r = IPersistFile_Load( pf, szShortcutTarget, |
| STGM_READ | STGM_SHARE_DENY_WRITE ); |
| if( SUCCEEDED( r ) ) |
| { |
| r = IPersistFile_QueryInterface( pf, &IID_IShellLinkDataList, |
| (LPVOID*) &dl ); |
| if( SUCCEEDED( r ) ) |
| { |
| IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, |
| (LPVOID) &darwin ); |
| IShellLinkDataList_Release( dl ); |
| } |
| } |
| IPersistFile_Release( pf ); |
| } |
| |
| if (SUCCEEDED(init)) |
| CoUninitialize(); |
| |
| TRACE("darwin = %p\n", darwin); |
| |
| if (darwin) |
| { |
| DWORD sz; |
| UINT ret; |
| |
| ret = MsiDecomposeDescriptorW( darwin->szwDarwinID, |
| szProductCode, szFeatureId, szComponentCode, &sz ); |
| LocalFree( darwin ); |
| return ret; |
| } |
| |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| UINT WINAPI MsiReinstallFeatureW( LPCWSTR szProduct, LPCWSTR szFeature, |
| DWORD dwReinstallMode ) |
| { |
| MSIPACKAGE* package = NULL; |
| UINT r; |
| WCHAR sourcepath[MAX_PATH]; |
| WCHAR filename[MAX_PATH]; |
| static const WCHAR szLogVerbose[] = { |
| ' ','L','O','G','V','E','R','B','O','S','E',0 }; |
| WCHAR reinstallmode[11]; |
| LPWSTR ptr; |
| DWORD sz; |
| |
| FIXME("%s %s %i\n", debugstr_w(szProduct), debugstr_w(szFeature), |
| dwReinstallMode); |
| |
| ptr = reinstallmode; |
| |
| if (dwReinstallMode & REINSTALLMODE_FILEMISSING) |
| *ptr++ = 'p'; |
| if (dwReinstallMode & REINSTALLMODE_FILEOLDERVERSION) |
| *ptr++ = 'o'; |
| if (dwReinstallMode & REINSTALLMODE_FILEEQUALVERSION) |
| *ptr++ = 'w'; |
| if (dwReinstallMode & REINSTALLMODE_FILEEXACT) |
| *ptr++ = 'd'; |
| if (dwReinstallMode & REINSTALLMODE_FILEVERIFY) |
| *ptr++ = 'c'; |
| if (dwReinstallMode & REINSTALLMODE_FILEREPLACE) |
| *ptr++ = 'a'; |
| if (dwReinstallMode & REINSTALLMODE_USERDATA) |
| *ptr++ = 'u'; |
| if (dwReinstallMode & REINSTALLMODE_MACHINEDATA) |
| *ptr++ = 'm'; |
| if (dwReinstallMode & REINSTALLMODE_SHORTCUT) |
| *ptr++ = 's'; |
| if (dwReinstallMode & REINSTALLMODE_PACKAGE) |
| *ptr++ = 'v'; |
| *ptr = 0; |
| |
| sz = sizeof(sourcepath); |
| MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); |
| |
| sz = sizeof(filename); |
| MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, |
| MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); |
| |
| lstrcatW( sourcepath, filename ); |
| |
| if (dwReinstallMode & REINSTALLMODE_PACKAGE) |
| r = MSI_OpenPackageW( sourcepath, &package ); |
| else |
| r = MSI_OpenProductW( szProduct, &package ); |
| |
| if (r != ERROR_SUCCESS) |
| return r; |
| |
| msi_set_property( package->db, szReinstallMode, reinstallmode ); |
| msi_set_property( package->db, szInstalled, szOne ); |
| msi_set_property( package->db, szLogVerbose, szOne ); |
| msi_set_property( package->db, szReinstall, szFeature ); |
| |
| r = MSI_InstallPackage( package, sourcepath, NULL ); |
| |
| msiobj_release( &package->hdr ); |
| |
| return r; |
| } |
| |
| UINT WINAPI MsiReinstallFeatureA( LPCSTR szProduct, LPCSTR szFeature, |
| DWORD dwReinstallMode ) |
| { |
| LPWSTR wszProduct; |
| LPWSTR wszFeature; |
| UINT rc; |
| |
| TRACE("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), |
| dwReinstallMode); |
| |
| wszProduct = strdupAtoW(szProduct); |
| wszFeature = strdupAtoW(szFeature); |
| |
| rc = MsiReinstallFeatureW(wszProduct, wszFeature, dwReinstallMode); |
| |
| msi_free(wszProduct); |
| msi_free(wszFeature); |
| return rc; |
| } |
| |
| typedef struct |
| { |
| unsigned int i[2]; |
| unsigned int buf[4]; |
| unsigned char in[64]; |
| unsigned char digest[16]; |
| } MD5_CTX; |
| |
| extern VOID WINAPI MD5Init( MD5_CTX *); |
| extern VOID WINAPI MD5Update( MD5_CTX *, const unsigned char *, unsigned int ); |
| extern VOID WINAPI MD5Final( MD5_CTX *); |
| |
| /*********************************************************************** |
| * MsiGetFileHashW [MSI.@] |
| */ |
| UINT WINAPI MsiGetFileHashW( LPCWSTR szFilePath, DWORD dwOptions, |
| PMSIFILEHASHINFO pHash ) |
| { |
| HANDLE handle, mapping; |
| void *p; |
| DWORD length; |
| UINT r = ERROR_FUNCTION_FAILED; |
| |
| TRACE("%s %08x %p\n", debugstr_w(szFilePath), dwOptions, pHash ); |
| |
| if (!szFilePath) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!*szFilePath) |
| return ERROR_PATH_NOT_FOUND; |
| |
| if (dwOptions) |
| return ERROR_INVALID_PARAMETER; |
| if (!pHash) |
| return ERROR_INVALID_PARAMETER; |
| if (pHash->dwFileHashInfoSize < sizeof *pHash) |
| return ERROR_INVALID_PARAMETER; |
| |
| handle = CreateFileW( szFilePath, GENERIC_READ, |
| FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL ); |
| if (handle == INVALID_HANDLE_VALUE) |
| return ERROR_FILE_NOT_FOUND; |
| |
| length = GetFileSize( handle, NULL ); |
| |
| mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, 0, NULL ); |
| if (mapping) |
| { |
| p = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, length ); |
| if (p) |
| { |
| MD5_CTX ctx; |
| |
| MD5Init( &ctx ); |
| MD5Update( &ctx, p, length ); |
| MD5Final( &ctx ); |
| UnmapViewOfFile( p ); |
| |
| memcpy( pHash->dwData, ctx.digest, sizeof pHash->dwData ); |
| r = ERROR_SUCCESS; |
| } |
| CloseHandle( mapping ); |
| } |
| CloseHandle( handle ); |
| |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiGetFileHashA [MSI.@] |
| */ |
| UINT WINAPI MsiGetFileHashA( LPCSTR szFilePath, DWORD dwOptions, |
| PMSIFILEHASHINFO pHash ) |
| { |
| LPWSTR file; |
| UINT r; |
| |
| TRACE("%s %08x %p\n", debugstr_a(szFilePath), dwOptions, pHash ); |
| |
| file = strdupAtoW( szFilePath ); |
| if (szFilePath && !file) |
| return ERROR_OUTOFMEMORY; |
| |
| r = MsiGetFileHashW( file, dwOptions, pHash ); |
| msi_free( file ); |
| return r; |
| } |
| |
| /*********************************************************************** |
| * MsiAdvertiseScriptW [MSI.@] |
| */ |
| UINT WINAPI MsiAdvertiseScriptW( LPCWSTR szScriptFile, DWORD dwFlags, |
| PHKEY phRegData, BOOL fRemoveItems ) |
| { |
| FIXME("%s %08x %p %d\n", |
| debugstr_w( szScriptFile ), dwFlags, phRegData, fRemoveItems ); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| /*********************************************************************** |
| * MsiAdvertiseScriptA [MSI.@] |
| */ |
| UINT WINAPI MsiAdvertiseScriptA( LPCSTR szScriptFile, DWORD dwFlags, |
| PHKEY phRegData, BOOL fRemoveItems ) |
| { |
| FIXME("%s %08x %p %d\n", |
| debugstr_a( szScriptFile ), dwFlags, phRegData, fRemoveItems ); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| /*********************************************************************** |
| * MsiIsProductElevatedW [MSI.@] |
| */ |
| UINT WINAPI MsiIsProductElevatedW( LPCWSTR szProduct, BOOL *pfElevated ) |
| { |
| FIXME("%s %p - stub\n", |
| debugstr_w( szProduct ), pfElevated ); |
| *pfElevated = TRUE; |
| return ERROR_SUCCESS; |
| } |
| |
| /*********************************************************************** |
| * MsiIsProductElevatedA [MSI.@] |
| */ |
| UINT WINAPI MsiIsProductElevatedA( LPCSTR szProduct, BOOL *pfElevated ) |
| { |
| FIXME("%s %p - stub\n", |
| debugstr_a( szProduct ), pfElevated ); |
| *pfElevated = TRUE; |
| return ERROR_SUCCESS; |
| } |
| |
| /*********************************************************************** |
| * MsiSetExternalUIRecord [MSI.@] |
| */ |
| UINT WINAPI MsiSetExternalUIRecord( INSTALLUI_HANDLER_RECORD handler, |
| DWORD filter, LPVOID context, |
| PINSTALLUI_HANDLER_RECORD prev ) |
| { |
| TRACE("%p %08x %p %p\n", handler, filter, context, prev); |
| |
| if (prev) |
| *prev = gUIHandlerRecord; |
| |
| gUIHandlerRecord = handler; |
| gUIFilter = filter; |
| gUIContext = context; |
| |
| return ERROR_SUCCESS; |
| } |
| |
| /*********************************************************************** |
| * MsiInstallMissingComponentW [MSI.@] |
| */ |
| UINT WINAPI MsiInstallMissingComponentW(LPCWSTR szProduct, LPCWSTR szComponent, INSTALLSTATE eInstallState) |
| { |
| FIXME("(%s %s %d\n", debugstr_w(szProduct), debugstr_w(szComponent), eInstallState); |
| return ERROR_SUCCESS; |
| } |