public void MsiThrowOnFailure(IntPtr hDatabase, uint retVal, string message) { if (retVal != CwMsiWin32.ERROR_SUCCESS) { IntPtr hView = IntPtr.Zero; IntPtr hRecord = IntPtr.Zero; hRecord = CwMsiWin32.MsiGetLastErrorRecord(); //try to get a handle to the error record string retString = message + "( SYSTEM ERROR CODE " + retVal + ")."; if (hRecord != IntPtr.Zero) { uint errCode = CwMsiWin32.MsiRecordGetInteger(hRecord, 1); //MSDN says field 1 is the err code retString += "\n\nError Table data (code=" + errCode + ")"; IntPtr lpBuffer = Marshal.AllocHGlobal(4096); uint pcchResultBuf = 4096; //format the record string CwMsiWin32.MsiFormatRecord(IntPtr.Zero, hRecord, lpBuffer, ref pcchResultBuf); //add it to our main return string retString += "'" + Marshal.PtrToStringAnsi(lpBuffer) + "'"; //cleanup duty Marshal.FreeHGlobal(lpBuffer); CwMsiWin32.MsiCloseHandle(hRecord); //discard the error record handle now that we have the code hRecord = IntPtr.Zero; } throw new Exception(retString); } }