示例#1
0
        /// <summary>
        /// Reflects on an input CA module to locate custom action entry-points.
        /// </summary>
        /// <param name="module">Assembly module with CA entry-points.</param>
        /// <returns>Mapping from entry-point names to assembly!class.method paths.</returns>
        private static IDictionary <string, string> FindEntryPoints(string module)
        {
            log.WriteLine("Searching for custom action entry points " +
                          "in {0}", Path.GetFileName(module));

            Dictionary <string, string> entryPoints = new Dictionary <string, string>();

            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(module);

            foreach (Type type in assembly.GetExportedTypes())
            {
                foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
                {
                    string entryPointName = MakeSfxCA.GetEntryPoint(method);
                    if (entryPointName != null)
                    {
                        string entryPointPath = String.Format(
                            "{0}!{1}.{2}",
                            Path.GetFileNameWithoutExtension(module),
                            type.FullName,
                            method.Name);
                        entryPoints.Add(entryPointName, entryPointPath);

                        log.WriteLine("    {0}={1}", entryPointName, entryPointPath);
                    }
                }
            }

            return(entryPoints);
        }
示例#2
0
        /// <summary>
        /// Packages up all the inputs to the output location.
        /// </summary>
        /// <exception cref="Exception">Various exceptions are thrown
        /// if things go wrong.</exception>
        public static void Build(string output, string sfxdll, IList <string> inputs, TextWriter log)
        {
            MakeSfxCA.log = log;

            if (String.IsNullOrEmpty(output))
            {
                throw new ArgumentNullException("output");
            }

            if (String.IsNullOrEmpty(sfxdll))
            {
                throw new ArgumentNullException("sfxdll");
            }

            if (inputs == null || inputs.Count == 0)
            {
                throw new ArgumentNullException("inputs");
            }

            if (!File.Exists(sfxdll))
            {
                throw new FileNotFoundException(sfxdll);
            }

            string customActionAssembly = inputs[0];

            if (!File.Exists(customActionAssembly))
            {
                throw new FileNotFoundException(customActionAssembly);
            }

            inputs = MakeSfxCA.SplitList(inputs);

            IDictionary <string, string> inputsMap = MakeSfxCA.GetPackFileMap(inputs);

            bool foundWIAssembly = false;

            foreach (string input in inputsMap.Keys)
            {
                if (String.Compare(input, MakeSfxCA.RequiredWIAssembly,
                                   StringComparison.OrdinalIgnoreCase) == 0)
                {
                    foundWIAssembly = true;
                }
            }

            if (!foundWIAssembly)
            {
                throw new ArgumentException(MakeSfxCA.RequiredWIAssembly +
                                            " must be included in the list of support files. " +
                                            "If using the MSBuild targets, make sure the assembly reference " +
                                            "has the Private (Copy Local) flag set.");
            }

            MakeSfxCA.ResolveDependentAssemblies(inputsMap, Path.GetDirectoryName(customActionAssembly));

            IDictionary <string, string> entryPoints = MakeSfxCA.FindEntryPoints(customActionAssembly);
            string uiClass = MakeSfxCA.FindEmbeddedUIClass(customActionAssembly);

            if (entryPoints.Count == 0 && uiClass == null)
            {
                throw new ArgumentException(
                          "No CA or UI entry points found in module: " + customActionAssembly);
            }
            else if (entryPoints.Count > 0 && uiClass != null)
            {
                throw new NotSupportedException(
                          "CA and UI entry points cannot be in the same assembly: " + customActionAssembly);
            }

            string dir = Path.GetDirectoryName(output);

            if (dir.Length > 0 && !Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            using (Stream outputStream = File.Create(output))
            {
                MakeSfxCA.WriteEntryModule(sfxdll, outputStream, entryPoints, uiClass);
            }

            MakeSfxCA.CopyVersionResource(customActionAssembly, output);

            MakeSfxCA.PackInputFiles(output, inputsMap);

            log.WriteLine("MakeSfxCA finished: " + new FileInfo(output).FullName);
        }
示例#3
0
        /// <summary>
        /// Writes a modified version of SfxCA.dll to the output stream,
        /// with the template entry-points mapped to the CA entry-points.
        /// </summary>
        /// <remarks>
        /// To avoid having to recompile SfxCA.dll for every different set of CAs,
        /// this method looks for a preset number of template entry-points in the
        /// binary file and overwrites their entrypoint name and string data with
        /// CA-specific values.
        /// </remarks>
        private static void WriteEntryModule(
            string sfxdll, Stream outputStream, IDictionary <string, string> entryPoints, string uiClass)
        {
            log.WriteLine("Modifying SfxCA.dll stub");

            byte[] fileBytes;
            using (FileStream readStream = File.OpenRead(sfxdll))
            {
                fileBytes = new byte[(int)readStream.Length];
                readStream.Read(fileBytes, 0, fileBytes.Length);
            }

            const string ENTRYPOINT_FORMAT   = "CustomActionEntryPoint{0:d03}";
            const int    MAX_ENTRYPOINT_NAME = 72;
            const int    MAX_ENTRYPOINT_PATH = 160;

            byte[] emptyBytes = new byte[0];

            int slotCount = MakeSfxCA.GetEntryPointSlotCount(fileBytes, ENTRYPOINT_FORMAT);

            if (slotCount == 0)
            {
                throw new ArgumentException("Invalid SfxCA.dll file.");
            }

            if (entryPoints.Count > slotCount)
            {
                throw new ArgumentException(String.Format(
                                                "The custom action assembly has {0} entrypoints, which is more than the maximum ({1}). " +
                                                "Refactor the custom actions or add more entrypoint slots in SfxCA\\EntryPoints.h.",
                                                entryPoints.Count, slotCount));
            }

            string[] slotSort = new string[slotCount];
            for (int i = 0; i < slotCount - entryPoints.Count; i++)
            {
                slotSort[i] = String.Empty;
            }

            entryPoints.Keys.CopyTo(slotSort, slotCount - entryPoints.Count);
            Array.Sort <string>(slotSort, slotCount - entryPoints.Count, entryPoints.Count);

            for (int i = 0; ; i++)
            {
                string templateName       = String.Format(ENTRYPOINT_FORMAT, i);
                byte[] templateAsciiBytes = Encoding.ASCII.GetBytes(templateName);
                byte[] templateUniBytes   = Encoding.Unicode.GetBytes(templateName);

                int nameOffset = MakeSfxCA.FindBytes(fileBytes, templateAsciiBytes);
                if (nameOffset < 0)
                {
                    break;
                }

                int pathOffset = MakeSfxCA.FindBytes(fileBytes, templateUniBytes);
                if (pathOffset < 0)
                {
                    break;
                }

                string entryPointName = slotSort[i];
                string entryPointPath = entryPointName.Length > 0 ?
                                        entryPoints[entryPointName] : String.Empty;

                if (entryPointName.Length > MAX_ENTRYPOINT_NAME)
                {
                    throw new ArgumentException(String.Format(
                                                    "Entry point name exceeds limit of {0} characters: {1}",
                                                    MAX_ENTRYPOINT_NAME,
                                                    entryPointName));
                }

                if (entryPointPath.Length > MAX_ENTRYPOINT_PATH)
                {
                    throw new ArgumentException(String.Format(
                                                    "Entry point path exceeds limit of {0} characters: {1}",
                                                    MAX_ENTRYPOINT_PATH,
                                                    entryPointPath));
                }

                byte[] replaceNameBytes = Encoding.ASCII.GetBytes(entryPointName);
                byte[] replacePathBytes = Encoding.Unicode.GetBytes(entryPointPath);

                MakeSfxCA.ReplaceBytes(fileBytes, nameOffset, MAX_ENTRYPOINT_NAME, replaceNameBytes);
                MakeSfxCA.ReplaceBytes(fileBytes, pathOffset, MAX_ENTRYPOINT_PATH * 2, replacePathBytes);
            }

            if (entryPoints.Count == 0 && uiClass != null)
            {
                // Remove the zzz prefix from exported EmbeddedUI entry-points.
                foreach (string export in new string[] { "InitializeEmbeddedUI", "EmbeddedUIHandler", "ShutdownEmbeddedUI" })
                {
                    byte[] exportNameBytes = Encoding.ASCII.GetBytes("zzz" + export);

                    int exportOffset = MakeSfxCA.FindBytes(fileBytes, exportNameBytes);
                    if (exportOffset < 0)
                    {
                        throw new ArgumentException("Input SfxCA.dll does not contain exported entry-point: " + export);
                    }

                    byte[] replaceNameBytes = Encoding.ASCII.GetBytes(export);
                    MakeSfxCA.ReplaceBytes(fileBytes, exportOffset, exportNameBytes.Length, replaceNameBytes);
                }

                if (uiClass.Length > MAX_ENTRYPOINT_PATH)
                {
                    throw new ArgumentException(String.Format(
                                                    "UI class full name exceeds limit of {0} characters: {1}",
                                                    MAX_ENTRYPOINT_PATH,
                                                    uiClass));
                }

                byte[] templateBytes = Encoding.Unicode.GetBytes("InitializeEmbeddedUI_FullClassName");
                byte[] replaceBytes  = Encoding.Unicode.GetBytes(uiClass);

                // Fill in the embedded UI implementor class so the proxy knows which one to load.
                int replaceOffset = MakeSfxCA.FindBytes(fileBytes, templateBytes);
                if (replaceOffset >= 0)
                {
                    MakeSfxCA.ReplaceBytes(fileBytes, replaceOffset, MAX_ENTRYPOINT_PATH * 2, replaceBytes);
                }
            }

            outputStream.Write(fileBytes, 0, fileBytes.Length);
        }
示例#4
0
        /// <summary>
        /// Sets up a reflection-only assembly-resolve-handler to handle loading dependent assemblies during reflection.
        /// </summary>
        /// <param name="inputFiles">List of input files which include non-GAC dependent assemblies.</param>
        /// <param name="inputDir">Directory to auto-locate additional dependent assemblies.</param>
        /// <remarks>
        /// Also searches the assembly's directory for unspecified dependent assemblies, and adds them
        /// to the list of input files if found.
        /// </remarks>
        private static void ResolveDependentAssemblies(IDictionary <string, string> inputsMap, string inputDir)
        {
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += delegate(object sender, ResolveEventArgs args)
            {
                AssemblyName resolveName = new AssemblyName(args.Name);
                Assembly     assembly    = null;

                // First, try to find the assembly in the list of input files.
                foreach (string inputFile in inputsMap.Values)
                {
                    string inputName      = Path.GetFileNameWithoutExtension(inputFile);
                    string inputExtension = Path.GetExtension(inputFile);
                    if (String.Equals(inputName, resolveName.Name, StringComparison.OrdinalIgnoreCase) &&
                        (String.Equals(inputExtension, ".dll", StringComparison.OrdinalIgnoreCase) ||
                         String.Equals(inputExtension, ".exe", StringComparison.OrdinalIgnoreCase)))
                    {
                        assembly = MakeSfxCA.TryLoadDependentAssembly(inputFile);

                        if (assembly != null)
                        {
                            break;
                        }
                    }
                }

                // Second, try to find the assembly in the input directory.
                if (assembly == null && inputDir != null)
                {
                    string assemblyPath = null;
                    if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".dll"))
                    {
                        assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".dll";
                    }
                    else if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".exe"))
                    {
                        assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".exe";
                    }

                    if (assemblyPath != null)
                    {
                        assembly = MakeSfxCA.TryLoadDependentAssembly(assemblyPath);

                        if (assembly != null)
                        {
                            // Add this detected dependency to the list of files to be packed.
                            inputsMap.Add(Path.GetFileName(assemblyPath), assemblyPath);
                        }
                    }
                }

                // Third, try to load the assembly from the GAC.
                if (assembly == null)
                {
                    try
                    {
                        assembly = Assembly.ReflectionOnlyLoad(args.Name);
                    }
                    catch (FileNotFoundException)
                    {
                    }
                }

                if (assembly != null)
                {
                    if (String.Equals(assembly.GetName().ToString(), resolveName.ToString()))
                    {
                        log.WriteLine("    Loaded dependent assembly: " + assembly.Location);
                        return(assembly);
                    }
                    else
                    {
                        log.WriteLine("    Warning: Loaded mismatched dependent assembly: " + assembly.Location);
                        log.WriteLine("      Loaded assembly   : " + assembly.GetName());
                        log.WriteLine("      Reference assembly: " + resolveName);
                    }
                }
                else
                {
                    log.WriteLine("    Error: Dependent assembly not supplied: " + resolveName);
                }

                return(null);
            };
        }