///////////////////////////////////////////////////////////////////// // // Function: LogMessage // // Description: This function writes to the MSI log file and displays // the SetupError dialog box as appropriate. // ///////////////////////////////////////////////////////////////////// UINT BOINCCABase::LogMessage( const UINT uiInstallMessageType, // message type to send to Windows Installer const UINT uiPushButtonStyle, // push button sstyle to use in message box const UINT uiIconStyle, // icon style to use in message box const UINT uiErrorNumber, // number of error in Error table const UINT uiErrorCode, // the return value from an api const tstring strMessage // message ) { UINT uiReturnValue = 0; switch(uiInstallMessageType) { // Send informational message to the log file case INSTALLMESSAGE_INFO: MsiRecordSetString (m_phLogInfoRec, 2, strMessage.c_str()); MsiRecordSetInteger(m_phLogInfoRec, 3, uiErrorCode); // returns IDOK if successful uiReturnValue = MsiProcessMessage( m_hMSIHandle, INSTALLMESSAGE(uiInstallMessageType), m_phLogInfoRec ); break; // Display a dialog and send error message to log file case INSTALLMESSAGE_ERROR: case INSTALLMESSAGE_WARNING: case INSTALLMESSAGE_USER: PMSIHANDLE phLogErrorRec = MsiCreateRecord(2); MsiRecordSetString (phLogErrorRec, 0, _T("[1]")); MsiRecordSetString (phLogErrorRec, 1, strMessage.c_str()); // Return value to indicate which button is // pushed on message box uiReturnValue = MsiProcessMessage( m_hMSIHandle, INSTALLMESSAGE(uiInstallMessageType|uiPushButtonStyle|uiIconStyle), phLogErrorRec ); break; } return uiReturnValue; }
UINT Blip( MSIHANDLE hModule) // Handle of MSI being installed. [in] { // Set up variables. PMSIHANDLE hRecord = ::MsiCreateRecord(4); HANDLE_OK(hRecord) UINT uiAnswer = ::MsiRecordSetInteger(hRecord, 1, 2); MSI_OK(uiAnswer) uiAnswer = ::MsiRecordSetInteger(hRecord, 2, 0); MSI_OK(uiAnswer) uiAnswer = ::MsiRecordSetInteger(hRecord, 3, 0); MSI_OK(uiAnswer) uiAnswer = ::MsiRecordSetInteger(hRecord, 4, 0); MSI_OK(uiAnswer) // Send the message uiAnswer = ::MsiProcessMessage(hModule, INSTALLMESSAGE(INSTALLMESSAGE_PROGRESS), hRecord); // Corrects return value for use with MSI_OK. switch (uiAnswer) { case IDOK: case 0: // Means no action was taken... return ERROR_SUCCESS; case IDCANCEL: return ERROR_INSTALL_USEREXIT; default: return ERROR_INSTALL_FAILURE; } }
int MsiMessageBox(MSIHANDLE hInstall, TCHAR* szString, DWORD dwDlgFlags) { // I am not aware of anyway to programmatically set the dialog title through this code. // I think you may just have to author the Error Dialog in your .msi to use the text that you want. // chances are you will want to use [ProgramName] because the Error Dialog can get called from the MSI // it self, if it encounters an error, and it would be weird if you hard-coded the title bar to // "Desktop Shortcut?" or something like that. PMSIHANDLE newHandle = ::MsiCreateRecord(2); MsiRecordSetString(newHandle, 0, szString); return (MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER + dwDlgFlags), newHandle)); }
static void logStringW(MSIHANDLE hInstall, LPCWSTR pwszString, ...) { PMSIHANDLE newHandle = MsiCreateRecord(2); WCHAR szBuffer[1024]; va_list va; va_start(va, pwszString); _vsnwprintf(szBuffer, RT_ELEMENTS(szBuffer), pwszString, va); va_end(va); MsiRecordSetStringW(newHandle, 0, szBuffer); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), newHandle); MsiCloseHandle(newHandle); }
void LogString(MSIHANDLE hInstall, TCHAR* szString) { // if you are curious what the PMSIHANDLE is, look it up in the msi.chm. It is actually a good idea to use it // rather than MSIHANDLE. Basically it will free itself when it goes out of scope. In the past I helped // track a bug down to the fact that they weren't using it... PMSIHANDLE newHandle = ::MsiCreateRecord(2); TCHAR szTemp[MAX_PATH * 2]; wsprintf(szTemp, L"-- MSI_LOGGING -- %s", szString); MsiRecordSetString(newHandle, 0, szTemp); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), newHandle); // if you get one of those: cannot convert parameter 2 from 'enum tagINSTALLMESSAGE' to 'enum tagMSIMESSAGE' // errors on the line above, then chances are you: // 1) havent installed the latest MSI SDK (now apart of the Platform SDK) // or 2) havent added the \include\ and \lib\ directories to the Visual Studio path (tools | options, directory tab). }
static void logString(MSIHANDLE hInstall, LPCSTR szString, ...) { PMSIHANDLE newHandle = MsiCreateRecord(2); char szBuffer[1024]; va_list va; va_start(va, szString); _vsnprintf(szBuffer, sizeof(szBuffer), szString, va); va_end(va); MsiRecordSetStringA(newHandle, 0, szBuffer); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), newHandle); MsiCloseHandle(newHandle); #if 0/*def DEBUG - wrong? What does '%s' expect, wchar or char? */ _tprintf(_T("Debug: %s\n"), szBuffer); #endif }
////////////////////////////////////////////////////////////////////////////// // RemoveUserAccount // // Attempts to remove a user account on the local machine according // to the "instructions" provided in the CustomActionData property // // As a deferred custom action, you do not have access to the database. // The only source of information comes from a property that an immediate // custom action can set to provide the information you need. This // property is written into the script // UINT __stdcall RemoveUserAccount(MSIHANDLE hInstall) { // determine mode in which we are called BOOL bRollback = MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK); // true for rollback, else regular deferred version (for uninstall) BOOL fSuccess = FALSE; // id's for error and warning messages const int iRemoveError = 25003; const int iRemoveWarning = 25004; // Grab the CustomActionData property DWORD cchCAData = 0; if (ERROR_MORE_DATA == MsiGetPropertyW(hInstall, IPROPNAME_CUSTOMACTIONDATA, L"", &cchCAData)) { WCHAR* wszCAData = new WCHAR[++cchCAData]; // add 1 for null-terminator which is not included in size on return if (wszCAData) { if (ERROR_SUCCESS == MsiGetPropertyW(hInstall, IPROPNAME_CUSTOMACTIONDATA, wszCAData, &cchCAData)) { // send ActionData message (template in ActionText table) // send ActionData message (template in ActionText table) PMSIHANDLE hRec = MsiCreateRecord(1); if (!hRec || ERROR_SUCCESS != MsiRecordSetStringW(hRec, 1, wszCAData)) { delete [] wszCAData; return ERROR_INSTALL_FAILURE; } int iRet = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRec); if (IDCANCEL == iRet || IDABORT == iRet) { delete [] wszCAData; return ERROR_INSTALL_USEREXIT; } // // Call the NetUserDel function, // NET_API_STATUS nStatus = NetUserDel(NULL /*local machine*/, wszCAData /*user name*/); if (NERR_Success != nStatus) { PMSIHANDLE hRecErr = MsiCreateRecord(3); if ( !hRecErr || ERROR_SUCCESS != MsiRecordSetStringW(hRecErr, 2, wszCAData)) { delete [] wszCAData; return ERROR_INSTALL_FAILURE; } // In rollback mode, NERR_UserNotFound means cancel button depressed in middle of deferred CA trying to create this account if (bRollback && NERR_UserNotFound == nStatus) { fSuccess = TRUE; } else if (NERR_UserNotFound == nStatus) { // treat this as a warning, but success since we are attempting to delete and it is not present if (ERROR_SUCCESS != MsiRecordSetInteger(hRecErr, 1, iRemoveWarning)) { delete [] wszCAData; return ERROR_INSTALL_FAILURE; } // just pop up an OK button // OPTIONALLY, could specify multiple buttons and cancel // install based on user selection by handling the return value // from MsiProcessMessage, but here we are ignoring the MsiProcessMessage return MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING|MB_ICONWARNING|MB_OK), hRecErr); fSuccess = TRUE; } else { if (ERROR_SUCCESS == MsiRecordSetInteger(hRecErr, 1, iRemoveError) && ERROR_SUCCESS == MsiRecordSetInteger(hRecErr, 3, nStatus)) { // returning failure anyway, so ignoring MsiProcessMessage return MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecErr); } } } else // NERR_Success { fSuccess = TRUE; } } delete [] wszCAData; } } return fSuccess ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; }