// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.Harvesters { using System; using System.Collections; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using WixToolset.Data; using WixToolset.Harvesters.Data; using WixToolset.Harvesters.Extensibility; using Wix = WixToolset.Harvesters.Serialize; /// /// The WiX Toolset harvester mutator. /// public sealed class UtilHarvesterMutator : BaseMutatorExtension { // Flags for SetErrorMode() native method. private const UInt32 SEM_FAILCRITICALERRORS = 0x0001; private const UInt32 SEM_NOGPFAULTERRORBOX = 0x0002; private const UInt32 SEM_NOALIGNMENTFAULTEXCEPT = 0x0004; private const UInt32 SEM_NOOPENFILEERRORBOX = 0x8000; // Remember whether we were able to call OaEnablePerUserTLibRegistration private bool calledPerUserTLibReg; /// /// allow process to handle serious system errors. /// [DllImport("Kernel32.dll")] private static extern void SetErrorMode(UInt32 uiMode); /// /// enable the RegisterTypeLib API to use the appropriate override mapping for non-admin users on Vista /// [DllImport("Oleaut32.dll")] private static extern void OaEnablePerUserTLibRegistration(); public UtilHarvesterMutator() { this.calledPerUserTLibReg = false; SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); try { OaEnablePerUserTLibRegistration(); this.calledPerUserTLibReg = true; } catch (EntryPointNotFoundException) { } } /// /// Gets the sequence of this mutator extension. /// /// The sequence of this mutator extension. public override int Sequence { get { return 100; } } /// /// Mutate a WiX document. /// /// The Wix document element. public override void Mutate(Wix.Wix wix) { this.MutateElement(null, wix); } /// /// Mutate an element. /// /// The parent of the element to mutate. /// The element to mutate. private void MutateElement(Wix.IParentElement parentElement, Wix.ISchemaElement element) { if (element is Wix.File) { this.MutateFile(parentElement, (Wix.File)element); } // mutate the child elements if (element is Wix.IParentElement) { ArrayList childElements = new ArrayList(); // copy the child elements to a temporary array (to allow them to be deleted/moved) foreach (Wix.ISchemaElement childElement in ((Wix.IParentElement)element).Children) { childElements.Add(childElement); } foreach (Wix.ISchemaElement childElement in childElements) { this.MutateElement((Wix.IParentElement)element, childElement); } } } /// /// Mutate a file. /// /// The parent of the element to mutate. /// The file to mutate. private void MutateFile(Wix.IParentElement parentElement, Wix.File file) { if (null != file.Source) { string fileExtension = Path.GetExtension(file.Source); string fileSource = this.Core.ResolveFilePath(file.Source); if (String.Equals(".ax", fileExtension, StringComparison.OrdinalIgnoreCase) || // DirectShow filter String.Equals(".dll", fileExtension, StringComparison.OrdinalIgnoreCase) || String.Equals(".exe", fileExtension, StringComparison.OrdinalIgnoreCase) || String.Equals(".ocx", fileExtension, StringComparison.OrdinalIgnoreCase)) // ActiveX { // try the assembly harvester try { AssemblyHarvester assemblyHarvester = new AssemblyHarvester(); this.Core.Messaging.Write(HarvesterVerboses.HarvestingAssembly(fileSource)); Wix.RegistryValue[] registryValues = assemblyHarvester.HarvestRegistryValues(fileSource); foreach (Wix.RegistryValue registryValue in registryValues) { parentElement.AddChild(registryValue); } // also try self-reg since we could have a mixed-mode assembly this.HarvestSelfReg(parentElement, fileSource); } catch (BadImageFormatException) // not an assembly, try raw DLL. { this.HarvestSelfReg(parentElement, fileSource); } catch (Exception ex) { this.Core.Messaging.Write(HarvesterWarnings.AssemblyHarvestFailed(fileSource, ex.Message)); } } else if (String.Equals(".olb", fileExtension, StringComparison.OrdinalIgnoreCase) || // type library String.Equals(".tlb", fileExtension, StringComparison.OrdinalIgnoreCase)) // type library { // try the type library harvester try { TypeLibraryHarvester typeLibHarvester = new TypeLibraryHarvester(); this.Core.Messaging.Write(HarvesterVerboses.HarvestingTypeLib(fileSource)); Wix.RegistryValue[] registryValues = typeLibHarvester.HarvestRegistryValues(fileSource); foreach (Wix.RegistryValue registryValue in registryValues) { parentElement.AddChild(registryValue); } } catch (COMException ce) { // 0x8002801C (TYPE_E_REGISTRYACCESS) // If we don't have permission to harvest typelibs, it's likely because we're on // Vista or higher and aren't an Admin, or don't have the appropriate QFE installed. if (!this.calledPerUserTLibReg && (0x8002801c == unchecked((uint)ce.ErrorCode))) { this.Core.Messaging.Write(WarningMessages.InsufficientPermissionHarvestTypeLib()); } else if (0x80029C4A == unchecked((uint)ce.ErrorCode)) // generic can't load type library { this.Core.Messaging.Write(HarvesterWarnings.TypeLibLoadFailed(fileSource, ce.Message)); } } } } } /// /// Calls self-reg harvester. /// /// The parent element. /// The file source. private void HarvestSelfReg(Wix.IParentElement parentElement, string fileSource) { // try the self-reg harvester try { DllHarvester dllHarvester = new DllHarvester(); this.Core.Messaging.Write(HarvesterVerboses.HarvestingSelfReg(fileSource)); Wix.RegistryValue[] registryValues = dllHarvester.HarvestRegistryValues(fileSource); foreach (Wix.RegistryValue registryValue in registryValues) { parentElement.AddChild(registryValue); } } catch (TargetInvocationException tie) { if (tie.InnerException is EntryPointNotFoundException) { // No DllRegisterServer(), which is fine by me. } else { this.Core.Messaging.Write(HarvesterWarnings.SelfRegHarvestFailed(fileSource, tie.Message)); } } catch (Exception ex) { this.Core.Messaging.Write(HarvesterWarnings.SelfRegHarvestFailed(fileSource, ex.Message)); } } } }