示例#1
0
        static void Main(string[] args)
        {
            if (args.Length < 3)
            {
                PrintUsageAndExit();
            }
            var logger = new ConsoleLogger();
            var input  = args[0];

            if (!input.StartsWith("/input:"))
            {
                logger.Error("First argument must be /input:<input_assembly>");
                return;
            }
            input = input.Substring("/input:".Length);
            var output = args[1];

            if (!output.StartsWith("/output:"))
            {
                logger.Error("Second argument must be /output:<output_assembly>");
                return;
            }
            output = output.Substring("/output:".Length);
            // parse resources
            var       resources      = new List <ResourceInfo>();
            const int resourceOffset = 2;

            for (int i = resourceOffset; i < args.Length; i++)
            {
                var data = args[i];
                if (!data.Contains(">"))
                {
                    logger.Error("Resource file {0} did not contain required deliminator '>'.", i - resourceOffset);
                    Environment.Exit(-1);
                }
                var idx           = data.IndexOf('>');
                var inputResource = data.Substring(0, idx);
                var embeddedName  = data.Substring(idx + 1);
                if (!File.Exists(inputResource))
                {
                    logger.Error("Input file: '{0}' not found.", inputResource);
                    Environment.Exit(-2);
                }
                resources.Add(new ResourceInfo(inputResource, embeddedName));
            }
            using (IModifyAssemblies modifier = new CecilBasedAssemblyModifier(logger, input, output))
            {
                if (!modifier.EmbedResources(resources.ToArray()))
                {
                    logger.Error("Failed to embed resources!");
                    Environment.Exit(-3);
                }
                if (!modifier.InjectModuleInitializedCode(CecilHelpers.InjectEmbeddedResourceLoader))
                {
                    logger.Error("Failed to inject code!");
                    Environment.Exit(-4);
                }
            }
            Console.WriteLine("Successfully added resources and code!");
        }
        public void TestEmbedResourceAndInjectCodeInWinForms()
        {
            var file = Path.Combine(AssemblyDirectory(), "WinFormsFullTest.exe");

            if (File.Exists(file))
            {
                File.Delete(file);
            }
            File.Copy(Path.Combine(AssemblyDirectory(), "WinFormsTest.exe"), file);
            if (File.Exists(Path.ChangeExtension(file, "pdb")))
            {
                File.Delete(Path.ChangeExtension(file, "pdb"));
            }

            var logger = Substitute.For <ILogger>();

            using (IModifyAssemblies modifer = new CecilBasedAssemblyModifier(logger, file, file))
            {
                var resources = new[]
                {
                    new ResourceInfo(Path.Combine(AssemblyDirectory(), "de\\WinFormsTest.resources.dll"), "WinFormsTest.de.resources.dll"),
                    new ResourceInfo(Path.Combine(AssemblyDirectory(), "fr\\WinFormsTest.resources.dll"), "WinFormsTest.fr.resources.dll")
                };
                modifer.EmbedResources(resources).Should().BeTrue();

                modifer.InjectModuleInitializedCode(CecilHelpers.InjectEmbeddedResourceLoader).Should().BeTrue();
            }

            // assert that the resource is embedded and that it automatically localizes using the injected code
            var info = new ProcessStartInfo(file, "/testFullyProcessed");

            using (var p = Process.Start(info))
            {
                p.Should().NotBeNull();
                p.WaitForExit(3 * 1000).Should().BeTrue();
                p.ExitCode.Should().Be(0, "because all localized files have been loaded");
            }
            File.Delete(file);
        }
        /// <summary>
        /// Copies the exe to a temp location, embedds the specific resource into it and then returns the loaded assembly.
        /// </summary>
        /// <param name="exeName"></param>
        /// <param name="resources"></param>
        /// <returns></returns>
        private static AssemblyHelper EmbedHelper(string exeName, ResourceInfo[] resources)
        {
            var dir  = new FileInfo(Assembly.GetExecutingAssembly().GetLocation()).DirectoryName;
            var file = Path.Combine(dir, exeName);

            File.Exists(file).Should().BeTrue("because we referenced it.");

            var tempFile = Path.GetTempFileName();

            File.Delete(tempFile);
            tempFile += ".exe";
            using (IModifyAssemblies modifer = new CecilBasedAssemblyModifier(Substitute.For <ILogger>(), file, tempFile))
            {
                modifer.EmbedResources(resources).Should().BeTrue();
            }

            var bytes = File.ReadAllBytes(tempFile);

            var asm = Assembly.Load(bytes);

            return(new AssemblyHelper(asm, tempFile, () => File.Delete(tempFile)));
        }
示例#4
0
        public override bool Execute()
        {
            var logger = new MSBuildBasedLogger(BuildEngine, "ResourceEmbedder");

            if (!AssertSetup(logger))
            {
                return(false);
            }

            var watch = new Stopwatch();

            watch.Start();
            // run in object dir (=AssemblyPath) as we will run just after satellite assembly generated and ms build will then copy the output to target dir
            string inputAssembly = Path.Combine(ProjectDirectory, AssemblyPath);
            var    workingDir    = new FileInfo(inputAssembly).DirectoryName;

            if (!IsNet46OrAbove())
            {
                // resource embedder doesn't support < .Net 4.0 due to .Net not invoking resource assembly event prior to .Net 4: https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx
                // .Net 4.6 is also the new minimum target to ensure cross compile with .Net Standard works
                logger.Error("Versions prior to .Net 4.6 are no longer supported. Verison 1.x supports all version from .Net 4 and above. Please either upgrade to .Net 4.6, downgrade this package to 1.0 or remove the Resource.Embedder NuGet package from this project. " +
                             "See https://github.com/MarcStan/Resource.Embedder/issues/3 and https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx for details.");
                return(false);
            }

            var assembliesToEmbed = new List <ResourceInfo>();
            var cultures          = CultureInfo.GetCultures(CultureTypes.AllCultures);
            var inputAssemblyName = Path.GetFileNameWithoutExtension(inputAssembly);

            var usedCultures = new List <string>();

            foreach (var ci in cultures)
            {
                // check if culture satellite assembly exists, if so embed
                var ciPath = Path.Combine(workingDir, ci.Name, string.Format("{0}.resources.dll", inputAssemblyName));
                if (File.Exists(ciPath))
                {
                    //logger.Debug("Embedding culture: {0}", ci);
                    usedCultures.Add(ci.Name);
                    assembliesToEmbed.Add(new ResourceInfo(ciPath, string.Format("{0}.{1}.resources.dll", inputAssemblyName, ci)));
                }
            }
            if (assembliesToEmbed.Count == 0)
            {
                logger.Info("Nothing to embed! Skipping {0}", inputAssembly);
                return(true);
            }

            // add target directory where the assembly is compiled to to search path for reference assemblies
            var searchDirs = new List <string> {
                new FileInfo(TargetPath).DirectoryName
            };
            // fix for https://github.com/MarcStan/Resource.Embedder/issues/5
            // when references are marked as CopyLocal: False they will not end up at TargetPath when we run this code (instead they may be copied later)
            // so we need to tell Cecil about all the directories where they could be
            var referenceFiles = References ?? "";
            var referenceDirs  = referenceFiles.Contains(";") ? referenceFiles.Split(';') : new[] { referenceFiles };

            // we need the directory path, but the references are all files, so convert and take distinct set
            searchDirs.AddRange(referenceDirs.Select(f => new FileInfo(f).DirectoryName).Distinct());
            logger.Info("Looking for references in: {0}", string.Join(", ", searchDirs));

            StrongNameKeyPair signingKey = null;
            var debugSymbolType          = DebugSymbolHelper.FromString(DebugType);
            var symbolReader             = CecilBasedAssemblyModifier.GetSymbolReader(inputAssembly, debugSymbolType);
            var rp = CecilBasedAssemblyModifier.GetReaderParameters(inputAssembly, searchDirs, symbolReader);

            if (!SignAssembly)
            {
                if (DebugSymbols && !File.Exists(Path.ChangeExtension(inputAssembly, ".pdb")))
                {
                    // can't call ReadModule with DebugSymbols=true when .pdb is missing; since we most likely won't end up producing working output anyway
                    // just ignore the sign assembly check
                    logger.Warning("DebugSymbols are requested, but .pdb file is missing!");
                }
                else
                {
                    using (var md = ModuleDefinition.ReadModule(inputAssembly, rp))
                    {
                        var name        = nameof(AssemblyKeyFileAttribute);
                        var keyFileAttr = md.Assembly.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == name);
                        if (keyFileAttr != null)
                        {
                            logger.Info("Found AssemblyKeyFileAttribute even though MSBuild said SignAssembly=false, assuming assembly has to be signed anyway.");
                            SignAssembly = true;
                        }
                    }
                }
            }
            if (SignAssembly)
            {
                var keyFilePath = GetSigningKeyPath(inputAssembly, rp, logger);
                if (!File.Exists(keyFilePath))
                {
                    logger.Info("Could not find signing key file at path '{0}'.", keyFilePath);
                    return(false);
                }
                signingKey = new StrongNameKeyPair(File.OpenRead(keyFilePath));
            }

            using (IModifyAssemblies modifer = new CecilBasedAssemblyModifier(logger, inputAssembly, inputAssembly, searchDirs.ToArray(), debugSymbolType, signingKey))
            {
                if (!modifer.EmbedResources(assembliesToEmbed.ToArray()))
                {
                    logger.Error("Failed to embed resources into assembly: " + inputAssembly);
                    return(false);
                }
                if (!modifer.InjectModuleInitializedCode(CecilHelpers.InjectEmbeddedResourceLoader))
                {
                    logger.Error("Failed to inject required code into assembly: " + inputAssembly);
                    return(false);
                }
            }
            watch.Stop();
            EmbeddedCultures = string.Join(";", usedCultures);
            logger.Info("Finished embedding cultures: {0} into {1} in {2}ms", string.Join(", ", usedCultures), Path.GetFileName(inputAssembly), watch.ElapsedMilliseconds);
            return(true);
        }