aboutsummaryrefslogtreecommitdiff
path: root/src/tools/heat/DllHarvester.cs
blob: e8c5ae40b3ed5416b2112c42af5b1091b375badc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 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.Reflection;
    using System.Reflection.Emit;
    using System.Runtime.InteropServices;
    using Wix = WixToolset.Harvesters.Serialize;

    /// <summary>
    /// Harvest WiX authoring from a native DLL file.
    /// </summary>
    public sealed class DllHarvester
    {
        /// <summary>
        /// Harvest the registry values written by calling DllRegisterServer on the specified file.
        /// </summary>
        /// <param name="file">The file to harvest registry values from.</param>
        /// <returns>The harvested registry values.</returns>
        public Wix.RegistryValue[] HarvestRegistryValues(string file)
        {
            // load the DLL
            NativeMethods.LoadLibrary(file);

            using (RegistryHarvester registryHarvester = new RegistryHarvester(true))
            {
                try
                {
                    DynamicPInvoke(file, "DllRegisterServer", typeof(int), null, null);

                    return registryHarvester.HarvestRegistry();
                }
                catch (TargetInvocationException e)
                {
                    e.Data["file"] = file;
                    throw;
                }
            }
        }

        /// <summary>
        /// Dynamically PInvokes into a DLL.
        /// </summary>
        /// <param name="dll">Dynamic link library containing the entry point.</param>
        /// <param name="entryPoint">Entry point into dynamic link library.</param>
        /// <param name="returnType">Return type of entry point.</param>
        /// <param name="parameterTypes">Type of parameters to entry point.</param>
        /// <param name="parameterValues">Value of parameters to entry point.</param>
        /// <returns>Value from invoked code.</returns>
        private static object DynamicPInvoke(string dll, string entryPoint, Type returnType, Type[] parameterTypes, object[] parameterValues)
        {
#if NETCOREAPP
            throw new PlatformNotSupportedException();
#else
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = "wixTempAssembly";

            AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("wixTempModule");

            MethodBuilder dynamicMethod = dynamicModule.DefinePInvokeMethod(entryPoint, dll, MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.PinvokeImpl, CallingConventions.Standard, returnType, parameterTypes, CallingConvention.Winapi, CharSet.Ansi);
            dynamicModule.CreateGlobalFunctions();

            MethodInfo methodInfo = dynamicModule.GetMethod(entryPoint);
            return methodInfo.Invoke(null, parameterValues);
#endif
        }

        /// <summary>
        /// Native methods for loading libraries.
        /// </summary>
        private sealed class NativeMethods
        {
            private const UInt32 LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;

            /// <summary>
            /// Load a DLL library.
            /// </summary>
            /// <param name="file">The file name of the executable module.</param>
            /// <returns>If the function succeeds, the return value is a handle to the mapped executable module.</returns>
            internal static IntPtr LoadLibrary(string file)
            {
                IntPtr dllHandle = LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_WITH_ALTERED_SEARCH_PATH);

                if (IntPtr.Zero == dllHandle)
                {
                    int lastError = Marshal.GetLastWin32Error();
                    throw new Exception(String.Format("Unable to load file: {0}, error: {1}", file, lastError));
                }

                return dllHandle;
            }

            /// <summary>
            /// Maps the specified executable module into the address space of the calling process.
            /// </summary>
            /// <param name="file">The file name of the executable module.</param>
            /// <param name="fileHandle">This parameter is reserved for future use. It must be NULL.</param>
            /// <param name="flags">Action to take when loading the module.</param>
            /// <returns>If the function succeeds, the return value is a handle to the mapped executable module.</returns>
            [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]
            private static extern IntPtr LoadLibraryEx(string file, IntPtr fileHandle, UInt32 flags);
        }
    }
}