Index: src/installer-ca/wcawrap.cpp |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/installer-ca/wcawrap.cpp |
@@ -0,0 +1,1588 @@ |
+//------------------------------------------------------------------------------------------------- |
+// <copyright file="wcawrap.cpp" company="Outercurve Foundation"> |
+// Copyright (c) 2004, Outercurve Foundation. |
+// This software is released under Microsoft Reciprocal License (MS-RL). |
+// The license and further copyright text can be found in the file |
+// LICENSE.TXT at the root directory of the distribution. |
+// </copyright> |
+// |
+// <summary> |
+// Windows Installer XML CustomAction utility library wrappers for MSI API |
+// </summary> |
+//------------------------------------------------------------------------------------------------- |
+ |
+#include "precomp.h" |
+ |
+ |
+/******************************************************************** |
+WcaProcessMessage() - sends a message from the CustomAction |
+ |
+********************************************************************/ |
+extern "C" UINT WIXAPI WcaProcessMessage( |
+ __in INSTALLMESSAGE eMessageType, |
+ __in MSIHANDLE hRecord |
+ ) |
+{ |
+ UINT er = ::MsiProcessMessage(WcaGetInstallHandle(), eMessageType, hRecord); |
+ if (ERROR_INSTALL_USEREXIT == er || IDCANCEL == er) |
+ { |
+ WcaSetReturnValue(ERROR_INSTALL_USEREXIT); |
+ } |
+ |
+ return er; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaErrorMessage() - sends an error message from the CustomAction using |
+the Error table |
+ |
+NOTE: Any and all var_args (...) must be WCHAR* |
+********************************************************************/ |
+extern "C" UINT __cdecl WcaErrorMessage( |
+ __in int iError, |
+ __in HRESULT hrError, |
+ __in UINT uiType, |
+ __in DWORD cArgs, |
+ ... |
+ ) |
+{ |
+ UINT er; |
+ MSIHANDLE hRec = NULL; |
+ va_list args; |
+ |
+ uiType |= INSTALLMESSAGE_ERROR; // ensure error type is set |
+ hRec = ::MsiCreateRecord(cArgs + 2); |
+ if (!hRec) |
+ { |
+ er = ERROR_OUTOFMEMORY; |
+ ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to create record when sending error message"); |
+ } |
+ |
+ er = ::MsiRecordSetInteger(hRec, 1, iError); |
+ ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set error code into error message"); |
+ |
+ er = ::MsiRecordSetInteger(hRec, 2, hrError); |
+ ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set hresult code into error message"); |
+ |
+ va_start(args, cArgs); |
+ for (DWORD i = 0; i < cArgs; i++) |
+ { |
+ er = ::MsiRecordSetStringW(hRec, i + 3, va_arg(args, WCHAR*)); |
+ ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set string string into error message"); |
+ } |
+ va_end(args); |
+ |
+ er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(uiType), hRec); |
+LExit: |
+ if (hRec) |
+ { |
+ ::MsiCloseHandle(hRec); |
+ } |
+ |
+ return er; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaProgressMessage() - extends the progress bar or sends a progress |
+update from the CustomAction |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaProgressMessage( |
+ __in UINT uiCost, |
+ __in BOOL fExtendProgressBar |
+ ) |
+{ |
+ static BOOL fExplicitProgressMessages = FALSE; |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ MSIHANDLE hRec = ::MsiCreateRecord(3); |
+ |
+ // if aren't extending the progress bar and we haven't switched into explicit message mode |
+ if (!fExtendProgressBar && !fExplicitProgressMessages) |
+ { |
+ AssertSz(::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED) || |
+ ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT) || |
+ ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK), "can only send progress bar messages in a deferred CustomAction"); |
+ |
+ // tell Darwin to use explicit progress messages |
+ ::MsiRecordSetInteger(hRec, 1, 1); |
+ ::MsiRecordSetInteger(hRec, 2, 1); |
+ ::MsiRecordSetInteger(hRec, 3, 0); |
+ |
+ er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); |
+ if (0 == er || IDOK == er || IDYES == er) |
+ { |
+ hr = S_OK; |
+ } |
+ else if (IDABORT == er || IDCANCEL == er) |
+ { |
+ WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit |
+ ExitFunction1(hr = S_FALSE); |
+ } |
+ else |
+ { |
+ hr = E_UNEXPECTED; |
+ } |
+ ExitOnFailure(hr, "failed to tell Darwin to use explicit progress messages"); |
+ |
+ fExplicitProgressMessages = TRUE; |
+ } |
+#if DEBUG |
+ else if (fExtendProgressBar) // if we are extending the progress bar, make sure we're not deferred |
+ { |
+ AssertSz(!::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED), "cannot add ticks to progress bar length from deferred CustomAction"); |
+ } |
+#endif |
+ |
+ // send the progress message |
+ ::MsiRecordSetInteger(hRec, 1, (fExtendProgressBar) ? 3 : 2); |
+ ::MsiRecordSetInteger(hRec, 2, uiCost); |
+ ::MsiRecordSetInteger(hRec, 3, 0); |
+ |
+ er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); |
+ if (0 == er || IDOK == er || IDYES == er) |
+ { |
+ hr = S_OK; |
+ } |
+ else if (IDABORT == er || IDCANCEL == er) |
+ { |
+ WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit |
+ hr = S_FALSE; |
+ } |
+ else |
+ { |
+ hr = E_UNEXPECTED; |
+ } |
+ |
+LExit: |
+ if (hRec) |
+ { |
+ ::MsiCloseHandle(hRec); |
+ } |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaIsInstalling() - determines if a pair of installstates means install |
+ |
+********************************************************************/ |
+extern "C" BOOL WIXAPI WcaIsInstalling( |
+ __in INSTALLSTATE isInstalled, |
+ __in INSTALLSTATE isAction |
+ ) |
+{ |
+ return (INSTALLSTATE_LOCAL == isAction || |
+ INSTALLSTATE_SOURCE == isAction || |
+ (INSTALLSTATE_DEFAULT == isAction && |
+ (INSTALLSTATE_LOCAL == isInstalled || |
+ INSTALLSTATE_SOURCE == isInstalled))); |
+} |
+ |
+/******************************************************************** |
+WcaIsReInstalling() - determines if a pair of installstates means reinstall |
+ |
+********************************************************************/ |
+extern "C" BOOL WIXAPI WcaIsReInstalling( |
+ __in INSTALLSTATE isInstalled, |
+ __in INSTALLSTATE isAction |
+ ) |
+{ |
+ return ((INSTALLSTATE_LOCAL == isAction || |
+ INSTALLSTATE_SOURCE == isAction || |
+ INSTALLSTATE_DEFAULT == isAction) && |
+ (INSTALLSTATE_LOCAL == isInstalled || |
+ INSTALLSTATE_SOURCE == isInstalled)); |
+} |
+ |
+ |
+/******************************************************************** |
+WcaIsUninstalling() - determines if a pair of installstates means uninstall |
+ |
+********************************************************************/ |
+extern "C" BOOL WIXAPI WcaIsUninstalling( |
+ __in INSTALLSTATE isInstalled, |
+ __in INSTALLSTATE isAction |
+ ) |
+{ |
+ return ((INSTALLSTATE_ABSENT == isAction || |
+ INSTALLSTATE_REMOVED == isAction) && |
+ (INSTALLSTATE_LOCAL == isInstalled || |
+ INSTALLSTATE_SOURCE == isInstalled)); |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetComponentToDo() - gets a component's install states and |
+determines if they mean install, uninstall, or reinstall. |
+********************************************************************/ |
+extern "C" WCA_TODO WIXAPI WcaGetComponentToDo( |
+ __in_z LPCWSTR wzComponentId |
+ ) |
+{ |
+ INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; |
+ INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; |
+ if (ERROR_SUCCESS != ::MsiGetComponentStateW(WcaGetInstallHandle(), wzComponentId, &isInstalled, &isAction)) |
+ { |
+ return WCA_TODO_UNKNOWN; |
+ } |
+ |
+ if (WcaIsReInstalling(isInstalled, isAction)) |
+ { |
+ return WCA_TODO_REINSTALL; |
+ } |
+ else if (WcaIsUninstalling(isInstalled, isAction)) |
+ { |
+ return WCA_TODO_UNINSTALL; |
+ } |
+ else if (WcaIsInstalling(isInstalled, isAction)) |
+ { |
+ return WCA_TODO_INSTALL; |
+ } |
+ else |
+ { |
+ return WCA_TODO_UNKNOWN; |
+ } |
+} |
+ |
+ |
+/******************************************************************** |
+WcaSetComponentState() - sets the install state of a Component |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaSetComponentState( |
+ __in_z LPCWSTR wzComponent, |
+ __in INSTALLSTATE isState |
+ ) |
+{ |
+ UINT er = ::MsiSetComponentStateW(WcaGetInstallHandle(), wzComponent, isState); |
+ if (ERROR_INSTALL_USEREXIT == er) |
+ { |
+ WcaSetReturnValue(er); |
+ } |
+ |
+ return HRESULT_FROM_WIN32(er); |
+} |
+ |
+ |
+/******************************************************************** |
+WcaTableExists() - determines if installing database contains a table |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaTableExists( |
+ __in_z LPCWSTR wzTable |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ |
+ // NOTE: The following line of commented out code should work in a |
+ // CustomAction but does not in Windows Installer v1.1 |
+ // er = ::MsiDatabaseIsTablePersistentW(hDatabase, wzTable); |
+ |
+ // a "most elegant" workaround a Darwin v1.1 bug |
+ PMSIHANDLE hRec; |
+ er = ::MsiDatabaseGetPrimaryKeysW(WcaGetDatabaseHandle(), wzTable, &hRec); |
+ |
+ if (ERROR_SUCCESS == er) |
+ { |
+ hr = S_OK; |
+ } |
+ else if (ERROR_INVALID_TABLE == er) |
+ { |
+ hr = S_FALSE; |
+ } |
+ else |
+ { |
+ hr = E_FAIL; |
+ } |
+ Assert(SUCCEEDED(hr)); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaOpenView() - opens a view on the installing database |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaOpenView( |
+ __in_z LPCWSTR wzSql, |
+ __out MSIHANDLE* phView |
+ ) |
+{ |
+ if (!wzSql || !*wzSql|| !phView) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); |
+ ExitOnWin32Error1(er, hr, "failed to open view on database with SQL: %ls", wzSql); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaExecuteView() - executes a parameterized open view on the installing database |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaExecuteView( |
+ __in MSIHANDLE hView, |
+ __in MSIHANDLE hRec |
+ ) |
+{ |
+ if (!hView) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ AssertSz(hRec, "Use WcaOpenExecuteView() if you don't need to pass in a record"); |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiViewExecute(hView, hRec); |
+ ExitOnWin32Error(er, hr, "failed to execute view"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaOpenExecuteView() - opens and executes a view on the installing database |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaOpenExecuteView( |
+ __in_z LPCWSTR wzSql, |
+ __out MSIHANDLE* phView |
+ ) |
+{ |
+ if (!wzSql || !*wzSql|| !phView) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); |
+ ExitOnWin32Error(er, hr, "failed to open view on database"); |
+ |
+ er = ::MsiViewExecute(*phView, NULL); |
+ ExitOnWin32Error(er, hr, "failed to execute view"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaFetchRecord() - gets the next record from a view on the installing database |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaFetchRecord( |
+ __in MSIHANDLE hView, |
+ __out MSIHANDLE* phRec |
+ ) |
+{ |
+ if (!hView|| !phRec) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiViewFetch(hView, phRec); |
+ hr = HRESULT_FROM_WIN32(er); |
+ if (FAILED(hr) && E_NOMOREITEMS != hr) |
+ { |
+ ExitOnFailure(hr, "failed to fetch record from view"); |
+ } |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaFetchSingleRecord() - gets a single record from a view on the installing database |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaFetchSingleRecord( |
+ __in MSIHANDLE hView, |
+ __out MSIHANDLE* phRec |
+ ) |
+{ |
+ if (!hView|| !phRec) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiViewFetch(hView, phRec); |
+ if (ERROR_NO_MORE_ITEMS == er) |
+ { |
+ hr = S_FALSE; |
+ } |
+ else |
+ { |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ ExitOnFailure(hr, "failed to fetch single record from view"); |
+ |
+#ifdef DEBUG // only do this in debug to verify that a single record was returned |
+ MSIHANDLE hRecTest; |
+ er = ::MsiViewFetch(hView, &hRecTest); |
+ AssertSz(ERROR_NO_MORE_ITEMS == er && NULL == hRecTest, "WcaSingleFetch() did not fetch a single record"); |
+ ::MsiCloseHandle(hRecTest); |
+#endif |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetProperty - gets a string property value from the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetProperty( |
+ __in_z LPCWSTR wzProperty, |
+ __inout LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!wzProperty || !*wzProperty || !ppwzData) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ DWORD_PTR cch = 0; |
+ |
+ if (!*ppwzData) |
+ { |
+ WCHAR szEmpty[1] = L""; |
+ er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, (DWORD *)&cch); |
+ if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ } |
+ else |
+ { |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ ExitOnFailure1(hr, "Failed to allocate string for Property '%ls'", wzProperty); |
+ } |
+ else |
+ { |
+ hr = StrMaxLength(*ppwzData, &cch); |
+ ExitOnFailure(hr, "Failed to get previous size of property data string."); |
+ } |
+ |
+ er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch); |
+ if (ERROR_MORE_DATA == er) |
+ { |
+ Assert(*ppwzData); |
+ hr = StrAlloc(ppwzData, ++cch); |
+ ExitOnFailure1(hr, "Failed to allocate string for Property '%ls'", wzProperty); |
+ |
+ er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch); |
+ } |
+ ExitOnWin32Error1(er, hr, "Failed to get data for property '%ls'", wzProperty); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetFormattedProperty - gets a formatted string property value from |
+the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetFormattedProperty( |
+ __in_z LPCWSTR wzProperty, |
+ __out LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!wzProperty || !*wzProperty || !ppwzData) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ LPWSTR pwzPropertyValue = NULL; |
+ |
+ hr = WcaGetProperty(wzProperty, &pwzPropertyValue); |
+ ExitOnFailure1(hr, "failed to get %ls", wzProperty); |
+ |
+ hr = WcaGetFormattedString(pwzPropertyValue, ppwzData); |
+ ExitOnFailure2(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue); |
+ |
+LExit: |
+ ReleaseStr(pwzPropertyValue); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetFormattedString - gets a formatted string value from |
+the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetFormattedString( |
+ __in_z LPCWSTR wzString, |
+ __out LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!wzString || !*wzString || !ppwzData) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ PMSIHANDLE hRecord = ::MsiCreateRecord(1); |
+ DWORD_PTR cch = 0; |
+ |
+ er = ::MsiRecordSetStringW(hRecord, 0, wzString); |
+ ExitOnWin32Error1(er, hr, "Failed to set record field 0 with '%ls'", wzString); |
+ |
+ if (!*ppwzData) |
+ { |
+ WCHAR szEmpty[1] = L""; |
+ er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, (DWORD *)&cch); |
+ if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ } |
+ else |
+ { |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ ExitOnFailure1(hr, "Failed to allocate string for formatted string: '%ls'", wzString); |
+ } |
+ else |
+ { |
+ hr = StrMaxLength(*ppwzData, &cch); |
+ ExitOnFailure(hr, "Failed to get previous size of property data string"); |
+ } |
+ |
+ er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch); |
+ if (ERROR_MORE_DATA == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ ExitOnFailure1(hr, "Failed to allocate string for formatted string: '%ls'", wzString); |
+ |
+ er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch); |
+ } |
+ ExitOnWin32Error1(er, hr, "Failed to get formatted string: '%ls'", wzString); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetIntProperty - gets an integer property value from the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetIntProperty( |
+ __in_z LPCWSTR wzProperty, |
+ __inout int* piData |
+ ) |
+{ |
+ if (!piData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ UINT er; |
+ |
+ WCHAR wzValue[32]; |
+ DWORD cch = countof(wzValue) - 1; |
+ |
+ er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch); |
+ ExitOnWin32Error1(er, hr, "Failed to get data for property '%ls'", wzProperty); |
+ |
+ *piData = wcstol(wzValue, NULL, 10); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetTargetPath - gets the target path for a specified folder |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetTargetPath( |
+ __in_z LPCWSTR wzFolder, |
+ __out LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!wzFolder || !*wzFolder || !ppwzData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ |
+ UINT er = ERROR_SUCCESS; |
+ DWORD_PTR cch = 0; |
+ |
+ if (!*ppwzData) |
+ { |
+ WCHAR szEmpty[1] = L""; |
+ er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, (DWORD*)&cch); |
+ if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
+ { |
+ ++cch; //Add one for the null terminator |
+ hr = StrAlloc(ppwzData, cch); |
+ } |
+ else |
+ { |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ ExitOnFailure1(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); |
+ } |
+ else |
+ { |
+ hr = StrMaxLength(*ppwzData, &cch); |
+ ExitOnFailure(hr, "Failed to get previous size of string"); |
+ } |
+ |
+ er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch); |
+ if (ERROR_MORE_DATA == er) |
+ { |
+ ++cch; |
+ hr = StrAlloc(ppwzData, cch); |
+ ExitOnFailure1(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); |
+ |
+ er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch); |
+ } |
+ ExitOnWin32Error1(er, hr, "Failed to get target path for folder '%ls'", wzFolder); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaSetProperty - sets a string property value in the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaSetProperty( |
+ __in_z LPCWSTR wzPropertyName, |
+ __in_z LPCWSTR wzPropertyValue |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ |
+ if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue) |
+ return E_INVALIDARG; |
+ |
+ UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); |
+ ExitOnWin32Error1(er, hr, "failed to set property: %ls", wzPropertyName); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaSetIntProperty - sets a integer property value in the active install |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaSetIntProperty( |
+ __in_z LPCWSTR wzPropertyName, |
+ __in int nPropertyValue |
+ ) |
+{ |
+ if (!wzPropertyName || !*wzPropertyName) |
+ return E_INVALIDARG; |
+ |
+ // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null |
+ WCHAR wzPropertyValue[13]; |
+ HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue); |
+ ExitOnFailure1(hr, "failed to convert into string property value: %d", nPropertyValue); |
+ |
+ UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); |
+ ExitOnWin32Error1(er, hr, "failed to set property: %ls", wzPropertyName); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaIsPropertySet() - returns TRUE if property is set |
+ |
+********************************************************************/ |
+extern "C" BOOL WIXAPI WcaIsPropertySet( |
+ __in LPCSTR szProperty |
+ ) |
+{ |
+ DWORD cchProperty = 0; |
+ char szEmpty[1] = ""; |
+#ifdef DEBUG |
+ UINT er = |
+#endif |
+ ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty); |
+ AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); |
+ |
+ return 0 < cchProperty; // property is set if the length is greater than zero |
+} |
+ |
+ |
+/******************************************************************** |
+WcaIsUnicodePropertySet() - returns TRUE if property is set |
+ |
+********************************************************************/ |
+extern "C" BOOL WIXAPI WcaIsUnicodePropertySet( |
+ __in LPCWSTR wzProperty |
+ ) |
+{ |
+ DWORD cchProperty = 0; |
+ wchar_t wzEmpty[1] = L""; |
+#ifdef DEBUG |
+ UINT er = |
+#endif |
+ ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty); |
+ AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); |
+ |
+ return 0 < cchProperty; // property is set if the length is greater than zero |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetRecordInteger() - gets an integer field out of a record |
+ |
+NOTE: returns S_FALSE if the field was null |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetRecordInteger( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __inout int* piData |
+ ) |
+{ |
+ if (!hRec || !piData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ *piData = ::MsiRecordGetInteger(hRec, uiField); |
+ if (MSI_NULL_INTEGER == *piData) |
+ hr = S_FALSE; |
+ |
+ //LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetRecordString() - gets a string field out of a record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetRecordString( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __inout LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!hRec || !ppwzData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ UINT er; |
+ DWORD_PTR cch = 0; |
+ |
+ if (!*ppwzData) |
+ { |
+ WCHAR szEmpty[1] = L""; |
+ er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, (DWORD*)&cch); |
+ if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ } |
+ else |
+ { |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ ExitOnFailure(hr, "Failed to allocate memory for record string"); |
+ } |
+ else |
+ { |
+ hr = StrMaxLength(*ppwzData, &cch); |
+ ExitOnFailure(hr, "Failed to get previous size of string"); |
+ } |
+ |
+ er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch); |
+ if (ERROR_MORE_DATA == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ ExitOnFailure(hr, "Failed to allocate memory for record string"); |
+ |
+ er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch); |
+ } |
+ ExitOnWin32Error(er, hr, "Failed to get string from record"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+HideNulls() - internal helper function to escape [~] in formatted strings |
+ |
+********************************************************************/ |
+static void HideNulls( |
+ __inout_z LPWSTR wzData |
+ ) |
+{ |
+ LPWSTR pwz = wzData; |
+ |
+ while(*pwz) |
+ { |
+ if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~] |
+ { |
+ pwz[0] = L'!'; // turn it into !$! |
+ pwz[1] = L'$'; |
+ pwz[2] = L'!'; |
+ pwz += 3; |
+ } |
+ else |
+ { |
+ ++pwz; |
+ } |
+ } |
+} |
+ |
+ |
+/******************************************************************** |
+RevealNulls() - internal helper function to unescape !$! in formatted strings |
+ |
+********************************************************************/ |
+static void RevealNulls( |
+ __inout_z LPWSTR wzData |
+ ) |
+{ |
+ LPWSTR pwz = wzData; |
+ |
+ while(*pwz) |
+ { |
+ if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$! |
+ { |
+ pwz[0] = L'['; // turn it back into [~] |
+ pwz[1] = L'~'; |
+ pwz[2] = L']'; |
+ pwz += 3; |
+ } |
+ else |
+ { |
+ ++pwz; |
+ } |
+ } |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetRecordFormattedString() - gets formatted string filed from record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetRecordFormattedString( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __inout LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!hRec || !ppwzData) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ UINT er; |
+ DWORD_PTR cch = 0; |
+ PMSIHANDLE hRecFormat; |
+ |
+ // get the format string |
+ hr = WcaGetRecordString(hRec, uiField, ppwzData); |
+ ExitOnFailure(hr, "failed to get string from record"); |
+ |
+ if (!**ppwzData) |
+ { |
+ ExitFunction(); |
+ } |
+ |
+ // hide the nulls '[~]' so we can get them back after formatting |
+ HideNulls(*ppwzData); |
+ |
+ // set up the format record |
+ hRecFormat = ::MsiCreateRecord(1); |
+ ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string"); |
+ hr = WcaSetRecordString(hRecFormat, 0, *ppwzData); |
+ ExitOnFailure(hr, "failed to set string to format record"); |
+ |
+ // format the string |
+ hr = StrMaxLength(*ppwzData, &cch); |
+ ExitOnFailure(hr, "failed to get max length of string"); |
+ |
+ er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch); |
+ if (ERROR_MORE_DATA == er) |
+ { |
+ hr = StrAlloc(ppwzData, ++cch); |
+ ExitOnFailure(hr, "Failed to allocate memory for record string"); |
+ |
+ er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch); |
+ } |
+ ExitOnWin32Error(er, hr, "Failed to format string"); |
+ |
+ // put the nulls back |
+ RevealNulls(*ppwzData); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetRecordFormattedInteger() - gets formatted integer from record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __out int* piData |
+ ) |
+{ |
+ if (!hRec || !piData) |
+ { |
+ return E_INVALIDARG; |
+ } |
+ |
+ HRESULT hr = S_OK; |
+ LPWSTR pwzData = NULL; |
+ |
+ hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData); |
+ ExitOnFailure1(hr, "failed to get record field: %u", uiField); |
+ if (pwzData && *pwzData) |
+ { |
+ LPWSTR wz = NULL; |
+ *piData = wcstol(pwzData, &wz, 10); |
+ if (wz && *wz) |
+ { |
+ hr = E_INVALIDARG; |
+ ExitOnFailure2(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData); |
+ } |
+ } |
+ else |
+ { |
+ *piData = MSI_NULL_INTEGER; |
+ } |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaAllocStream() - creates a byte stream of the specified size |
+ |
+NOTE: Use WcaFreeStream() to release the byte stream |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaAllocStream( |
+ __deref_out_bcount_part(cbData, 0) BYTE** ppbData, |
+ __in DWORD cbData |
+ ) |
+{ |
+ Assert(ppbData); |
+ HRESULT hr; |
+ BYTE* pbNewData; |
+ |
+ if (*ppbData) |
+ pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE)); |
+ else |
+ pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); |
+ |
+ if (!pbNewData) |
+ { |
+ ExitOnLastError(hr, "Failed to allocate string"); |
+ } |
+ |
+ *ppbData = pbNewData; |
+ pbNewData = NULL; |
+ |
+ hr = S_OK; |
+LExit: |
+ ReleaseMem(pbNewData); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaFreeStream() - frees a byte stream |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaFreeStream( |
+ __in BYTE* pbData |
+ ) |
+{ |
+ if (!pbData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = MemFree(pbData); |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaGetRecordStream() - gets a byte stream field from record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaGetRecordStream( |
+ __in MSIHANDLE hRecBinary, |
+ __in UINT uiField, |
+ __deref_out_bcount_full(*pcbData) BYTE** ppbData, |
+ __out DWORD* pcbData |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ |
+ if (!hRecBinary || !ppbData || !pcbData) |
+ return E_INVALIDARG; |
+ |
+ *pcbData = 0; |
+ er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData); |
+ ExitOnWin32Error(er, hr, "failed to get size of stream"); |
+ |
+ hr = WcaAllocStream(ppbData, *pcbData); |
+ ExitOnFailure(hr, "failed to allocate data for stream"); |
+ |
+ er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData); |
+ ExitOnWin32Error(er, hr, "failed to read from stream"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaSetRecordString() - set a string field in record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaSetRecordString( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __in_z LPCWSTR wzData |
+ ) |
+{ |
+ if (!hRec || !wzData) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData); |
+ ExitOnWin32Error(er, hr, "failed to set string in record"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaSetRecordInteger() - set a integer field in record |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaSetRecordInteger( |
+ __in MSIHANDLE hRec, |
+ __in UINT uiField, |
+ __in int iValue |
+ ) |
+{ |
+ if (!hRec) |
+ return E_INVALIDARG; |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue); |
+ ExitOnWin32Error(er, hr, "failed to set integer in record"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+ |
+WcaDoDeferredAction() - schedules an action at this point in the script |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaDoDeferredAction( |
+ __in_z LPCWSTR wzAction, |
+ __in_z LPCWSTR wzCustomActionData, |
+ __in UINT uiCost |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ UINT er; |
+ |
+ if (wzCustomActionData && *wzCustomActionData) |
+ { |
+ er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData); |
+ ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action"); |
+ } |
+ |
+ if (0 < uiCost) |
+ { |
+ hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar |
+ // TODO: handle the return codes correctly |
+ } |
+ |
+ er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction); |
+ if (ERROR_INSTALL_USEREXIT == er) |
+ { |
+ WcaSetReturnValue(er); |
+ } |
+ ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaCountOfCustomActionDataRecords() - counts the number of records |
+passed to a deferred CustomAction |
+ |
+********************************************************************/ |
+extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords( |
+ __in_z LPCWSTR wzData |
+ ) |
+{ |
+ WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator |
+ DWORD dwCount = 0; |
+ |
+ // Loop through until there are no delimiters, we are at the end of the string, or the delimiter is the last character in the string |
+ for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim)) |
+ { |
+ ++dwCount; |
+ ++pwzCurrent; |
+ } |
+ |
+ return dwCount; |
+} |
+ |
+ |
+/******************************************************************** |
+BreakDownCustomActionData() - internal helper to chop up CustomActionData |
+ |
+NOTE: this modifies the passed in data |
+********************************************************************/ |
+static LPWSTR BreakDownCustomActionData( |
+ __inout LPWSTR* ppwzData |
+ ) |
+{ |
+ if (!ppwzData) |
+ return NULL; |
+ if (0 == *ppwzData) |
+ return NULL; |
+ |
+ WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator |
+ |
+ LPWSTR pwzReturn = *ppwzData; |
+ LPWSTR pwz = wcsstr(pwzReturn, delim); |
+ if (pwz) |
+ { |
+ *pwz = 0; |
+ *ppwzData = pwz + 1; |
+ } |
+ else |
+ *ppwzData = 0; |
+ |
+ return pwzReturn; |
+} |
+ |
+ |
+/******************************************************************** |
+RevertCustomActionData() - Reverts custom action data changes made |
+ by BreakDownCustomActionData; |
+ |
+NOTE: this modifies the passed in data |
+********************************************************************/ |
+extern "C" void WIXAPI RevertCustomActionData( |
+ __in LPWSTR wzRevertTo, |
+ __in LPCWSTR wzRevertFrom |
+ ) |
+{ |
+ if (!wzRevertTo) |
+ return; |
+ if (!wzRevertFrom) |
+ return; |
+ // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM |
+ for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++) |
+ { |
+ if (0 == *wzIndex) |
+ { |
+ *wzIndex = MAGIC_MULTISZ_DELIM; |
+ } |
+ } |
+ return; |
+} |
+ |
+/******************************************************************** |
+WcaReadStringFromCaData() - reads a string out of the CustomActionData |
+ |
+NOTE: this modifies the passed in ppwzCustomActionData variable |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaReadStringFromCaData( |
+ __deref_in LPWSTR* ppwzCustomActionData, |
+ __deref_out_z LPWSTR* ppwzString |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ |
+ LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); |
+ if (!pwz) |
+ return E_NOMOREITEMS; |
+ |
+ hr = StrAllocString(ppwzString, pwz, 0); |
+ ExitOnFailure(hr, "failed to allocate memory for string"); |
+ |
+ hr = S_OK; |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData |
+ |
+NOTE: this modifies the passed in ppwzCustomActionData variable |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData( |
+ __deref_in LPWSTR* ppwzCustomActionData, |
+ __out int* piResult |
+ ) |
+{ |
+ LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); |
+ if (!pwz || 0 == wcslen(pwz)) |
+ return E_NOMOREITEMS; |
+ |
+ *piResult = wcstol(pwz, NULL, 10); |
+ return S_OK; |
+} |
+ |
+ |
+ |
+ |
+/******************************************************************** |
+WcaWriteStringToCaData() - adds a string to the CustomActionData to |
+feed a deferred CustomAction |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaWriteStringToCaData( |
+ __in_z LPCWSTR wzString, |
+ __deref_inout_z_opt LPWSTR* ppwzCustomActionData |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator |
+ |
+ if (!ppwzCustomActionData) |
+ { |
+ ExitFunction1(hr = E_INVALIDARG); |
+ } |
+ |
+ DWORD cchString = lstrlenW(wzString) + 1; // assume we'll be adding the delim |
+ DWORD_PTR cchCustomActionData = 0; |
+ |
+ if (*ppwzCustomActionData) |
+ { |
+ hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData); |
+ ExitOnFailure(hr, "failed to get length of custom action data"); |
+ } |
+ |
+ if ((cchCustomActionData - lstrlenW(*ppwzCustomActionData)) < cchString + 1) |
+ { |
+ cchCustomActionData += cchString + 1 + 255; // add 255 for good measure |
+ hr = StrAlloc(ppwzCustomActionData, cchCustomActionData); |
+ ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string"); |
+ } |
+ |
+ if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end |
+ { |
+ hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim); |
+ ExitOnFailure(hr, "Failed to concatenate CustomActionData string"); |
+ } |
+ |
+ hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString); |
+ ExitOnFailure(hr, "Failed to concatenate CustomActionData string"); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to |
+feed a deferred CustomAction |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData( |
+ __in int i, |
+ __deref_out_z_opt LPWSTR* ppwzCustomActionData |
+ ) |
+{ |
+ WCHAR wzBuffer[13]; |
+ StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i); |
+ |
+ return WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData); |
+} |
+ |
+ |
+ |
+/******************************************************************** |
+WcaAddTempRecord - adds a temporary record to the active database |
+ |
+NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters |
+NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique |
+********************************************************************/ |
+extern "C" HRESULT __cdecl WcaAddTempRecord( |
+ __inout MSIHANDLE* phTableView, |
+ __inout MSIHANDLE* phColumns, |
+ __in_z LPCWSTR wzTable, |
+ __out_opt MSIDBERROR* pdbError, |
+ __in UINT uiUniquifyColumn, |
+ __in UINT cColumns, |
+ ... |
+ ) |
+{ |
+ Assert(phTableView && phColumns); |
+ |
+ static DWORD dwUniquifyValue = ::GetTickCount(); |
+ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ |
+ LPWSTR pwzQuery = NULL; |
+ PMSIHANDLE hTempRec; |
+ DWORD i; |
+ va_list args; |
+ |
+ LPWSTR pwzData = NULL; |
+ LPWSTR pwzUniquify = NULL; |
+ |
+ // |
+ // if we don't have a table and its columns already |
+ // |
+ if (NULL == *phTableView) |
+ { |
+ // set the query |
+ hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); |
+ ExitOnFailure(hr, "failed to allocate string for query"); |
+ |
+ // Open and Execute the temp View |
+ hr = WcaOpenExecuteView(pwzQuery, phTableView); |
+ ExitOnFailure1(hr, "failed to openexecute temp view with query %ls", pwzQuery); |
+ } |
+ |
+ if (NULL == *phColumns) |
+ { |
+ // use GetColumnInfo to populate the datatype record |
+ er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns); |
+ ExitOnWin32Error1(er, hr, "failed to columns for table: %ls", wzTable); |
+ } |
+ AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table"); |
+ |
+ // |
+ // create the temp record |
+ // |
+ hTempRec = ::MsiCreateRecord(cColumns); |
+ ExitOnNull1(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable); |
+ |
+ // |
+ // loop through all the columns filling in the data |
+ // |
+ va_start(args, cColumns); |
+ for (i = 1; i <= cColumns; i++) |
+ { |
+ hr = WcaGetRecordString(*phColumns, i, &pwzData); |
+ ExitOnFailure1(hr, "failed to get the data type for %d", i); |
+ |
+ // if data type is string write string |
+ if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData) |
+ { |
+ LPCWSTR wz = va_arg(args, WCHAR*); |
+ |
+ // if this is the column that is supposed to be unique add the time stamp on the end |
+ if (uiUniquifyColumn == i) |
+ { |
+ hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name |
+ ExitOnFailure1(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn); |
+ |
+ wz = pwzUniquify; |
+ } |
+ |
+ er = ::MsiRecordSetStringW(hTempRec, i, wz); |
+ ExitOnWin32Error1(er, hr, "failed to set string value at position %d", i); |
+ } |
+ // if data type is integer write integer |
+ else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData) |
+ { |
+ AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column"); |
+ int iData = va_arg(args, int); |
+ |
+ er = ::MsiRecordSetInteger(hTempRec, i, iData); |
+ ExitOnWin32Error1(er, hr, "failed to set integer value at position %d", i); |
+ } |
+ else |
+ { |
+ // not supporting binary streams so error out |
+ hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); |
+ ExitOnRootFailure2(hr, "unsupported data type '%ls' in column: %d", pwzData, i); |
+ } |
+ } |
+ va_end(args); |
+ |
+ // |
+ // add the temporary record to the MSI |
+ // |
+ er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec); |
+ hr = HRESULT_FROM_WIN32(er); |
+ if (FAILED(hr)) |
+ { |
+ if (pdbError) |
+ { |
+ // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row |
+ // can't be inserted; if we're being asked to provide the detailed error, |
+ // get it using the MSIMODIFY_VALIDATE_NEW flag |
+ er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec); |
+ hr = HRESULT_FROM_WIN32(er); |
+ } |
+ |
+ WCHAR wzBuf[MAX_PATH]; |
+ DWORD cchBuf = countof(wzBuf); |
+ MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf); |
+ if (pdbError) |
+ { |
+ *pdbError = dbErr; |
+ } |
+ ExitOnFailure2(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf); |
+ } |
+ |
+LExit: |
+ ReleaseStr(pwzUniquify); |
+ ReleaseStr(pwzData); |
+ ReleaseStr(pwzQuery); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+WcaDumpTable - dumps a table to the log file |
+ |
+********************************************************************/ |
+extern "C" HRESULT WIXAPI WcaDumpTable( |
+ __in_z LPCWSTR wzTable |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ UINT er = ERROR_SUCCESS; |
+ |
+ LPWSTR pwzQuery = NULL; |
+ PMSIHANDLE hView; |
+ PMSIHANDLE hColumns; |
+ DWORD cColumns = 0; |
+ PMSIHANDLE hRec; |
+ |
+ LPWSTR pwzData = NULL; |
+ LPWSTR pwzPrint = NULL; |
+ |
+ hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); |
+ ExitOnFailure(hr, "failed to allocate string for query"); |
+ |
+ // Open and Execute the temp View |
+ hr = WcaOpenExecuteView(pwzQuery, &hView); |
+ ExitOnFailure1(hr, "failed to openexecute temp view with query %ls", pwzQuery); |
+ |
+ // Use GetColumnInfo to populate the names of the columns. |
+ er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns); |
+ hr = HRESULT_FROM_WIN32(er); |
+ ExitOnFailure1(hr, "failed to get column info for table: %ls", wzTable); |
+ |
+ cColumns = ::MsiRecordGetFieldCount(hColumns); |
+ |
+ WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable); |
+ |
+ // Loop through all the columns filling in the data. |
+ for (DWORD i = 1; i <= cColumns; i++) |
+ { |
+ hr = WcaGetRecordString(hColumns, i, &pwzData); |
+ ExitOnFailure1(hr, "failed to get the column name for %d", i); |
+ |
+ hr = StrAllocConcat(&pwzPrint, pwzData, 0); |
+ ExitOnFailure(hr, "Failed to add column name."); |
+ |
+ hr = StrAllocConcat(&pwzPrint, L"\t", 1); |
+ ExitOnFailure(hr, "Failed to add column name."); |
+ } |
+ |
+ WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); |
+ |
+ // Now dump the actual rows. |
+ while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) |
+ { |
+ if (pwzPrint && *pwzPrint) |
+ { |
+ *pwzPrint = L'\0'; |
+ } |
+ |
+ for (DWORD i = 1; i <= cColumns; i++) |
+ { |
+ hr = WcaGetRecordString(hRec, i, &pwzData); |
+ ExitOnFailure1(hr, "failed to get the column name for %d", i); |
+ |
+ hr = StrAllocConcat(&pwzPrint, pwzData, 0); |
+ ExitOnFailure(hr, "Failed to add column name."); |
+ |
+ hr = StrAllocConcat(&pwzPrint, L"\t", 1); |
+ ExitOnFailure(hr, "Failed to add column name."); |
+ } |
+ |
+ WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); |
+ } |
+ |
+ WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable); |
+ |
+LExit: |
+ ReleaseStr(pwzPrint); |
+ ReleaseStr(pwzData); |
+ ReleaseStr(pwzQuery); |
+ |
+ return hr; |
+} |
+ |
+ |
+HRESULT WIXAPI WcaDeferredActionRequiresReboot() |
+{ |
+ HRESULT hr = S_OK; |
+ ATOM atomReboot = 0; |
+ |
+ atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot"); |
+ ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom."); |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+BOOL WIXAPI WcaDidDeferredActionRequireReboot() |
+{ |
+ // NOTE: This function does not delete the global atom. That is done |
+ // purposefully so that any other installs that occur after this point also |
+ // require a reboot. |
+ ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot"); |
+ return 0 != atomReboot; |
+} |