internal InstallationPart(string id, string productCode, string userSid, UserContexts context) { this.id = id; this.productCode = productCode; this.userSid = userSid; this.context = context; }
/// <summary> /// Creates a new instance of the <see cref="SourceInfo"/> class. /// </summary> /// <param name="productCode">The ProductCode to which the source is registered.</param> /// <param name="userSid">The user SID of the product.</param> /// <param name="userContext">The user context of the product.</param> /// <param name="path">The registered source path.</param> /// <param name="order">The order in which the source path is tried.</param> internal SourceInfo(string productCode, string userSid, UserContexts userContext, string path, int order) { this.ProductCode = productCode; this.UserSid = userSid; this.UserContext = userContext; this.Path = path; this.Order = order; }
/// <summary> /// Creates a new ComponentInstallation for a component installed by a /// specific product. /// </summary> /// <param name="componentCode">component GUID</param> /// <param name="productCode">ProductCode GUID</param> /// <param name="szUserSid">context user SID</param> /// <param name="dwContext">user contexts</param> internal ComponentInstallation(string componentCode, string productCode, string szUserSid, UserContexts dwContext) : base(componentCode, productCode, szUserSid, dwContext) { if (string.IsNullOrEmpty(componentCode)) { throw new ArgumentNullException("componentCode"); } }
/// <summary> /// Creates a new object for accessing information about a product installation on the current system. /// </summary> /// <param name="productCode">ProductCode (GUID) of the product.</param> /// <param name="userSid">The specific user, when working in a user context. This /// parameter may be null to indicate the current user. The parameter must be null /// when working in a machine context.</param> /// <param name="context">The user context. The calling process must have administrative /// privileges to get information for a product installed for a user other than the /// current user.</param> public ProductInstallation(string productCode, string userSid, UserContexts context) : base(productCode, userSid, context) { if (string.IsNullOrWhiteSpace(productCode)) { throw new ArgumentNullException("productCode"); } }
/// <summary> /// Creates a new object for accessing information about a product installation on the current system. /// </summary> /// <param name="productCode">ProductCode (GUID) of the product.</param> /// <param name="userSid">The specific user, when working in a user context. This /// parameter may be null to indicate the current user. The parameter must be null /// when working in a machine context.</param> /// <param name="context">The user context. The calling process must have administrative /// privileges to get information for a product installed for a user other than the /// current user.</param> internal ProductInstallation(string productCode, string userSid, UserContexts context) : base(productCode, userSid, context) { if (String.IsNullOrEmpty(productCode)) { throw new ArgumentNullException("productCode"); } }
/// <summary> /// Creates a new object for accessing information about a patch installation on the current system. /// </summary> /// <param name="patchCode">Registered patch code (GUID) of the patch.</param> /// <param name="productCode">ProductCode (GUID) the patch has been applied to. /// This parameter may be null for patches that are registered only and not yet /// applied to any product.</param> /// <param name="userSid">The specific user, when working in a user context. This /// parameter may be null to indicate the current user. The parameter must be null /// when working in a machine context.</param> /// <param name="context">The user context. The calling process must have administrative /// privileges to get information for a product installed for a user other than the /// current user.</param> /// <remarks><p> /// If the <paramref name="productCode"/> is null, the Patch object may /// only be used to read and update the patch's SourceList information. /// </p></remarks> public PatchInstallation(string patchCode, string productCode, string userSid, UserContexts context) : base(patchCode, userSid, context) { if (String.IsNullOrEmpty(patchCode)) { throw new ArgumentNullException("patchCode"); } this.productCode = productCode; }
internal Installation(string installationCode, string userSid, UserContexts context) { if (context == UserContexts.Machine) { userSid = null; } this.installationCode = installationCode; this.userSid = userSid; this.context = context; }
/// <summary> /// Gets the set of installed components for products in the indicated context. /// </summary> /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/dd407947.aspx">MsiEnumComponentsEx</a> /// </p></remarks> public static IEnumerable<ComponentInstallation> Components(string szUserSid, UserContexts dwContext) { uint pcchSid = 32; StringBuilder szSid = new StringBuilder((int)pcchSid); StringBuilder buf = new StringBuilder(40); UserContexts installedContext; for (uint i = 0; true; i++) { uint ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); if (ret == (uint) NativeMethods.Error.MORE_DATA) { szSid.EnsureCapacity((int) ++pcchSid); ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ComponentInstallation(buf.ToString(), szSid.ToString(), installedContext); } }
/// <summary> /// Creates a new ComponentInstallation, automatically detecting the /// product that the component is a part of. /// </summary> /// <param name="componentCode">component GUID</param> /// <param name="szUserSid">context user SID</param> /// <param name="dwContext">user contexts</param> public ComponentInstallation(string componentCode, string szUserSid, UserContexts dwContext) : this(componentCode, ComponentInstallation.GetProductCode(componentCode, szUserSid, dwContext), szUserSid, dwContext) { }
/// <summary> /// Determines which patches apply to a specified product and in what sequence. If /// the product is installed, this method accounts for patches that have already been applied to /// the product and accounts for obsolete and superceded patches. /// </summary> /// <param name="product">The product that is the target for the set of patches. This may be /// either a ProductCode (GUID) of a product that is currently installed, or the path to a an /// MSI file.</param> /// <param name="patches">An array of strings specifying the patches to be checked. Each item /// may be the path to an MSP file, the path an XML file, or just an XML blob.</param> /// <param name="errorHandler">Callback to be invoked for each inapplicable patch, reporting the /// reason the patch is not applicable. This value may be left null if that information is not /// desired.</param> /// <param name="userSid">Specifies a security identifier (SID) of a user. This parameter restricts /// the context of enumeration for this user account. This parameter cannot be the special SID /// strings s-1-1-0 (everyone) or s-1-5-18 (local system). If <paramref name="context"/> is set to /// <see cref="UserContexts.None"/> or <see cref="UserContexts.Machine"/>, then /// <paramref name="userSid"/> must be null. For the current user context, <paramref name="userSid"/> /// can be null and <paramref name="context"/> can be set to <see cref="UserContexts.UserManaged"/> /// or <see cref="UserContexts.UserUnmanaged"/>.</param> /// <param name="context">Restricts the enumeration to per-user-unmanaged, per-user-managed, /// or per-machine context, or (if referring to an MSI) to no system context at all. This /// parameter can be <see cref="UserContexts.Machine"/>, <see cref="UserContexts.UserManaged"/>, /// <see cref="UserContexts.UserUnmanaged"/>, or <see cref="UserContexts.None"/>.</param> /// <returns>An array of selected patch strings from <paramref name="patches"/>, indicating /// the set of applicable patches. The items are re-ordered to be in the best sequence.</returns> /// <remarks><p> /// If an item in <paramref name="patches"/> is a file path but does not end in .MSP or .XML, /// it is assumed to be an MSP file. /// </p><p> /// Passing an InstallContext of None only analyzes the MSI file; it does not consider the /// current state of the system. You cannot use InstallContext.None with a ProductCode GUID. /// </p><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidetermineapplicablepatches.asp">MsiDetermineApplicablePatches</a> /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msideterminepatchsequence.asp">MsiDeterminePatchSequence</a> /// </p></remarks> public static IList <string> DetermineApplicablePatches( string product, string[] patches, InapplicablePatchHandler errorHandler, string userSid, UserContexts context) { if (String.IsNullOrEmpty(product)) { throw new ArgumentNullException("product"); } if (patches == null) { throw new ArgumentNullException("patches"); } NativeMethods.MsiPatchSequenceData[] sequenceData = new NativeMethods.MsiPatchSequenceData[patches.Length]; for (int i = 0; i < patches.Length; i++) { if (String.IsNullOrEmpty(patches[i])) { throw new ArgumentNullException("patches[" + i + "]"); } sequenceData[i].szPatchData = patches[i]; sequenceData[i].ePatchDataType = GetPatchStringDataType(patches[i]); sequenceData[i].dwOrder = -1; sequenceData[i].dwStatus = 0; } uint ret; if (context == UserContexts.None) { ret = NativeMethods.MsiDetermineApplicablePatches(product, (uint)sequenceData.Length, sequenceData); } else { ret = NativeMethods.MsiDeterminePatchSequence(product, userSid, context, (uint)sequenceData.Length, sequenceData); } if (errorHandler != null) { for (int i = 0; i < sequenceData.Length; i++) { if (sequenceData[i].dwOrder < 0 && sequenceData[i].dwStatus != 0) { errorHandler(sequenceData[i].szPatchData, InstallerException.ExceptionFromReturnCode(sequenceData[i].dwStatus)); } } } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } IList <string> patchSeq = new List <string>(patches.Length); for (int i = 0; i < sequenceData.Length; i++) { for (int j = 0; j < sequenceData.Length; j++) { if (sequenceData[j].dwOrder == i) { patchSeq.Add(sequenceData[j].szPatchData); } } } return(patchSeq); }
private IEnumerable <PatchSequence> DetermineApplicablePatches(string product, string userSid, UserContexts context) { var patches = this.Patches.ToArray(); InapplicablePatchHandler handler = (patch, ex) => this.OnInapplicablePatch(new InapplicablePatchEventArgs(patch, product, ex)); // Keep track of the sequence. int i = 0; // The current implementation does not return a null list. var applicable = Installer.DetermineApplicablePatches(product, patches, handler, userSid, context); foreach (var path in applicable) { yield return(new PatchSequence() { Patch = path, Sequence = i++, Product = product, UserSid = userSid, UserContext = context, }); } }
/// <summary> /// Gets a list of patches that are applicable to the given ProductCode. /// </summary> /// <param name="product">The ProductCode for which applicability is determined.</param> /// <param name="userSid">The SID of the user for which the product is installed. The default is null.</param> /// <param name="context">The <see cref="UserContexts"/> into which the product is installed. This must be <see cref="UserContexts.None"/> for a package path. The default is <see cref="UserContexts.None"/>.</param> /// <returns>An ordered list of applicable patch or patch XML files.</returns> /// <exception cref="ArgumentException">The parameters are not correct for the given package path or installed ProductCode (ex: cannot use <see cref="UserContexts.All"/> in any case).</exception> internal IEnumerable <PatchSequence> GetApplicablePatches(string product, string userSid = null, UserContexts context = UserContexts.None) { // Enumerate all applicable patches immediately and return. return(this.DetermineApplicablePatches(product, userSid, context).ToList()); }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern int MsiGetComponentPathEx(string szProduct, string szComponent, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, StringBuilder lpPathBuf, ref uint pcchBuf);
/// <summary> /// Gets the weight of a package given its ProductCode. /// </summary> /// <param name="productCode">The ProductCode of the product to query.</param> /// <param name="userSid">The optional user SID of the product to query. The default is null.</param> /// <param name="context">The optional context of the product ot query. The default is <see cref="UserContexts.All"/>.</param> /// <returns>The weight of a package given its ProductCode or 0 if the product is not installed or the package missing.</returns> internal static long GetWeightFromProductCode(string productCode, string userSid = null, UserContexts context = UserContexts.All) { var product = new ProductInstallation(productCode, userSid, context); if (null != product && product.IsInstalled) { var path = product.LocalPackage; if (!string.IsNullOrEmpty(path)) { return PackageInfo.GetWeightFromPath(path); } } return 0; }
/// <summary> /// Enumerates patch installations based on certain criteria. /// </summary> /// <param name="patchCode">PatchCode (GUID) of the patch to be enumerated. Only /// instances of patches within the scope of the context specified by the /// <paramref name="userSid"/> and <paramref name="context"/> parameters will be /// enumerated. This parameter may be set to null to enumerate all patches in the specified /// context.</param> /// <param name="targetProductCode">ProductCode (GUID) product whose patches are to be /// enumerated. If non-null, patch enumeration is restricted to instances of this product /// within the specified context. If null, the patches for all products under the specified /// context are enumerated.</param> /// <param name="userSid">Specifies a security identifier (SID) that restricts the context /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts /// enumeration to the current user or any user in the system. The special SID string /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter /// can be set to null to restrict the enumeration scope to the current user. When /// <paramref name="userSid"/> must be null.</param> /// <param name="context">Specifies the user context.</param> /// <param name="states">The <see cref="PatchStates"/> of patches to return.</param> /// <remarks><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumpatchesex.asp">MsiEnumPatchesEx</a> /// </p></remarks> public static IEnumerable <PatchInstallation> GetPatches( string patchCode, string targetProductCode, string userSid, UserContexts context, PatchStates states) { StringBuilder buf = new StringBuilder(40); StringBuilder targetProductBuf = new StringBuilder(40); UserContexts targetContext; StringBuilder targetSidBuf = new StringBuilder(40); for (uint i = 0; ; i++) { uint targetSidBufSize = (uint)targetSidBuf.Capacity; uint ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint)states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); if (ret == (uint)NativeMethods.Error.MORE_DATA) { targetSidBuf.Capacity = (int)++targetSidBufSize; ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint)states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); } if (ret == (uint)NativeMethods.Error.NO_MORE_ITEMS) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } string thisPatchCode = buf.ToString(); if (patchCode == null || patchCode == thisPatchCode) { yield return(new PatchInstallation( buf.ToString(), targetProductBuf.ToString(), targetSidBuf.ToString(), targetContext)); } } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiSourceListClearMediaDisk(string szProductCodeOrPatchCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwOptions, uint dwDiskID);
private static string GetProductCode(string component, string szUserSid, UserContexts dwContext) { // TODO: We really need what would be MsiGetProductCodeEx, or at least a reasonable facsimile thereof (something that restricts the search to the context defined by szUserSid & dwContext) return(GetProductCode(component)); }
/// <summary> /// Creates a new object for accessing information about a patch installation on the current system. /// </summary> /// <param name="patchCode">Registered patch code (GUID) of the patch.</param> /// <param name="productCode">ProductCode (GUID) the patch has been applied to. /// This parameter may be null for patches that are registered only and not yet /// applied to any product.</param> /// <param name="userSid">The specific user, when working in a user context. This /// parameter may be null to indicate the current user. The parameter must be null /// when working in a machine context.</param> /// <param name="context">The user context. The calling process must have administrative /// privileges to get information for a product installed for a user other than the /// current user.</param> /// <remarks><p> /// If the <paramref name="productCode"/> is null, the Patch object may /// only be used to read and update the patch's SourceList information. /// </p></remarks> public PatchInstallation(string patchCode, string productCode, string userSid, UserContexts context) : base(patchCode, userSid, context) { if (string.IsNullOrWhiteSpace(patchCode)) { throw new ArgumentNullException("patchCode"); } this.productCode = productCode; }
/// <summary> /// Gets the set of installed components for products in the indicated context. /// </summary> /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/dd407947.aspx">MsiEnumComponentsEx</a> /// </p></remarks> public static IEnumerable <ComponentInstallation> Components(string szUserSid, UserContexts dwContext) { uint pcchSid = 32; StringBuilder szSid = new StringBuilder((int)pcchSid); StringBuilder buf = new StringBuilder(40); UserContexts installedContext; for (uint i = 0; true; i++) { uint ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); if (ret == (uint)NativeMethods.Error.MORE_DATA) { szSid.EnsureCapacity((int)++pcchSid); ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); } if (ret == (uint)NativeMethods.Error.NO_MORE_ITEMS) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return(new ComponentInstallation(buf.ToString(), szSid.ToString(), installedContext)); } }
/// <summary> /// Creates a new ComponentInstallation for a component installed by a /// specific product. /// </summary> /// <param name="componentCode">component GUID</param> /// <param name="productCode">ProductCode GUID</param> /// <param name="szUserSid">context user SID</param> /// <param name="dwContext">user contexts</param> public ComponentInstallation(string componentCode, string productCode, string szUserSid, UserContexts dwContext) : base(componentCode, productCode, szUserSid, dwContext) { if (string.IsNullOrWhiteSpace(componentCode)) { throw new ArgumentNullException("componentCode"); } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiSourceListSetInfo(string szProductCodeOrPatchCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwOptions, string szProperty, string szValue);
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiDeterminePatchSequence(string szProductCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint cPatchInfo, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3), In, Out] MsiPatchSequenceData[] pPatchInfo);
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiSourceListEnumMediaDisks(string szProductCodeOrPatchCode, string szUserSID, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwOptions, uint dwIndex, out uint pdwDiskId, StringBuilder szVolumeLabel, ref uint pcchVolumeLabel, StringBuilder szDiskPrompt, ref uint pcchDiskPrompt);
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiEnumPatchesEx(string szProductCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwFilter, uint dwIndex, StringBuilder szPatchCode, StringBuilder szTargetProductCode, [MarshalAs(UnmanagedType.I4)] out UserContexts pdwTargetProductContext, StringBuilder szTargetUserSid, ref uint pcchTargetUserSid);
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiEnumClientsEx(string szComponent, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint iProductIndex, StringBuilder lpProductBuf, [MarshalAs(UnmanagedType.I4)] out UserContexts pdwInstalledContext, StringBuilder szSid, ref uint pcchSid);
private static string GetProductCode(string component, string szUserSid, UserContexts dwContext) { // TODO: We really need what would be MsiGetProductCodeEx, or at least a reasonable facimile thereof (something that restricts the search to the context defined by szUserSid & dwContext) return GetProductCode(component); }
/// <summary> /// Initializes a new instance of the <see cref="PatchSourceInfo"/> class. /// </summary> /// <param name="productCode">The ProductCode to which the source is registered.</param> /// <param name="patchCode">The patch code to which the source is regisered.</param> /// <param name="userSid">The user SID of the product.</param> /// <param name="userContext">The user context of the product.</param> /// <param name="path">The registered source path.</param> /// <param name="order">The order in which the source path is tried.</param> internal PatchSourceInfo(string productCode, string patchCode, string userSid, UserContexts userContext, string path, int order) : base(productCode, userSid, userContext, path, order) { this.PatchCode = patchCode; }
/// <summary> /// Gets a list of patches that are applicable to the given ProductCode. /// </summary> /// <param name="product">The ProductCode for which applicability is determined.</param> /// <param name="userSid">The SID of the user for which the product is installed. The default is null.</param> /// <param name="context">The <see cref="UserContexts"/> into which the product is installed. This must be <see cref="UserContexts.None"/> for a package path. The default is <see cref="UserContexts.None"/>.</param> /// <returns>An ordered list of applicable patch or patch XML files.</returns> /// <exception cref="ArgumentException">The parameters are not correct for the given package path or installed ProductCode (ex: cannot use <see cref="UserContexts.All"/> in any case).</exception> internal IEnumerable<PatchSequence> GetApplicablePatches(string product, string userSid = null, UserContexts context = UserContexts.None) { // Enumerate all applicable patches immediately and return. return this.DetermineApplicablePatches(product, userSid, context).ToList(); }
/// <summary> /// Creates a new instance of the <see cref="SourceInfo"/> class. /// </summary> /// <param name="productCode">The ProductCode to which the source is registered.</param> /// <param name="patchCode">The patch code to which the source is regisered.</param> /// <param name="userSid">The user SID of the product.</param> /// <param name="userContext">The user context of the product.</param> /// <param name="path">The registered source path.</param> /// <param name="order">The order in which the source path is tried.</param> internal PatchSourceInfo(string productCode, string patchCode, string userSid, UserContexts userContext, string path, int order) : base(productCode, userSid, userContext, path, order) { this.PatchCode = patchCode; }
/// <summary> /// Enumerates patches for the given patch codes and ProductCodes and writes them to the pipeline. /// </summary> /// <param name="patchCode">The patch code to enumerate.</param> /// <param name="productCode">The ProductCode having patches to enumerate.</param> /// <param name="userSid">The user's SID for patches to enumerate.</param> /// <param name="context">The installation context for patches to enumerate.</param> /// <param name="filter">The patch installation state for patches to enumerate.</param> private void WritePatches(string patchCode, string productCode, string userSid, UserContexts context, PatchStates filter) { foreach (PatchInstallation patch in PatchInstallation.GetPatches(patchCode, productCode, userSid, context, filter)) { this.WritePatch(patch); } }
///<inheritdoc/> public IEnumerable<IProductInstallation> GetProducts(string productCode, string userSid, UserContexts context) { return ProductInstallation.GetProducts(productCode, userSid, context).Select(x => new ProductInstallationWrap(x) as IProductInstallation); }
/// <summary> /// Start to get a list of patches that are applicable to the given ProductCode in a separate thread. /// </summary> /// <param name="product">The ProductCode for which applicability is determined.</param> /// <param name="userSid">The SID of the user for which the product is installed. The default is null.</param> /// <param name="context">The <see cref="UserContexts"/> into which the product is installed. This must be <see cref="UserContexts.None"/> for a package path. The default is <see cref="UserContexts.None"/>.</param> /// <returns>An <see cref="IAsyncResult"/> you can use to query the current state of the task and return results when complete.</returns> /// <exception cref="ArgumentException">The parameters are not correct for the given package path or installed ProductCode (ex: cannot use <see cref="UserContexts.All"/> in any case).</exception> internal IAsyncResult BeginGetApplicablePatches(string product, string userSid = null, UserContexts context = UserContexts.None) { return this.action.BeginInvoke(product, userSid, context, null, null); }
/// <summary> /// Start to get a list of patches that are applicable to the given ProductCode in a separate thread. /// </summary> /// <param name="product">The ProductCode for which applicability is determined.</param> /// <param name="userSid">The SID of the user for which the product is installed. The default is null.</param> /// <param name="context">The <see cref="UserContexts"/> into which the product is installed. This must be <see cref="UserContexts.None"/> for a package path. The default is <see cref="UserContexts.None"/>.</param> /// <returns>An <see cref="IAsyncResult"/> you can use to query the current state of the task and return results when complete.</returns> /// <exception cref="ArgumentException">The parameters are not correct for the given package path or installed ProductCode (ex: cannot use <see cref="UserContexts.All"/> in any case).</exception> internal IAsyncResult BeginGetApplicablePatches(string product, string userSid = null, UserContexts context = UserContexts.None) { return(this.action.BeginInvoke(product, userSid, context, null, null)); }
/// <summary> /// Enumerates products for the given ProductCode and writes them to the pipeline. /// </summary> /// <param name="productCode">The ProductCode of products to enumerate.</param> /// <param name="userSid">The user's SID for products to enumerate.</param> /// <param name="context">The installation context for products to enumerate.</param> /// <param name="patterns">Optional list of <see cref="WildcardPattern"/> to match product names.</param> private void WriteProducts(string productCode, string userSid, UserContexts context, IList<WildcardPattern> patterns = null) { foreach (ProductInstallation product in ProductInstallation.GetProducts(productCode, userSid, context)) { if (0 == patterns.Count() || product.ProductName.Match(patterns)) { this.WriteProduct(product); } } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiEnumProductsEx(string szProductCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwIndex, StringBuilder szInstalledProductCode, [MarshalAs(UnmanagedType.I4)] out UserContexts pdwInstalledContext, StringBuilder szSid, ref uint pcchSid);
private IEnumerable<PatchSequence> DetermineApplicablePatches(string product, string userSid, UserContexts context) { var patches = this.Patches.ToArray(); InapplicablePatchHandler handler = (patch, ex) => this.OnInapplicablePatch(new InapplicablePatchEventArgs(patch, product, ex)); // Keep track of the sequence. int i = 0; // The current implementation does not return a null list. var applicable = Installer.DetermineApplicablePatches(product, patches, handler, userSid, context); foreach (var path in applicable) { yield return new PatchSequence() { Patch = path, Sequence = i++, Product = product, UserSid = userSid, UserContext = context, }; } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiGetProductInfoEx(string szProductCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, string szProperty, StringBuilder lpValue, ref uint pcchValue);
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiQueryComponentState(string szProductCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, string szComponent, out int pdwState);
/// <summary> /// Enumerates product installations based on certain criteria. /// </summary> /// <param name="productCode">ProductCode (GUID) of the product instances to be enumerated. Only /// instances of products within the scope of the context specified by the /// <paramref name="userSid"/> and <paramref name="context"/> parameters will be /// enumerated. This parameter may be set to null to enumerate all products in the specified /// context.</param> /// <param name="userSid">Specifies a security identifier (SID) that restricts the context /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts /// enumeration to the current user or any user in the system. The special SID string /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter /// can be set to null to restrict the enumeration scope to the current user. When /// <paramref name="context"/> is set to the machine context only, /// <paramref name="userSid"/> must be null.</param> /// <param name="context">Specifies the user context.</param> /// <returns>An enumeration of product objects for enumerated product instances.</returns> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumproductsex.asp">MsiEnumProductsEx</a> /// </p></remarks> public static IEnumerable<ProductInstallation> GetProducts( string productCode, string userSid, UserContexts context) { StringBuilder buf = new StringBuilder(40); UserContexts targetContext; StringBuilder targetSidBuf = new StringBuilder(40); for (uint i = 0; ; i++) { uint targetSidBufSize = (uint) targetSidBuf.Capacity; uint ret = NativeMethods.MsiEnumProductsEx( productCode, userSid, context, i, buf, out targetContext, targetSidBuf, ref targetSidBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { targetSidBuf.Capacity = (int) ++targetSidBufSize; ret = NativeMethods.MsiEnumProductsEx( productCode, userSid, context, i, buf, out targetContext, targetSidBuf, ref targetSidBufSize); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ProductInstallation( buf.ToString(), targetSidBuf.ToString(), targetContext); } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiSourceListEnumSources(string szProductCodeOrPatchCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwOptions, uint dwIndex, StringBuilder szSource, ref uint pcchSource);
/// <summary> /// Enumerates patch installations based on certain criteria. /// </summary> /// <param name="patchCode">PatchCode (GUID) of the patch to be enumerated. Only /// instances of patches within the scope of the context specified by the /// <paramref name="userSid"/> and <paramref name="context"/> parameters will be /// enumerated. This parameter may be set to null to enumerate all patches in the specified /// context.</param> /// <param name="targetProductCode">ProductCode (GUID) product whose patches are to be /// enumerated. If non-null, patch enumeration is restricted to instances of this product /// within the specified context. If null, the patches for all products under the specified /// context are enumerated.</param> /// <param name="userSid">Specifies a security identifier (SID) that restricts the context /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts /// enumeration to the current user or any user in the system. The special SID string /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter /// can be set to null to restrict the enumeration scope to the current user. When /// <paramref name="userSid"/> must be null.</param> /// <param name="context">Specifies the user context.</param> /// <param name="states">The <see cref="PatchStates"/> of patches to return.</param> /// <remarks><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumpatchesex.asp">MsiEnumPatchesEx</a> /// </p></remarks> public static IEnumerable<PatchInstallation> GetPatches( string patchCode, string targetProductCode, string userSid, UserContexts context, PatchStates states) { StringBuilder buf = new StringBuilder(40); StringBuilder targetProductBuf = new StringBuilder(40); UserContexts targetContext; StringBuilder targetSidBuf = new StringBuilder(40); for (uint i = 0; ; i++) { uint targetSidBufSize = (uint) targetSidBuf.Capacity; uint ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint) states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { targetSidBuf.Capacity = (int) ++targetSidBufSize; ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint) states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } string thisPatchCode = buf.ToString(); if (patchCode == null || patchCode == thisPatchCode) { yield return new PatchInstallation( buf.ToString(), targetProductBuf.ToString(), targetSidBuf.ToString(), targetContext); } } }
[DllImport("msi.dll", CharSet = CharSet.Unicode)] internal static extern uint MsiSourceListForceResolutionEx(string szProductCodeOrPatchCode, string szUserSid, [MarshalAs(UnmanagedType.I4)] UserContexts dwContext, uint dwOptions);
/// <summary> /// Determines which patches apply to a specified product and in what sequence. If /// the product is installed, this method accounts for patches that have already been applied to /// the product and accounts for obsolete and superceded patches. /// </summary> /// <param name="product">The product that is the target for the set of patches. This may be /// either a ProductCode (GUID) of a product that is currently installed, or the path to a an /// MSI file.</param> /// <param name="patches">An array of strings specifying the patches to be checked. Each item /// may be the path to an MSP file, the path an XML file, or just an XML blob.</param> /// <param name="errorHandler">Callback to be invoked for each inapplicable patch, reporting the /// reason the patch is not applicable. This value may be left null if that information is not /// desired.</param> /// <param name="userSid">Specifies a security identifier (SID) of a user. This parameter restricts /// the context of enumeration for this user account. This parameter cannot be the special SID /// strings s-1-1-0 (everyone) or s-1-5-18 (local system). If <paramref name="context"/> is set to /// <see cref="UserContexts.None"/> or <see cref="UserContexts.Machine"/>, then /// <paramref name="userSid"/> must be null. For the current user context, <paramref name="userSid"/> /// can be null and <paramref name="context"/> can be set to <see cref="UserContexts.UserManaged"/> /// or <see cref="UserContexts.UserUnmanaged"/>.</param> /// <param name="context">Restricts the enumeration to per-user-unmanaged, per-user-managed, /// or per-machine context, or (if referring to an MSI) to no system context at all. This /// parameter can be <see cref="UserContexts.Machine"/>, <see cref="UserContexts.UserManaged"/>, /// <see cref="UserContexts.UserUnmanaged"/>, or <see cref="UserContexts.None"/>.</param> /// <returns>An array of selected patch strings from <paramref name="patches"/>, indicating /// the set of applicable patches. The items are re-ordered to be in the best sequence.</returns> /// <remarks><p> /// If an item in <paramref name="patches"/> is a file path but does not end in .MSP or .XML, /// it is assumed to be an MSP file. /// </p><p> /// Passing an InstallContext of None only analyzes the MSI file; it does not consider the /// current state of the system. You cannot use InstallContext.None with a ProductCode GUID. /// </p><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidetermineapplicablepatches.asp">MsiDetermineApplicablePatches</a> /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msideterminepatchsequence.asp">MsiDeterminePatchSequence</a> /// </p></remarks> public static IList<string> DetermineApplicablePatches( string product, string[] patches, InapplicablePatchHandler errorHandler, string userSid, UserContexts context) { if (String.IsNullOrEmpty(product)) { throw new ArgumentNullException("product"); } if (patches == null) { throw new ArgumentNullException("patches"); } NativeMethods.MsiPatchSequenceData[] sequenceData = new NativeMethods.MsiPatchSequenceData[patches.Length]; for (int i = 0; i < patches.Length; i++) { if (String.IsNullOrEmpty(patches[i])) { throw new ArgumentNullException("patches[" + i + "]"); } sequenceData[i].szPatchData = patches[i]; sequenceData[i].ePatchDataType = GetPatchStringDataType(patches[i]); sequenceData[i].dwOrder = -1; sequenceData[i].dwStatus = 0; } uint ret; if (context == UserContexts.None) { ret = NativeMethods.MsiDetermineApplicablePatches(product, (uint) sequenceData.Length, sequenceData); } else { ret = NativeMethods.MsiDeterminePatchSequence(product, userSid, context, (uint) sequenceData.Length, sequenceData); } if (errorHandler != null) { for (int i = 0; i < sequenceData.Length; i++) { if (sequenceData[i].dwOrder < 0 && sequenceData[i].dwStatus != 0) { errorHandler(sequenceData[i].szPatchData, InstallerException.ExceptionFromReturnCode(sequenceData[i].dwStatus)); } } } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } IList<string> patchSeq = new List<string>(patches.Length); for (int i = 0; i < sequenceData.Length; i++) { for (int j = 0; j < sequenceData.Length; j++) { if (sequenceData[j].dwOrder == i) { patchSeq.Add(sequenceData[j].szPatchData); } } } return patchSeq; }
/// <summary> /// Gets the weight of a package given its ProductCode. /// </summary> /// <param name="productCode">The ProductCode of the product to query.</param> /// <param name="userSid">The optional user SID of the product to query. The default is null.</param> /// <param name="context">The optional context of the product ot query. The default is <see cref="UserContexts.All"/>.</param> /// <returns>The weight of a package given its ProductCode or 0 if the product is not installed or the package missing.</returns> internal static long GetWeightFromProductCode(string productCode, string userSid = null, UserContexts context = UserContexts.All) { var product = new ProductInstallation(productCode, userSid, context); if (null != product && product.IsInstalled) { var path = product.LocalPackage; if (!string.IsNullOrEmpty(path)) { return(PackageInfo.GetWeightFromPath(path)); } } return(0); }