Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: src/installer-ca/wcawrap.cpp

Issue 11521026: initial custom action library, "hello, world" quality (Closed)
Patch Set: Created Sept. 3, 2013, 12:48 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+}

Powered by Google App Engine
This is Rietveld