/// <summary> /// Gets a summary information property. /// </summary> /// <param name="index">Index of the summary information property.</param> /// <returns>The summary information property.</returns> public string GetProperty(int index) { uint dataType; StringBuilder stringValue = new StringBuilder(""); int bufSize = 0; int intValue; FILETIME timeValue; timeValue.dwHighDateTime = 0; timeValue.dwLowDateTime = 0; int error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); if (234 == error) { stringValue.EnsureCapacity(++bufSize); error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); } if (0 != error) { throw new MsiException(error); } switch ((VT)dataType) { case VT.EMPTY: return(String.Empty); case VT.LPSTR: return(stringValue.ToString()); case VT.I2: case VT.I4: return(Convert.ToString(intValue, CultureInfo.InvariantCulture)); case VT.FILETIME: long longFileTime = (((long)timeValue.dwHighDateTime) << 32) | unchecked ((uint)timeValue.dwLowDateTime); DateTime dateTime = DateTime.FromFileTime(longFileTime); return(dateTime.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture)); default: throw new InvalidOperationException(); } }
public static Dictionary <string, object> GetFieldValues(this IntPtr view, IntPtr record) { IntPtr names; var info = (IntPtr)MsiInterop.MsiViewGetColumnInfo(view, MsiColInfoType.Names, out names); var result = new Dictionary <string, object>(); for (uint i = 0; i <= MsiInterop.MsiRecordGetFieldCount(names); i++) { string name = names.GetString(i); result[name] = record.GetObject(i); } info.Close(); names.Close(); return(result); }
public static object GetObject(this IntPtr record, uint fieldIndex) { if (MsiInterop.MsiRecordIsNull(record, fieldIndex)) { return(null); } int result = record.GetInt(fieldIndex); if (result == MsiInterop.MsiNullInteger) //the field is s string { return(record.GetString(fieldIndex)); } else { return(result); } }
/// <summary> /// Gets string value at specified location. /// </summary> /// <param name="field">Index into record to get string.</param> /// <returns>String value</returns> public string GetString(int field) { int bufferSize = 255; StringBuilder buffer = new StringBuilder(bufferSize); uint error = MsiInterop.MsiRecordGetString(this.handle, field, buffer, ref bufferSize); if (234 == error) { buffer.EnsureCapacity(++bufferSize); error = MsiInterop.MsiRecordGetString(this.handle, field, buffer, ref bufferSize); } if (0 != error) { throw new System.Runtime.InteropServices.ExternalException("Failed to get string from record", (int)error); } return(buffer.ToString()); }
/// <summary> /// Opens an MSI database. /// </summary> /// <param name="path">Path to the database to be opened.</param> /// <param name="type">Persist mode to use when opening the database.</param> public void Open(string path, OpenDatabase type) { if (IntPtr.Zero != handle) { throw new ArgumentException("Database already open"); } uint er = MsiInterop.MsiOpenDatabase(path, (int)type, out handle); if (110 == er) { throw new System.IO.IOException(String.Concat("Failed to open Windows Installer database: ", path)); } else if (0 != er) { throw new System.Runtime.InteropServices.ExternalException(String.Format("Failed to open database: {0}", path), (int)er); } }
/// <summary> /// Imports an installer text archive table (idt file) into an open database. /// </summary> /// <param name="folderPath">Specifies the path to the folder containing archive files.</param> /// <param name="fileName">Specifies the name of the file to import.</param> public void Import(string folderPath, string fileName) { if (IntPtr.Zero == handle) { throw new ArgumentException("Invalid handle"); } uint er = MsiInterop.MsiDatabaseImport(handle, folderPath, fileName); if (1627 == er) { throw new System.Configuration.ConfigurationException("Invalid IDT file", String.Concat(folderPath, "\\", fileName), 0); } else if (0 != er) { throw new System.Runtime.InteropServices.ExternalException("Failed to Import file", (int)er); } }
/// <summary> /// Get stream at specified location. /// </summary> /// <param name="field">Index into record to get stream.</param> /// <param name="buffer">buffer to receive bytes from stream.</param> /// <param name="requestedBufferSize">Buffer size to read.</param> /// <returns>Stream read into string.</returns> public int GetStream(int field, byte[] buffer, int requestedBufferSize) { int bufferSize = 255; if (requestedBufferSize > 0) { bufferSize = requestedBufferSize; } int error = MsiInterop.MsiRecordReadStream(this.Handle, field, buffer, ref bufferSize); if (0 != error) { throw new Win32Exception(error); } return(bufferSize); }
/// <summary> /// Gets string value at specified location. /// </summary> /// <param name="field">Index into record to get string.</param> /// <returns>String value</returns> public string GetString(int field) { int bufferSize = 255; StringBuilder buffer = new StringBuilder(bufferSize); int error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize); if (234 == error) { buffer.EnsureCapacity(++bufferSize); error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize); } if (0 != error) { throw new Win32Exception(error); } return(0 < buffer.Length ? buffer.ToString() : null); }
/// <summary> /// Determines whether the specified product code is installed. /// </summary> /// <param name="productCode">The product code.</param> /// <returns>Returns <c>true</c> if the product is installed. Otherwise returns <c>false</c>.</returns> public static bool IsInstalled(string productCode) { StringBuilder sb = new StringBuilder(2048); uint size = 2048; MsiError err = MsiInterop.MsiGetProductInfo(productCode, "InstallDate", sb, ref size); if (err == MsiError.UnknownProduct) { return(false); } else if (err == MsiError.NoError) { return(true); } else { throw new Exception(err.ToString()); } }
/// <summary> /// Embeds a language transformation (mst file) in the specified msi file. /// </summary> /// <param name="msi">The MSI file.</param> /// <param name="mst">The MST file.</param> public static void Do(string msi, string mst) { var lngId = new CultureInfo(Path.GetFileNameWithoutExtension(mst)).LCID.ToString(); MsiInterop.MsiOpenDatabase(msi, MsiDbPersistMode.ReadWrite, out IntPtr db).check(nameof(MsiInterop.MsiOpenDatabase)); MsiInterop.MsiDatabaseOpenView(db, "SELECT `Name`,`Data` FROM _Storages", out IntPtr view).check(nameof(MsiInterop.MsiDatabaseOpenView)); var record = MsiInterop.MsiCreateRecord(2); MsiInterop.MsiRecordSetString(record, 1, lngId).check(nameof(MsiInterop.MsiRecordSetString)); MsiInterop.MsiRecordSetStream(record, 2, mst).check(nameof(MsiInterop.MsiRecordSetStream)); MsiInterop.MsiViewExecute(view, record).check(nameof(MsiInterop.MsiViewExecute)); MsiInterop.MsiViewModify(view, MsiModifyMode.ModifyAssign, record).check(nameof(MsiInterop.MsiViewModify)); MsiInterop.MsiDatabaseCommit(db).check(nameof(MsiInterop.MsiDatabaseCommit)); MsiInterop.MsiCloseHandle(view).check(nameof(MsiInterop.MsiCloseHandle)); MsiInterop.MsiCloseHandle(db).check(nameof(MsiInterop.MsiCloseHandle)); }
/// <summary> /// Exports an installer table from an open database to a text archive file (idt file). /// </summary> /// <param name="tableName">Specifies the name of the table to export.</param> /// <param name="folderPath">Specifies the name of the folder that contains archive files. If null or empty string, uses current directory.</param> /// <param name="fileName">Specifies the name of the exported table archive file.</param> public void Export(string tableName, string folderPath, string fileName) { if (IntPtr.Zero == handle) { throw new ArgumentException("Invalid handle"); } if (null == folderPath || 0 == folderPath.Length) { folderPath = System.Environment.CurrentDirectory; } uint er = MsiInterop.MsiDatabaseExport(handle, tableName, folderPath, fileName); if (0 != er) { throw new System.Runtime.InteropServices.ExternalException(String.Format("Failed to Export file: {0}", er), (int)er); } }
private Guid[] EnumRelatedProducts(string UpgradeCode) { Guid[] guids = new Guid[0]; MsiError ret; uint i = 0; StringBuilder buffer = new StringBuilder(38); while ((ret = MsiInterop.MsiEnumRelatedProducts(UpgradeCode, 0, i, buffer)) == 0) { Guid[] temp = new Guid[i + 1]; Array.Copy(guids, temp, i); temp[i] = new Guid(buffer.ToString()); guids = temp; i++; } return(guids); }
/// <summary> /// Takes the path to a file and returns a 128-bit hash of that file. /// </summary> /// <param name="filePath">Path to file that is to be hashed.</param> /// <param name="options">The value in this column must be 0. This parameter is reserved for future use.</param> /// <param name="hash">Int array that receives the returned file hash information.</param> internal static void GetFileHash(string filePath, int options, out int[] hash) { MsiInterop.MSIFILEHASHINFO hashInterop = new MsiInterop.MSIFILEHASHINFO(); hashInterop.FileHashInfoSize = 20; int error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), ref hashInterop); if (0 != error) { throw new Win32Exception(error); } Debug.Assert(20 == hashInterop.FileHashInfoSize); hash = new int[4]; hash[0] = hashInterop.Data0; hash[1] = hashInterop.Data1; hash[2] = hashInterop.Data2; hash[3] = hashInterop.Data3; }
/// <summary> /// Enumerate all installed product GUIDs /// </summary> /// <returns>An enumerator that returns all installed products GUIDs</returns> public static IEnumerable <Guid> EnumerateGUIDs() { MsiExitCodes ret = 0; uint i = 0, dummy2 = 0; do { string guid = new string(new char[39]); object dummy1; ret = MsiInterop.MsiEnumProductsEx(null, null, InstallContext.All, i, guid, out dummy1, null, ref dummy2); if (ret == MsiExitCodes.Success) { if (Guid.TryParse(guid.TrimEnd('\0'), out var result)) { yield return(result); } } i++; } while (ret != MsiExitCodes.NoMoreItems); yield break; }
private static bool IsAlreadyInstalled(Product product, out List <SemVersion> installedVersions) { installedVersions = new List <SemVersion>(); var upgradeCode = FormatProductCode(GetUpgradeCode(product).ToString()); var productCodes = GetProductCodes(product); var productGuids = productCodes.ToDictionary(d => d.Value, d => d.Key); var productCodeBuilder = new StringBuilder(39); for (var productIndex = 0;; productIndex++) { var error = MsiInterop.MsiEnumRelatedProducts(upgradeCode, 0, productIndex, productCodeBuilder); if (error != MsiError.NoError) { break; } var productCode = productCodeBuilder.ToString(); if (Guid.TryParse(productCode, out var productGuid)) { if (productGuids.TryGetValue(productGuid, out var version)) { installedVersions.Add(version); } else { var productVersion = new StringBuilder(1024); error = GetVersion(product, productCode, productVersion); // TODO: what should we do in the case of any other error value? if (error == MsiError.NoError) { installedVersions.Add(productVersion.ToString()); } } } } return(installedVersions.Any()); }
/// <summary> /// Takes the path to a file and returns a 128-bit hash of that file. /// </summary> /// <param name="filePath">Path to file that is to be hashed.</param> /// <param name="options">The value in this column must be 0. This parameter is reserved for future use.</param> /// <param name="hash">Int array that receives the returned file hash information.</param> public static void GetFileHash(string filePath, int options, out int[] hash) { MsiInterop.MSIFILEHASHINFO hashInterop = new MsiInterop.MSIFILEHASHINFO(); hashInterop.fileHashInfoSize = 20; uint er = MsiInterop.MsiGetFileHash(filePath, 0, ref hashInterop); if (110 == er) { throw new System.IO.FileNotFoundException("Failed to find file for hashing.", filePath); } else if (0 != er) { throw new ApplicationException(String.Format("Unknown error while getting hash of file: {0}, system error: {1}", filePath, er)); // TODO: come up with a real exception to throw } hash = new int[4]; hash[0] = hashInterop.data0; hash[1] = hashInterop.data1; hash[2] = hashInterop.data2; hash[3] = hashInterop.data3; }
/// <summary> /// Sets a summary information property. /// </summary> /// <param name="index">Index of the summary information property.</param> /// <param name="property">Data to set into the property.</param> public void SetProperty(int index, object property) { if (IntPtr.Zero == handle) { throw new ApplicationException("Handle cannot be null."); } uint error = 0; FILETIME ft; ft.dwHighDateTime = 0; ft.dwLowDateTime = 0; if (11 == index || 12 == index || 13 == index) // must be a FileTime object { long l = Common.ToFileUtc(Convert.ToDateTime(property, System.Globalization.CultureInfo.InvariantCulture)); ft.dwHighDateTime = (int)(l >> 32); ft.dwLowDateTime = (int)(l & 0xFFFFFFFF); error = MsiInterop.MsiSummaryInfoSetProperty(handle, index, MsiInterop.VTFILETIME, 0, ref ft, null); } else if (1 == index) { error = MsiInterop.MsiSummaryInfoSetProperty(handle, index, MsiInterop.VTI2, Convert.ToInt32(property), ref ft, null); } else if (14 == index || 15 == index || 16 == index || 19 == index) { error = MsiInterop.MsiSummaryInfoSetProperty(handle, index, MsiInterop.VTI4, Convert.ToInt32(property), ref ft, null); } else // must be a string { error = MsiInterop.MsiSummaryInfoSetProperty(handle, index, MsiInterop.VTLPWSTR, 0, ref ft, (string)property); } if (0 != error) { throw new System.Runtime.InteropServices.ExternalException(String.Concat("Failed to set summary information property: ", index), (int)error); } }
/// <summary> /// Fetches the next row in the view. /// </summary> /// <param name="record">Record for recieving the data in the next row of the view.</param> /// <returns>Returns true if there was another record to be fetched and false if there wasn't.</returns> public bool Fetch(out Record record) { if (IntPtr.Zero == this.handle) { throw new ArgumentNullException(); // TODO: come up with a real exception to throw } IntPtr recordHandle; uint error = MsiInterop.MsiViewFetch(this.handle, out recordHandle); if (259 == error) { record = null; return(false); } else if (0 != error) { throw new System.Runtime.InteropServices.ExternalException("Failed to fetch record from view", (int)error); } record = new Record(recordHandle); return(true); }
/// <summary> /// Constructor that creates a view given a database handle and a query. /// </summary> /// <param name="db">Handle to the database to run the query on.</param> /// <param name="query">Query to be executed.</param> public View(Database db, string query) { if (null == db) { throw new ArgumentNullException("db"); } if (null == query) { throw new ArgumentNullException("query"); } uint handle = 0; int error = MsiInterop.MsiDatabaseOpenView(db.Handle, query, out handle); if (0 != error) { throw new MsiException(error); } this.Handle = handle; }
private void EnumRelatedFeatures(string productcode) { MsiError ret; StringBuilder featureParent = new StringBuilder(64); StringBuilder feature = new StringBuilder(64); uint i = 0; while ((ret = MsiInterop.MsiEnumFeatures(productcode, i, feature, featureParent)) == 0) { MsiInstallState installState = MsiInterop.MsiQueryFeatureState(productcode, feature.ToString()); if (installState == MsiInstallState.Local) { foreach (MsiFeature msiFeature in features) { if (msiFeature.Id == feature.ToString()) { msiFeature.SetIsAlreadyInstalled(true); } } } i++; } }
/// <summary> /// Gets integer value at specified location. /// </summary> /// <param name="field">Index into record to get integer</param> /// <returns>Integer value</returns> public int GetInteger(int field) { return(MsiInterop.MsiRecordGetInteger(this.Handle, field)); }
/// <summary> /// Executes the MSI file with the specified MSI parameters. /// </summary> /// <param name="msiFile">The MSI file.</param> /// <param name="msiParams">The MSI parameters.</param> /// <exception cref="System.ApplicationException"></exception> public void Execute(string msiFile, string msiParams) { MsiInstallUIHandler uiHandler = null; IntPtr parent = IntPtr.Zero; MsiInstallUILevel oldLevel = MsiInterop.MsiSetInternalUI(MsiInstallUILevel.None | MsiInstallUILevel.SourceResOnly, ref parent); MsiInstallUIHandler oldHandler = null; try { uiHandler = new MsiInstallUIHandler(OnExternalUI); //must be kept alive until the end of the MsiInstallProduct call if (SetupStarted != null) { InUiThread(SetupStarted); } oldHandler = MsiInterop.MsiSetExternalUI(uiHandler, MsiInstallLogMode.ExternalUI, IntPtr.Zero); MsiError ret = MsiInterop.MsiInstallProduct(msiFile, msiParams); CurrentActionName = ""; this.MsiErrorCode = (int)ret; if (ret != MsiError.NoError) { Console.WriteLine(string.Format("Failed to install -- {0}", ret)); //(ret==ProductVersion) Another version of this product is already installed throw new ApplicationException(string.Format("Failed to install -- {0}", ret)); } } catch (Exception e) { OnError("Application initialization error: " + e.ToString(), false); CurrentActionName = ""; // do something meaningful throw; } finally { if (oldHandler != null) { MsiInterop.MsiSetExternalUI(oldHandler, MsiInstallLogMode.None, IntPtr.Zero); oldHandler = null; } //It is important to reference uiHandler here to keep it alive till the end. //The debug build is more forgiving and referencing uiHandler is not essential as the code is not optimized if (uiHandler != null) { //see https://wixsharp.codeplex.com/discussions/647701 for details Environment.SetEnvironmentVariable("ReasonForThis", "IHadToDoSomethingThatJITWouldNotOptimiseThis"); uiHandler = null; } MsiInterop.MsiSetInternalUI(oldLevel, ref parent); if (SetupComplete != null) { InUiThread(SetupComplete); } } }
/// <summary> /// Enables the MSI runtime logging to the specified log file. /// </summary> /// <param name="logFile">The log file.</param> /// <param name="mode">The logging mode.</param> public void EnableLog(string logFile, MsiInstallLogMode mode = MsiInstallLogMode.Info | MsiInstallLogMode.Progress | MsiInstallLogMode.PropertyDump | MsiInstallLogMode.Error | MsiInstallLogMode.User | MsiInstallLogMode.ActionData) { MsiInterop.MsiEnableLog(mode, logFile, MsiLogAttribute.FlushEachLine); }
/// <summary> /// Verifies the existence or absence of a table. /// </summary> /// <param name="tableName">Table name to to verify the existence of.</param> /// <returns>Returns true if the table exists, false if it does not.</returns> public bool TableExists(string tableName) { int result = MsiInterop.MsiDatabaseIsTablePersistent(this.Handle, tableName); return(MsiInterop.MSICONDITIONTRUE == result); }
/// <summary> /// Gets a summary information property. /// </summary> /// <param name="index">Index of the summary information property.</param> /// <returns>The summary information property.</returns> public object GetProperty(int index) { uint dataType; StringBuilder stringValue = new StringBuilder(""); int bufSize = 0; int intValue; FILETIME timeValue; timeValue.dwHighDateTime = 0; timeValue.dwLowDateTime = 0; uint ret = MsiInterop.MsiSummaryInfoGetProperty(handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); //if(ret != (dataType == (uint) VT.LPSTR ? (uint) MsiInterop.Error.MORE_DATA : 0)) if (234 == ret) { stringValue.EnsureCapacity(++bufSize); ret = MsiInterop.MsiSummaryInfoGetProperty(handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); } if (0 != ret) { throw new ArgumentNullException(); // TODO: come up with a real exception to throw } switch ((VT)dataType) { case VT.EMPTY: { return(""); //if(type == typeof(DateTime)) return DateTime.MinValue; //else if(type == typeof(string)) return ""; //else if(type == typeof(short)) return (short) 0; //else if(type == typeof(int)) return (int) 0; //else throw new ArgumentNullException(); // TODO: come up with a real exception to throw } case VT.LPSTR: { return(stringValue.ToString()); //if(type == typeof(string)) return stringValue.ToString(); //else if(type == typeof(short) || type == typeof(int) || type == typeof(DateTime)) throw new InstallerException(); //else throw new ArgumentNullException();// TODO: come up with a real exception to throw } case VT.I2: case VT.I4: { return("" + intValue); //if(type == typeof(short)) return (short) intValue; //else if(type == typeof(int)) return intValue; //else if(type == typeof(string)) return "" + intValue; //else if(type == typeof(DateTime)) throw new InstallerException(); //else throw new ArgumentException(); } case VT.FILETIME: { return(timeValue.ToString()); //if(type == typeof(DateTime)) return DateTime.FromFileTime(timeValue); //else if(type == typeof(string)) return "" + DateTime.FromFileTime(timeValue); //else if(type == typeof(short) || type == typeof(int)) throw new InstallerException(); //else throw new ArgumentException(); } default: { throw new ArgumentNullException(); // TODO: come up with a real exception to throw } } }
public static void CloseView(this IntPtr view) { Invoke(() => MsiInterop.MsiViewClose(view)); Close(view); }
public static void Close(this IntPtr handle) { Invoke(() => MsiInterop.MsiCloseHandle(handle)); }
public static int GetInt(this IntPtr record, uint fieldIndex) { return(MsiInterop.MsiRecordGetInteger(record, fieldIndex)); }
/// <summary> /// Enables the installer's internal user interface. /// </summary> /// <param name="uiLevel">Specifies the level of complexity of the user interface.</param> /// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.</param> /// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns> internal static int SetInternalUI(int uiLevel, ref IntPtr hwnd) { return(MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd)); }
/// <summary> /// Enables an external user-interface handler. /// </summary> /// <param name="installUIHandler">Specifies a callback function.</param> /// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param> /// <param name="context">Pointer to an application context that is passed to the callback function.</param> /// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns> internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context) { return(MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context)); }