/// <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.IsNullOrWhiteSpace(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.IsNullOrWhiteSpace(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> /// 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.IsNullOrWhiteSpace(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.IsNullOrWhiteSpace(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; }