This is an automated email from the ASF dual-hosted git repository. ardovm pushed a commit to branch AOO42X in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 8f99ff636f612e8a183ef33aafed4934aa3050c3 Author: Arrigo Marchiori <[email protected]> AuthorDate: Sat Feb 7 08:42:33 2026 +0100 Avoid static linking Ensure we use MSI from the current host, rather than the build system's. (cherry picked from commit a2971faee62b678751866b8308f86df85c7eacbd) --- main/desktop/win32/source/setup/aoo_msi.cxx | 239 ++++++++++++++++++++++++++++ main/desktop/win32/source/setup/aoo_msi.hxx | 64 ++++++++ main/desktop/win32/source/setup/makefile.mk | 4 +- main/desktop/win32/source/setup/setup.cpp | 148 ++++------------- 4 files changed, 334 insertions(+), 121 deletions(-) diff --git a/main/desktop/win32/source/setup/aoo_msi.cxx b/main/desktop/win32/source/setup/aoo_msi.cxx new file mode 100755 index 0000000000..35fa59b4bc --- /dev/null +++ b/main/desktop/win32/source/setup/aoo_msi.cxx @@ -0,0 +1,239 @@ +/************************************************************** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + +#define UNICODE 1 +#define _UNICODE 1 + +#include "aoo_msi.hxx" + +#include <strsafe.h> + +/// Handle to MSI.DLL +static HMODULE hMsi = NULL; + + +TCHAR *getInstallerLocation( void ) +{ + static const TCHAR sInstKey[] = TEXT( "Software\\Microsoft\\Windows\\CurrentVersion\\Installer" ); + static const TCHAR sInstLocValue[] = TEXT( "InstallerLocation" ); + + HKEY hInstKey = NULL; + TCHAR *sMsiFolder = new TCHAR[ MAX_PATH + 1 ]; + sMsiFolder[0] = '\0'; + + // find registered location of Msi.dll + if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) ) + { + long nRet = ERROR_SUCCESS; + DWORD dwMsiFolderSize = MAX_PATH + 1; + DWORD dwType = 0; + + if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, + &dwType, (BYTE*)sMsiFolder, &dwMsiFolderSize ) ) ) + { + // try again with larger buffer + delete [] sMsiFolder; + sMsiFolder = new TCHAR[ dwMsiFolderSize ]; + nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType, + (BYTE*)sMsiFolder, &dwMsiFolderSize ); + } + + if ( ( ERROR_SUCCESS == nRet) && + ( dwType == REG_SZ ) && ( dwMsiFolderSize ) ) { + // Make sure the string is null-terminated + sMsiFolder[dwMsiFolderSize / sizeof ( TCHAR ) + 1] = 0; + } else { + sMsiFolder[0] = '\0'; + } + } + return sMsiFolder; +} + +/** Load MSI.DLL and a symbol from it + * + * @return the requested symbol, or NULL in case of error. + */ +static FARPROC aoo_msi_load( LPCSTR lpProcName ) +{ + static const TCHAR sMsiDll[] = TEXT( "\\msi.dll" ); + if ( !hMsi ) // use the default location + { + TCHAR *sInstallerLocation = getInstallerLocation(); + if ( sInstallerLocation[0] ) + { + // load Msi.dll from registered location + int nLength = lstrlen( sMsiDll ) + lstrlen( sInstallerLocation ) + 1; // use StringCchLength ? + TCHAR *pMsiLocation = new TCHAR[ nLength ]; + + if ( SUCCEEDED( StringCchCopy( pMsiLocation, nLength, sInstallerLocation ) ) && + SUCCEEDED( StringCchCat( pMsiLocation, nLength, sMsiDll ) ) ) + { + hMsi = LoadLibrary( pMsiLocation ); + } + delete[] pMsiLocation; + } + delete[] sInstallerLocation; + } + if ( !hMsi ) + { + return NULL; + } + return GetProcAddress( hMsi, lpProcName ); +} + + +typedef HRESULT (CALLBACK* PFnDllGetVersion)( DLLVERSIONINFO *pdvi ); +HRESULT aoo_MsiDllGetVersion( DLLVERSIONINFO *pdvi ) { + static PFnDllGetVersion f = NULL; + if ( f == NULL ) + f = (PFnDllGetVersion) aoo_msi_load( "DllGetVersion" ); + if ( f == NULL ) + return HRESULT_FROM_WIN32( GetLastError() ); + return f( pdvi ); +} + +typedef UINT (WINAPI* PFnMsiGetPatchInfo)( LPCTSTR szPatch, + LPCTSTR szAttribute, + LPTSTR lpValueBuf, + LPDWORD pcchValueBuf ); +UINT WINAPI aoo_MsiGetPatchInfo( LPCTSTR szPatch, + LPCTSTR szAttribute, + LPTSTR lpValueBuf, + LPDWORD pcchValueBuf ) +{ + static PFnMsiGetPatchInfo f = NULL; + if ( f == NULL ) + { +#ifdef UNICODE + f = (PFnMsiGetPatchInfo) aoo_msi_load( "MsiGetPatchInfoW" ); +#else + f = (PFnMsiGetPatchInfo) aoo_msi_load( "MsiGetPatchInfoA" ); +#endif + } + if ( !f ) + { + return ERROR_BAD_CONFIGURATION; + } + return f( szPatch, szAttribute, lpValueBuf, pcchValueBuf ); +} + +typedef UINT (WINAPI* PFnMsiGetSummaryInformation)( MSIHANDLE hDatabase, + LPCTSTR szDatabasePath, + UINT uiUpdateCount, + MSIHANDLE *phSummaryInfo ); +UINT WINAPI aoo_MsiGetSummaryInformation( MSIHANDLE hDatabase, + LPCTSTR szDatabasePath, + UINT uiUpdateCount, + MSIHANDLE *phSummaryInfo ) +{ + static PFnMsiGetSummaryInformation f = NULL; + if ( f == NULL ) + { +#ifdef UNICODE + f = (PFnMsiGetSummaryInformation) aoo_msi_load( "MsiGetSummaryInformationW" ); +#else + f = (PFnMsiGetSummaryInformation) aoo_msi_load( "MsiGetSummaryInformationA" ); +#endif + } + if ( f == NULL ) + { + return ERROR_BAD_CONFIGURATION; + } + return f( hDatabase, szDatabasePath, uiUpdateCount, phSummaryInfo ); +} + +typedef INSTALLSTATE (WINAPI* PFnMsiQueryProductState)( LPCTSTR szProduct ); +INSTALLSTATE WINAPI aoo_MsiQueryProductState( LPCTSTR szProduct ) +{ + static PFnMsiQueryProductState f = NULL; + if ( f == NULL ) + { +#ifdef UNICODE + f = (PFnMsiQueryProductState) aoo_msi_load( "MsiQueryProductStateW" ); +#else + f = (PFnMsiQueryProductState) aoo_msi_load( "MsiQueryProductStateA" ); +#endif + } + if ( f == NULL ) + { + return INSTALLSTATE_INVALIDARG; + } + return f( szProduct ); +} + +typedef UINT (WINAPI* PFnMsiSummaryInfoGetProperty)( MSIHANDLE hSummaryInfo, + UINT uiProperty, + PUINT puiDataType, + LPINT piValue, + FILETIME *pftValue, + LPTSTR szValueBuf, + LPDWORD pcchValueBuf ); +UINT WINAPI aoo_MsiSummaryInfoGetProperty( MSIHANDLE hSummaryInfo, + UINT uiProperty, + PUINT puiDataType, + LPINT piValue, + FILETIME *pftValue, + LPTSTR szValueBuf, + LPDWORD pcchValueBuf ) +{ + static PFnMsiSummaryInfoGetProperty f = NULL; + if ( f == NULL ) + { +#ifdef UNICODE + f = (PFnMsiSummaryInfoGetProperty) aoo_msi_load( "MsSummaryInfoGetPropertyW" ); +#else + f = (PFnMsiSummaryInfoGetProperty) aoo_msi_load( "MsSummaryInfoGetPropertyA" ); +#endif + } + if ( f == NULL ) + { + return ERROR_INVALID_FUNCTION; + } + return f( hSummaryInfo, uiProperty, puiDataType, piValue, pftValue, + szValueBuf, pcchValueBuf ); +} + + +typedef UINT (WINAPI *PFnMsiCloseHandle)( MSIHANDLE hAny ); +UINT WINAPI aoo_MsiCloseHandle( MSIHANDLE hAny ) +{ + static PFnMsiCloseHandle f = NULL; + if ( f == NULL ) + { + f = (PFnMsiCloseHandle) aoo_msi_load( "MsiCloseHandle" ); + } + if ( f == NULL ) + { + return ERROR_INVALID_FUNCTION; + } + return f( hAny ); +} + +/* + * MsiCloseHandle() must be defined because it is required by the + * destructor of class PMSIHANDLE, defined in Msi.h. + */ +extern "C" { + UINT WINAPI MsiCloseHandle( MSIHANDLE hAny ) + { + return aoo_MsiCloseHandle( hAny ); + } +} diff --git a/main/desktop/win32/source/setup/aoo_msi.hxx b/main/desktop/win32/source/setup/aoo_msi.hxx new file mode 100755 index 0000000000..9fdf3d3a95 --- /dev/null +++ b/main/desktop/win32/source/setup/aoo_msi.hxx @@ -0,0 +1,64 @@ +/************************************************************** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + +#ifndef AOO_MSI_H +#define AOO_MSI_H + +#if defined _MSC_VER +#pragma warning(push, 1) +#endif +#include <windows.h> +#if defined _MSC_VER +#pragma warning(pop) +#endif +#include <shlwapi.h> +#include <msi.h> + +/** Load the "Installer location" from the Windows registry. + * + * @return an array allocated with new[] of at least MAX_PATH + 1 + * elements. The string length will be zero in case of error. + */ +TCHAR *getInstallerLocation( void ); + +HRESULT aoo_MsiDllGetVersion( DLLVERSIONINFO *pdvi ); + +UINT WINAPI aoo_MsiGetPatchInfo( LPCTSTR szPatch, + LPCTSTR szAttribute, + LPTSTR lpValueBuf, + LPDWORD pcchValueBuf ); + +UINT WINAPI aoo_MsiGetSummaryInformation( MSIHANDLE hDatabase, + LPCTSTR szDatabasePath, + UINT uiUpdateCount, + MSIHANDLE *phSummaryInfo ); + +INSTALLSTATE WINAPI aoo_MsiQueryProductState( LPCTSTR szProduct ); + +UINT WINAPI aoo_MsiSummaryInfoGetProperty( MSIHANDLE hSummaryInfo, + UINT uiProperty, + PUINT puiDataType, + LPINT piValue, + FILETIME *pftValue, + LPTSTR szValueBuf, + LPDWORD pcchValueBuf ); + +#endif // defined AOO_MSI_H diff --git a/main/desktop/win32/source/setup/makefile.mk b/main/desktop/win32/source/setup/makefile.mk index 042bf765e6..35dce7f533 100644 --- a/main/desktop/win32/source/setup/makefile.mk +++ b/main/desktop/win32/source/setup/makefile.mk @@ -54,7 +54,7 @@ ULFDIR:=. .ENDIF # "$(WITH_LANG)"!="" OBJFILES= $(OBJ)$/setup_main.obj \ - $(OBJ)$/setup.obj + $(OBJ)$/setup.obj $(OBJ)$/aoo_msi.obj # --- Targets ------------------------------------------------------ # Generate the native Windows resource file @@ -66,7 +66,7 @@ LIBSALCPPRT= $(0) APP1NOSAL= TRUE APP1TARGET= loader2 -APP1STDLIBS= $(GDI32LIB) $(ADVAPI32LIB) $(SHELL32LIB) $(MSILIB) +APP1STDLIBS= $(GDI32LIB) $(ADVAPI32LIB) $(SHELL32LIB) .IF "$(COM)"!="GCC" APP1STDLIBS+= libcmt.lib .ENDIF diff --git a/main/desktop/win32/source/setup/setup.cpp b/main/desktop/win32/source/setup/setup.cpp index b35dd99615..d1be11df41 100644 --- a/main/desktop/win32/source/setup/setup.cpp +++ b/main/desktop/win32/source/setup/setup.cpp @@ -46,6 +46,7 @@ #include "strsafe.h" #include "setup.hxx" +#include "aoo_msi.hxx" #include "resource.h" @@ -79,7 +80,6 @@ #define CMDLN_REG_ALL_MSO_TYPES TEXT( "msoreg=1" ) #define CMDLN_REG_NO_MSO_TYPES TEXT( "msoreg=0" ) -#define MSI_DLL TEXT( "msi.dll" ) #define ADVAPI32_DLL TEXT( "advapi32.dll" ) #define PROFILE_NAME TEXT( "setup.ini" ) @@ -90,10 +90,8 @@ // Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161 #define PRODUCTCODE_X64 TEXT( "{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}" ) -#define MSIAPI_DllGetVersion "DllGetVersion" #define ADVAPI32API_CheckTokenMembership "CheckTokenMembership" -typedef HRESULT (CALLBACK* PFnDllGetVersion)( DLLVERSIONINFO *pdvi); typedef BOOL (WINAPI* PFnCheckTokenMembership)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember); #ifdef DEBUG @@ -114,9 +112,6 @@ static inline void OutputDebugStringFormat( LPCTSTR, ... ) //-------------------------------------------------------------------------- -const TCHAR sInstKey[] = TEXT( "Software\\Microsoft\\Windows\\CurrentVersion\\Installer" ); -const TCHAR sInstLocValue[] = TEXT( "InstallerLocation" ); -const TCHAR sMsiDll[] = TEXT( "\\msi.dll" ); const TCHAR sMsiExe[] = TEXT( "\\msiexec.exe" ); const TCHAR sDelayReboot[] = TEXT( " /c:\"msiinst /delayreboot\"" ); const TCHAR sMsiQuiet[] = TEXT( " /q" ); @@ -718,84 +713,15 @@ boolean SetupApp::ChooseLanguage( long& rLanguage ) return true; } -//-------------------------------------------------------------------------- -HMODULE SetupApp::LoadMsiLibrary() -{ - HMODULE hMsi = NULL; - HKEY hInstKey = NULL; - - // find registered location of Msi.dll - if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) ) - { - long nRet = ERROR_SUCCESS; - TCHAR *sMsiFolder = new TCHAR[ MAX_PATH + 1 ]; - DWORD dwMsiFolderSize = MAX_PATH + 1; - DWORD dwType = 0; - - if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, - &dwType, (BYTE*)sMsiFolder, &dwMsiFolderSize ) ) ) - { - // try again with larger buffer - delete [] sMsiFolder; - sMsiFolder = new TCHAR[ dwMsiFolderSize ]; - - nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType, - (BYTE*)sMsiFolder, &dwMsiFolderSize ); - } - - if ( ERROR_SUCCESS == nRet && dwType == REG_SZ && dwMsiFolderSize > 0 ) - { - // load Msi.dll from registered location - int nLength = lstrlen( sMsiDll ) + dwMsiFolderSize + 1; // use StringCchLength ? - TCHAR *pMsiLocation = new TCHAR[ nLength ]; - - if ( SUCCEEDED( StringCchCopy( pMsiLocation, nLength, sMsiFolder ) ) && - SUCCEEDED( StringCchCat( pMsiLocation, nLength, sMsiDll ) ) ) - { - hMsi = LoadLibrary( pMsiLocation ); - } - } - } - - if ( !hMsi ) // use the default location - { - hMsi = LoadLibrary( sMsiDll ); - } - - return hMsi; -} //-------------------------------------------------------------------------- LPCTSTR SetupApp::GetPathToMSI() { LPTSTR sMsiPath = NULL; HKEY hInstKey = NULL; - TCHAR *sMsiFolder = new TCHAR[ MAX_PATH + 1 ]; + TCHAR *sMsiFolder = getInstallerLocation(); DWORD nMsiFolderSize = MAX_PATH + 1; - sMsiFolder[0] = '\0'; - - // find registered location of Msi.dll - if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) ) - { - LONG nRet = ERROR_SUCCESS; - DWORD dwType = 0; - - if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, - &dwType, (BYTE*)sMsiFolder, &nMsiFolderSize ) ) ) - { - // try again with larger buffer - delete [] sMsiFolder; - sMsiFolder = new TCHAR[ nMsiFolderSize ]; - - nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType, - (BYTE*)sMsiFolder, &nMsiFolderSize ); - } - - if ( ERROR_SUCCESS != nRet || dwType != REG_SZ || nMsiFolderSize == 0 ) - sMsiFolder[0] = '\0'; - } - if ( sMsiFolder[0] == '\0' ) // use the default location { Log( TEXT( " Could not find path to msiexec.exe in registry" ) ); @@ -1174,50 +1100,34 @@ void SetupApp::GetLanguageName( long nLanguage, LPTSTR sName ) const boolean SetupApp::CheckVersion() { boolean bRet = false; - HMODULE hMsi = LoadMsiLibrary(); Log( TEXT( " Looking for installed MSI with version >= %s\r\n" ), m_pReqVersion ); - if ( !hMsi ) - { - Log( TEXT( "Error: No MSI found!\r\n" ) ); - SetError( (UINT) ERROR_SETUP_NOT_FOUND ); - } - else - { - PFnDllGetVersion pDllGetVersion = (PFnDllGetVersion) GetProcAddress( hMsi, MSIAPI_DllGetVersion ); + DLLVERSIONINFO aInfo; - if ( pDllGetVersion ) + aInfo.cbSize = sizeof( DLLVERSIONINFO ); + if ( NOERROR == aoo_MsiDllGetVersion( &aInfo ) ) + { + TCHAR pMsiVersion[ VERSION_SIZE ]; + StringCchPrintf( pMsiVersion, VERSION_SIZE, TEXT("%d.%d.%4d"), + aInfo.dwMajorVersion, + aInfo.dwMinorVersion, + aInfo.dwBuildNumber ); + if ( _tcsncmp( pMsiVersion, m_pReqVersion, _tcslen( pMsiVersion ) ) < 0 ) { - DLLVERSIONINFO aInfo; - - aInfo.cbSize = sizeof( DLLVERSIONINFO ); - if ( NOERROR == pDllGetVersion( &aInfo ) ) - { - TCHAR pMsiVersion[ VERSION_SIZE ]; - StringCchPrintf( pMsiVersion, VERSION_SIZE, TEXT("%d.%d.%4d"), - aInfo.dwMajorVersion, - aInfo.dwMinorVersion, - aInfo.dwBuildNumber ); - if ( _tcsncmp( pMsiVersion, m_pReqVersion, _tcslen( pMsiVersion ) ) < 0 ) - { - StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pMsiVersion ); - SetError( (UINT) ERROR_SETUP_TO_OLD ); - Log( TEXT( "Warning: Old MSI version found <%s>, update needed!\r\n" ), pMsiVersion ); - } - else - { - Log( TEXT( " Found MSI version <%s>, no update needed\r\n" ), pMsiVersion ); - bRet = true; - } - if ( aInfo.dwMajorVersion >= 3 ) - m_bSupportsPatch = true; - else - Log( TEXT("Warning: Patching not supported! MSI-Version <%s>\r\n"), pMsiVersion ); - } - } - - FreeLibrary( hMsi ); + StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pMsiVersion ); + SetError( (UINT) ERROR_SETUP_TO_OLD ); + Log( TEXT( "Warning: Old MSI version found <%s>, update needed!\r\n" ), pMsiVersion ); + } + else + { + Log( TEXT( " Found MSI version <%s>, no update needed\r\n" ), pMsiVersion ); + bRet = true; + } + if ( aInfo.dwMajorVersion >= 3 ) + m_bSupportsPatch = true; + else + Log( TEXT("Warning: Patching not supported! MSI-Version <%s>\r\n"), pMsiVersion ); } return bRet; @@ -1872,7 +1782,7 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) StringCchCopy( szDatabasePath, nLen, pBaseDir ); StringCchCat( szDatabasePath, nLen, pFileName ); - UINT nRet = MsiGetSummaryInformation( NULL, szDatabasePath, 0, &hSummaryInfo ); + UINT nRet = aoo_MsiGetSummaryInformation( NULL, szDatabasePath, 0, &hSummaryInfo ); if ( nRet != ERROR_SUCCESS ) { @@ -1884,7 +1794,7 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) UINT uiDataType; LPTSTR szPatchID = new TCHAR[ 64 ]; DWORD cchValueBuf = 64; - nRet = MsiSummaryInfoGetProperty( hSummaryInfo, PID_REVNUMBER, &uiDataType, NULL, NULL, szPatchID, &cchValueBuf ); + nRet = aoo_MsiSummaryInfoGetProperty( hSummaryInfo, PID_REVNUMBER, &uiDataType, NULL, NULL, szPatchID, &cchValueBuf ); if ( nRet != ERROR_SUCCESS ) { @@ -1893,7 +1803,7 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) return false; } - nRet = MsiGetPatchInfo( szPatchID, INSTALLPROPERTY_LOCALPACKAGE, NULL, NULL ); + nRet = aoo_MsiGetPatchInfo( szPatchID, INSTALLPROPERTY_LOCALPACKAGE, NULL, NULL ); StringCchPrintf( sBuf, 80, TEXT(" GetPatchInfo for (%s) returned (%u)\r\n"), szPatchID, nRet ); Log( sBuf ); @@ -1918,7 +1828,7 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) //-------------------------------------------------------------------------- boolean SetupApp::InstallRuntimes( TCHAR *sProductCode, TCHAR *sRuntimePath ) { - INSTALLSTATE nRet = MsiQueryProductState( sProductCode ); + INSTALLSTATE nRet = aoo_MsiQueryProductState( sProductCode ); OutputDebugStringFormat( TEXT( "MsiQueryProductState returned <%d>\r\n" ), nRet ); if ( nRet == INSTALLSTATE_DEFAULT ) return true;
