aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Bal/WixToolset.Dnc.Host/BootstrapperApplicationFactory.cs
blob: d38fd1a9fc01d6ed0189c4e9db91aecc2b8e4e8d (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
// 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.Dnc.Host
{
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;

    delegate IBootstrapperApplicationFactory StaticEntryDelegate([MarshalAs(UnmanagedType.LPWStr)] string baFactoryAssemblyName, [MarshalAs(UnmanagedType.LPWStr)] string baFactoryAssemblyPath);

    /// <summary>
    /// Entry point for the .NET Core host to create and return the BA to the engine.
    /// Reflection is used instead of referencing WixToolset.Mba.Core directly to avoid requiring it in the AssemblyLoadContext.
    /// </summary>
    public sealed class BootstrapperApplicationFactory : IBootstrapperApplicationFactory
    {
        private string baFactoryAssemblyName;
        private string baFactoryAssemblyPath;

        public BootstrapperApplicationFactory(string baFactoryAssemblyName, string baFactoryAssemblyPath)
        {
            this.baFactoryAssemblyName = baFactoryAssemblyName;
            this.baFactoryAssemblyPath = baFactoryAssemblyPath;
        }

        /// <summary>
        /// Loads the bootstrapper application assembly and calls its IBootstrapperApplicationFactory.Create method.
        /// </summary>
        /// <param name="pArgs">Pointer to BOOTSTRAPPER_CREATE_ARGS struct.</param>
        /// <param name="pResults">Pointer to BOOTSTRAPPER_CREATE_RESULTS struct.</param>
        /// <exception cref="MissingAttributeException">The bootstrapper application assembly
        /// does not define the <see cref="BootstrapperApplicationFactoryAttribute"/>.</exception>
        public void Create(IntPtr pArgs, IntPtr pResults)
        {
            // Load the BA's IBootstrapperApplicationFactory.
            var baFactoryType = BootstrapperApplicationFactory.GetBAFactoryTypeFromAssembly(this.baFactoryAssemblyName, this.baFactoryAssemblyPath);
            var baFactory = Activator.CreateInstance(baFactoryType);
            if (null == baFactory)
            {
                throw new InvalidBootstrapperApplicationFactoryException();
            }

            var createMethod = baFactoryType.GetMethod(nameof(Create), new[] { typeof(IntPtr), typeof(IntPtr) });
            if (null == createMethod)
            {
                throw new InvalidBootstrapperApplicationFactoryException();
            }
            createMethod.Invoke(baFactory, new object[] { pArgs, pResults });
        }

        /// <summary>
        /// Locates the <see cref="BootstrapperApplicationFactoryAttribute"/> and returns the specified type.
        /// </summary>
        /// <param name="assemblyName">The assembly that defines the IBootstrapperApplicationFactory implementation.</param>
        /// <returns>The bootstrapper application factory <see cref="Type"/>.</returns>
        private static Type GetBAFactoryTypeFromAssembly(string assemblyName, string assemblyPath)
        {
            // The default ALC shouldn't need help loading the assembly, since the host should have provided the deps.json
            // when starting the runtime. But it doesn't hurt so keep this in case an isolated ALC is ever needed.
            var alc = new DnchostAssemblyLoadContext(assemblyPath, false);
            var asm = alc.LoadFromAssemblyName(new AssemblyName(assemblyName));

            var attr = asm.GetCustomAttributes()
                          .Where(a => a.GetType().FullName == "WixToolset.Mba.Core.BootstrapperApplicationFactoryAttribute")
                          .SingleOrDefault();

            if (null == attr)
            {
                throw new MissingAttributeException();
            }

            var baFactoryTypeProperty = attr.GetType().GetProperty("BootstrapperApplicationFactoryType", typeof(Type));
            if (baFactoryTypeProperty == null || baFactoryTypeProperty.GetMethod == null)
            {
                throw new MissingAttributeException();
            }

            var baFactoryType = (Type)baFactoryTypeProperty.GetMethod.Invoke(attr, null);
            return baFactoryType;
        }

        // Entry point for the DNC host.
        public static IBootstrapperApplicationFactory CreateBAFactory([MarshalAs(UnmanagedType.LPWStr)] string baFactoryAssemblyName, [MarshalAs(UnmanagedType.LPWStr)] string baFactoryAssemblyPath)
        {
            return new BootstrapperApplicationFactory(baFactoryAssemblyName, baFactoryAssemblyPath);
        }
    }
}