[DllImport("msi.dll", CharSet=CharSet.Unicode)]
static extern int MsiRecordSetString(IntPtr hRecord, int iField, string szValue);
None.
Please add some!
The sample application below demonstrates how to use MsiRecordSetString, MsiOpenDatabase, MsiCreateRecord, MsiCloseHandle, MsiDatabaseOpenView, MsiViewExecute, MsiDatabaseCommit, and MsiViewClose in C# to modify a property value in an MSI database. To run it, simply create a new Windows Console application and replace the code that Visual Studio gives you with the code below. You may have to modify the namespace name. The sample as-is requires a valid MSI database named SETUP.msi, with a property named PROPERTY1 in the Property table, to be located in C:\. You can of course change the path to your MSI database in the calls to ChangeMSIProperty() in Main() and use any existing property you wish.
using System;
using System.Globalization;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
/////////////////////////////////////////////////////////////////////////////
// MsiInstallationSupportException class
/////////////////////////////////////////////////////////////////////////////
public class MsiInstallationSupportException : ApplicationException
{
// This class encapsulates and overloads exception handling.
public MsiInstallationSupportException() : base() { }
public MsiInstallationSupportException(string message, Exception innerException) : base(message, innerException) { }
public MsiInstallationSupportException(string message) : base(message) { }
}
/////////////////////////////////////////////////////////////////////////////
// Program class
/////////////////////////////////////////////////////////////////////////////
class Program
{
//*******************************************
// This enum contains common MSI error codes.
//*******************************************
private enum WINDOWS_MESSAGE_CODES
{
ERROR_SUCCESS = 0,
ERROR_INVALID_PARAMETER = 87,
ERROR_NO_MORE_ITEMS = 259,
ERROR_INSTALL_USEREXIT = 1602,
ERROR_INSTALL_FAILURE = 1603,
ERROR_BAD_CONFIGURATION = 1610,
ERROR_INSTALL_IN_PROGRESS = 1618,
ERROR_INSTALL_SOURCE_ABSENT = 1612,
ERROR_UNKNOWN_PRODUCT = 1605,
ERROR_FUNCTION_FAILED = 1627,
ERROR_INVALID_HANDLE_STATE = 1609,
ERROR_MORE_DATA = 234,
ERROR_UNKNOWN_PROPERTY = 1608,
ERROR_CREATE_FAILED = 1631,
ERROR_OPEN_FAILED = 110,
ERROR_BAD_QUERY_SYNTAX = 1615
}
//***********************************
// DllImports for the MSI API's used.
//***********************************
[DllImport("msi.dll", SetLastError = true)]
static extern int MsiOpenDatabase(string szDatabasePath, IntPtr phPersist, out IntPtr phDatabase);
[DllImport("msi.dll", ExactSpelling = true)]
static extern IntPtr MsiCreateRecord(uint cParams);
[DllImport("msi.dll", ExactSpelling = true)]
static extern int MsiCloseHandle(IntPtr hAny);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
static extern int MsiDatabaseOpenViewW(IntPtr hDatabase, [MarshalAs(UnmanagedType.LPWStr)] string szQuery, out IntPtr phView);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
static extern int MsiViewExecute(IntPtr hView, IntPtr hRecord);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
static extern int MsiDatabaseCommit(IntPtr hDatabase);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
static extern int MsiRecordSetString(IntPtr hRecord, int iField, string szValue);
[DllImport("msi.dll")]
static extern int MsiViewClose(IntPtr viewhandle);
//*****************************************
// Open mode constants for MsiOpenDatabase.
//*****************************************
const int MSIDBOPEN_READONLY = 0; // database open read-only, no persistent changes
const int MSIDBOPEN_TRANSACT = 1; // database read/write in transaction mode
const int MSIDBOPEN_DIRECT = 2; // database direct read/write without transaction
const int MSIDBOPEN_CREATE = 3; // create new database, transact mode read/write
const int MSIDBOPEN_CREATEDIRECT = 4; // create new database, direct mode read/write
//*********************
// Program entry point.
//*********************
static void Main(string[] args)
{
ChangeMSIProperty(@"C:\SETUP.msi", "PROPERTY1", "1"); // Set property "PROPERTY1" to a value of 1.
ChangeMSIProperty(@"C:\SETUP.msi", "PROPERTY1", "0"); // Now set property "PROPERTY1" to a value of 1.
}
//***************************************************
// The method to call to set/change a property value.
//***************************************************
static void ChangeMSIProperty(string path, string property, string value)
{
// The sql command. Note the "?" placeholder for the value.
string sql = @"UPDATE Property SET Value = ? WHERE Property = '" + property + "'";
IntPtr msiHandle = IntPtr.Zero;
IntPtr msiRecord = IntPtr.Zero;
IntPtr msiView = IntPtr.Zero;
int iOpenMode = MSIDBOPEN_DIRECT;
IntPtr persist = new IntPtr(iOpenMode);
try
{
// Open the database for read/write access.
WINDOWS_MESSAGE_CODES returnValue = (WINDOWS_MESSAGE_CODES)MsiOpenDatabase(path, persist, out msiHandle);
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiOpenDatabase returned error code {0}.", returnValue.ToString()));
// If the returnValue error code is defined in the enum WINDOWS_MESSAGE_CODES, the exception message will show the enum code name; otherwise it will show the error number.
// Create a record to be used in tandem with the view below.
msiRecord = MsiCreateRecord(1);
if (msiHandle == IntPtr.Zero)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiCreateRecord failed to return a valid record handle."));
// Set field 1 of your record to the value to set in the database.
returnValue = (WINDOWS_MESSAGE_CODES)MsiRecordSetString(msiRecord, (int)1, value);
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiRecordSetString returned error code {0}.", returnValue.ToString()));
// Open a view to use to apply the change. Here's where you specify your sql command.
returnValue = (WINDOWS_MESSAGE_CODES)MsiDatabaseOpenViewW(msiHandle, sql, out msiView);
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiDatabaseOpenViewW returned error code {0}.", returnValue.ToString()));
// Execute the view, passing it the record set. If the property does not exist, it will NOT be created and there will be NO error thrown.
returnValue = (WINDOWS_MESSAGE_CODES)MsiViewExecute(msiView, msiRecord);
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiViewExecute returned error code {0}.", returnValue.ToString()));
// Close the view.
returnValue = (WINDOWS_MESSAGE_CODES)MsiViewClose(msiView);
if (returnValue == WINDOWS_MESSAGE_CODES.ERROR_SUCCESS) // note prior to 7/11/2010, this was != instead of ==, causing MSI to be inconsistent. Commit seems required. Edited by txcraig
{
// Commit the changes.
returnValue = (WINDOWS_MESSAGE_CODES)MsiDatabaseCommit(msiHandle);
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiDatabaseCommit returned error code {0}.", returnValue.ToString()));
}
else
{
// Failed to close the view.
if (returnValue != WINDOWS_MESSAGE_CODES.ERROR_SUCCESS)
throw new MsiInstallationSupportException(string.Format(CultureInfo.InvariantCulture, "MsiViewClose returned error code {0}.", returnValue.ToString()));
}
}
finally
{
// Close handles or you could get a corrupted database and un-closed handles.
if (msiRecord != IntPtr.Zero)
MsiCloseHandle(msiRecord);
if (msiView != IntPtr.Zero)
MsiCloseHandle(msiView);
if (msiHandle != IntPtr.Zero)
MsiCloseHandle(msiHandle);
}
}
}
}
MSI interop library
http://sourceforge.net/project/showfiles.php?group_id=40188&package_id=110212