HRESULT CpiActionStartMessage( LPWSTR* ppwzActionData, BOOL fSuppress ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hRec; LPWSTR pwzData = NULL; // create record hRec = ::MsiCreateRecord(3); ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); // action name hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); ExitOnFailure(hr, "Failed to action name"); er = ::MsiRecordSetStringW(hRec, 1, pwzData); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set action name"); // description hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); ExitOnFailure(hr, "Failed to description"); er = ::MsiRecordSetStringW(hRec, 2, pwzData); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set description"); // template hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); ExitOnFailure(hr, "Failed to template"); er = ::MsiRecordSetStringW(hRec, 3, pwzData); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set template"); // message if (!fSuppress) { er = WcaProcessMessage(INSTALLMESSAGE_ACTIONSTART, 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: // clean up ReleaseStr(pwzData); return hr; }
/******************************************************************** WcaLog() - outputs trace and log info *******************************************************************/ extern "C" void __cdecl WcaLog( __in LOGLEVEL llv, __in_z __format_string PCSTR fmt, ... ) { static char szFmt[LOG_BUFFER]; static char szBuf[LOG_BUFFER]; static bool fInLogPrint = false; // prevent re-entrant logprints. (recursion issues between assert/logging code) if (fInLogPrint) return; fInLogPrint = true; if (LOGMSG_STANDARD == llv || (LOGMSG_VERBOSE == llv && IsVerboseLoggingLite()) #ifdef DEBUG || LOGMSG_TRACEONLY == llv #endif ) { va_list args; va_start(args, fmt); LPCSTR szLogName = WcaGetLogName(); if (szLogName[0] != 0) StringCchPrintfA(szFmt, countof(szFmt), "%s: %s", szLogName, fmt); else StringCchCopyA(szFmt, countof(szFmt), fmt); StringCchVPrintfA(szBuf, countof(szBuf), szFmt, args); va_end(args); #ifdef DEBUG // always write to the log in debug #else if (llv == LOGMSG_STANDARD || (llv == LOGMSG_VERBOSE && IsVerboseLoggingLite())) #endif { PMSIHANDLE hrec = MsiCreateRecord(1); ::MsiRecordSetStringA(hrec, 0, szBuf); // TODO: Recursion on failure. May not be safe to assert from here. WcaProcessMessage(INSTALLMESSAGE_INFO, hrec); } #if DEBUG StringCchCatA(szBuf, countof(szBuf), "\n"); OutputDebugStringA(szBuf); #endif } fInLogPrint = false; return; }
/****************************************************************** PromptToContinue - displays the prompt if the application is still running. ******************************************************************/ static HRESULT PromptToContinue( __in_z LPCWSTR wzApplication, __in_z LPCWSTR wzPrompt ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hRecMessage = NULL; DWORD *prgProcessIds = NULL; DWORD cProcessIds = 0; hRecMessage = ::MsiCreateRecord(1); ExitOnNull(hRecMessage, hr, E_OUTOFMEMORY, "Failed to create record for prompt."); er = ::MsiRecordSetStringW(hRecMessage, 0, wzPrompt); ExitOnWin32Error(er, hr, "Failed to set prompt record field string"); do { hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds); if (SUCCEEDED(hr) && 0 < cProcessIds) { er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_ICONWARNING), hRecMessage); if (IDABORT == er) { hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); } else if (IDRETRY == er) { hr = S_FALSE; } else if (IDIGNORE == er) { hr = S_OK; } else { ExitOnWin32Error(er, hr, "Unexpected return value from prompt to continue."); } } ReleaseNullMem(prgProcessIds); cProcessIds = 0; } while (S_FALSE == hr); LExit: ReleaseMem(prgProcessIds); return hr; }
HRESULT CpiActionDataMessage( DWORD cArgs, ... ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hRec; va_list args; // record hRec = ::MsiCreateRecord(cArgs); ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); va_start(args, cArgs); for (DWORD i = 1; i <= cArgs; i++) { LPCWSTR pwzArg = va_arg(args, WCHAR*); if (pwzArg && *pwzArg) { er = ::MsiRecordSetStringW(hRec, i, pwzArg); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set record field string"); } } va_end(args); // message er = WcaProcessMessage(INSTALLMESSAGE_ACTIONDATA, 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: return hr; }
/******************************************************************** 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; }