예제 #1
0
        void ValidateCAAssemblyImpl(string file, string refAsms)
        {
            //Debug.Assert(false);
            try
            {
                var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static;

                //var assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(file); //cannot prelaod all required assemblies
                var assembly = System.Reflection.Assembly.LoadFrom(file);

                var caMembers = assembly.GetTypes().SelectMany(t =>
                                                               t.GetMembers(bf)
                                                               .Where(mem =>
                                                                      mem.GetCustomAttributes(false)
                                                                      .Where(x => x.ToString() == "Microsoft.Deployment.WindowsInstaller.CustomActionAttribute").Any())).ToArray();

                var invalidMembers = new List <string>();
                foreach (MemberInfo mi in caMembers)
                {
                    string fullName = mi.DeclaringType.FullName + "." + mi.Name;

                    if (!mi.DeclaringType.IsPublic)
                    {
                        if (!invalidMembers.Contains(fullName))
                        {
                            invalidMembers.Add(fullName);
                        }
                    }

                    if (mi.MemberType != MemberTypes.Method)
                    {
                        if (!invalidMembers.Contains(fullName))
                        {
                            invalidMembers.Add(fullName);
                        }
                    }
                    else
                    {
                        var method = (mi as MethodInfo);
                        if (!method.IsPublic || !method.IsStatic)
                        {
                            if (!invalidMembers.Contains(fullName))
                            {
                                invalidMembers.Add(fullName);
                            }
                        }
                    }
                }
                if (invalidMembers.Any())
                {
                    Compiler.OutputWriteLine("Warning: some of the type members are marked with [CustomAction] attribute but they don't meet the MakeSfxCA criteria of being public static method of a public type:\n");
                    foreach (var member in invalidMembers)
                    {
                        Compiler.OutputWriteLine("  " + member);
                    }
                    Compiler.OutputWriteLine("");
                }
            }
            catch { }
        }
예제 #2
0
 /// <summary>
 /// Resets the <see cref="Id"/> generator. This method is exercised by the Wix# compiler before any
 /// <c>Build</c> operations to ensure reproducibility of the <see cref="Id"/> set between <c>Build()</c>
 /// calls.
 /// </summary>
 static public void ResetIdGenerator()
 {
     if (!DoNotResetIdGenerator)
     {
         if (idMaps.Count > 0)
         {
             Compiler.OutputWriteLine("----------------------------");
             Compiler.OutputWriteLine("Warning: Wix# compiler detected that some IDs has been auto-generated before the build started. " +
                                      "This can lead to the WiX ID duplications. To prevent this from happening either:\n" +
                                      "   - Avoid evaluating the auto-generated IDs values before the call to Build*\n" +
                                      "   - Set the IDs (to be evaluated) explicitly\n" +
                                      "   - Prevent resetting auto-ID generator by setting WixEntity.DoNotResetIdGenerator to true");
             Compiler.OutputWriteLine("----------------------------");
         }
         idMaps.Clear();
     }
 }
예제 #3
0
        static string BuildUiPlayerResources()
        {
            var dir    = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"WixSharp\Demo_UIPlayer");
            var result = Path.Combine(dir, "Demo_UIPlayer.msi");

            if (System.IO.File.Exists(result))
            {
                return(result);
            }
            else
            {
                try { Compiler.OutputWriteLine("Building UIPlayer resources..."); } catch { }
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                var project = new Project("Demo_UIPlayer",
                                          new Dir(@"%ProgramFiles%\WixSharp\Demo_UIPlayer"));

                project.OutDir = dir;

                string licence      = ManagedUI.Default.LicenceFileFor(project);
                string localization = ManagedUI.Default.LocalizationFileFor(project);
                string bitmap       = ManagedUI.Default.DialogBitmapFileFor(project);
                string banner       = ManagedUI.Default.DialogBannerFileFor(project);

                project.AddBinaries(new Binary(new Id("WixSharp_UIText"), localization),
                                    new Binary(new Id("WixSharp_LicenceFile"), licence),
                                    new Binary(new Id("WixUI_Bmp_Dialog"), bitmap),
                                    new Binary(new Id("WixUI_Bmp_Banner"), banner));

                project.UI = WUI.WixUI_ProgressOnly;
                return(project.BuildMsi());
            }
        }
예제 #4
0
        /// <summary>
        /// Builds WiX Bootstrapper application from the specified <see cref="Bundle"/> project instance.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="path">The path.</param>
        /// <exception cref="System.ApplicationException">Wix compiler/linker cannot be found</exception>
        public static string Build(Bundle project, string path)
        {
            path = path.ExpandEnvVars();

            if (Compiler.ClientAssembly.IsEmpty())
            {
                Compiler.ClientAssembly = System.Reflection.Assembly.GetCallingAssembly().GetLocation();
            }

            string oldCurrDir = Environment.CurrentDirectory;

            try
            {
                //System.Diagnostics.Debug.Assert(false);
                Compiler.TempFiles.Clear();
                string compiler = Utils.PathCombine(WixLocation, "candle.exe");
                string linker   = Utils.PathCombine(WixLocation, "light.exe");

                if (!IO.File.Exists(compiler) || !IO.File.Exists(linker))
                {
                    Compiler.OutputWriteLine("Wix binaries cannot be found. Expected location is " + compiler.PathGetDirName());
                    throw new ApplicationException("Wix compiler/linker cannot be found");
                }
                else
                {
                    if (!project.SourceBaseDir.IsEmpty())
                    {
                        Environment.CurrentDirectory = project.SourceBaseDir;
                    }

                    string wxsFile = BuildWxs(project);

                    string objFile = IO.Path.ChangeExtension(wxsFile, ".wixobj");
                    string pdbFile = IO.Path.ChangeExtension(wxsFile, ".wixpdb");

                    string extensionDlls = "";
                    foreach (string dll in project.WixExtensions.Distinct())
                    {
                        extensionDlls += " -ext \"" + dll + "\"";
                    }

                    string wxsFiles = "";
                    foreach (string file in project.WxsFiles.Distinct())
                    {
                        wxsFiles += " \"" + file + "\"";
                    }

                    var    candleOptions = CandleOptions + " " + project.CandleOptions;
                    string command       = candleOptions + " " + extensionDlls + " \"" + wxsFile + "\" ";

                    if (wxsFiles.IsNotEmpty())
                    {
                        command += wxsFiles;
                        string outDir = IO.Path.GetDirectoryName(wxsFile);
                        // if multiple files are specified candle expect a path for the -out switch
                        // or no path at all (use current directory)
                        // note the '\' character must be escaped twice: as a C# string and as a CMD char
                        if (outDir.IsNotEmpty())
                        {
                            command += $" -out \"{outDir}\\\\\"";
                        }
                    }
                    else
                    {
                        command += $" -out \"{objFile}\"";
                    }

                    command = command.ExpandEnvVars();

                    Run(compiler, command);

                    if (IO.File.Exists(objFile))
                    {
                        string outFile = wxsFile.PathChangeExtension(".exe");

                        if (path.IsNotEmpty())
                        {
                            outFile = IO.Path.GetFullPath(path);
                        }

                        if (IO.File.Exists(outFile))
                        {
                            IO.File.Delete(outFile);
                        }

                        //if (project.IsLocalized && IO.File.Exists(project.LocalizationFile))
                        //    Run(linker, LightOptions + " \"" + objFile + "\" -out \"" + msiFile + "\"" + extensionDlls + " -cultures:" + project.Language + " -loc \"" + project.LocalizationFile + "\"");
                        //else
                        string lightOptions = LightOptions + " " + project.LightOptions;

                        Run(linker, lightOptions + " \"" + objFile + "\" -out \"" + outFile + "\"" + extensionDlls + " -cultures:" + project.Language);

                        if (IO.File.Exists(outFile))
                        {
                            Compiler.TempFiles.Add(wxsFile);

                            Compiler.OutputWriteLine("\n----------------------------------------------------------\n");
                            Compiler.OutputWriteLine("Bootstrapper file has been built: " + path + "\n");
                            Compiler.OutputWriteLine(" Name       : " + project.Name);
                            Compiler.OutputWriteLine(" Version    : " + project.Version);
                            Compiler.OutputWriteLine(" UpgradeCode: {" + project.UpgradeCode + "}\n");

                            if (!PreserveDbgFiles && !project.PreserveDbgFiles)
                            {
                                objFile.DeleteIfExists();
                                pdbFile.DeleteIfExists();
                            }

                            project.DigitalSignature?.Apply(outFile);
                        }
                    }
                    else
                    {
                        Compiler.OutputWriteLine("Cannot build " + wxsFile);
                        Trace.WriteLine("Cannot build " + wxsFile);
                    }
                }

                if (!PreserveTempFiles && !project.PreserveTempFiles)
                {
                    foreach (var file in Compiler.TempFiles)
                    {
                        try
                        {
                            if (IO.File.Exists(file))
                            {
                                IO.File.Delete(file);
                            }
                        }
                        catch { }
                    }
                }
            }
            finally
            {
                Environment.CurrentDirectory = oldCurrDir;
            }

            return(path);
        }
예제 #5
0
        /// <summary>
        /// Builds the WiX source file (*.wxs) from the specified <see cref="Bundle"/> instance.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static string BuildWxs(Bundle project)
        {
            lock (typeof(Compiler))
            {
                //very important to keep "ClientAssembly = " in all "public Build*" methods to ensure GetCallingAssembly
                //returns the build script assembly but not just another method of Compiler.
                if (ClientAssembly.IsEmpty())
                {
                    ClientAssembly = System.Reflection.Assembly.GetCallingAssembly().GetLocation();
                }

                project.Validate();

                lock (Compiler.AutoGeneration.WxsGenerationSynchObject)
                {
                    var oldAlgorithm = AutoGeneration.CustomIdAlgorithm;
                    try
                    {
                        WixEntity.ResetIdGenerator(false);
                        AutoGeneration.CustomIdAlgorithm = project.CustomIdAlgorithm ?? AutoGeneration.CustomIdAlgorithm;

                        string file = IO.Path.GetFullPath(IO.Path.Combine(project.OutDir, project.OutFileName) + ".wxs");

                        if (IO.File.Exists(file))
                        {
                            IO.File.Delete(file);
                        }

                        string extraNamespaces = project.WixNamespaces.Distinct()
                                                 .Select(x => x.StartsWith("xmlns:") ? x : "xmlns:" + x)
                                                 .ConcatItems(" ");

                        var wix3Namespace = "http://schemas.microsoft.com/wix/2006/wi";
                        var wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs";

                        var wixNamespace = Compiler.IsWix4 ? wix4Namespace : wix3Namespace;

                        var doc = XDocument.Parse(
                            @"<?xml version=""1.0"" encoding=""utf-8""?>
                             " + $"<Wix xmlns=\"{wixNamespace}\" {extraNamespaces} " + @" >
                        </Wix>");

                        doc.Root.Add(project.ToXml());

                        AutoElements.NormalizeFilePaths(doc, project.SourceBaseDir, EmitRelativePaths);

                        project.InvokeWixSourceGenerated(doc);

                        AutoElements.ExpandCustomAttributes(doc, project);

                        if (WixSourceGenerated != null)
                        {
                            WixSourceGenerated(doc);
                        }

                        string xml = "";
                        using (IO.StringWriter sw = new StringWriterWithEncoding(Encoding.Default))
                        {
                            doc.Save(sw, SaveOptions.None);
                            xml = sw.ToString();
                        }

                        //of course you can use XmlTextWriter.WriteRaw but this is just a temporary quick'n'dirty solution
                        //http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2657663&SiteID=1
                        xml = xml.Replace("xmlns=\"\"", "");

                        DefaultWixSourceFormatedHandler(ref xml);

                        project.InvokeWixSourceFormated(ref xml);
                        if (WixSourceFormated != null)
                        {
                            WixSourceFormated(ref xml);
                        }

                        using (var sw = new IO.StreamWriter(file, false, Encoding.Default))
                            sw.WriteLine(xml);

                        Compiler.OutputWriteLine("\n----------------------------------------------------------\n");
                        Compiler.OutputWriteLine("Wix project file has been built: " + file + "\n");

                        project.InvokeWixSourceSaved(file);
                        if (WixSourceSaved != null)
                        {
                            WixSourceSaved(file);
                        }

                        return(file);
                    }
                    finally
                    {
                        AutoGeneration.CustomIdAlgorithm = oldAlgorithm;
                    }
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Builds the WiX source file and generates batch file capable of building
        /// WiX/MSI bootstrapper with WiX toolset.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="path">The path to the batch file to be created.</param>
        /// <exception cref="System.ApplicationException">Wix compiler/linker cannot be found</exception>
        public static string BuildCmd(Bundle project, string path = null)
        {
            if (Compiler.ClientAssembly.IsEmpty())
            {
                Compiler.ClientAssembly = System.Reflection.Assembly.GetCallingAssembly().GetLocation();
            }

            if (path == null)
            {
                path = IO.Path.GetFullPath(IO.Path.Combine(project.OutDir, "Build_" + project.OutFileName) + ".cmd");
            }

            path = path.ExpandEnvVars();

            //System.Diagnostics.Debug.Assert(false);
            string wixLocationEnvVar = $"set WixLocation={WixLocation}" + Environment.NewLine;
            string compiler          = Utils.PathCombine(WixLocation, "candle.exe");
            string linker            = Utils.PathCombine(WixLocation, "light.exe");
            string batchFile         = path;

            if (!IO.File.Exists(compiler) || !IO.File.Exists(linker))
            {
                Compiler.OutputWriteLine("Wix binaries cannot be found. Expected location is " + IO.Path.GetDirectoryName(compiler));
                throw new ApplicationException("Wix compiler/linker cannot be found");
            }
            else
            {
                string wxsFile = BuildWxs(project);

                if (!project.SourceBaseDir.IsEmpty())
                {
                    Environment.CurrentDirectory = project.SourceBaseDir;
                }

                string objFile = IO.Path.ChangeExtension(wxsFile, ".wixobj");
                string pdbFile = IO.Path.ChangeExtension(wxsFile, ".wixpdb");

                string extensionDlls = "";
                foreach (string dll in project.WixExtensions.Distinct())
                {
                    extensionDlls += " -ext \"" + dll + "\"";
                }

                string wxsFiles = "";
                foreach (string file in project.WxsFiles.Distinct())
                {
                    wxsFiles += " \"" + file + "\"";
                }

                var candleOptions = CandleOptions + " " + project.CandleOptions;

                string batchFileContent = wixLocationEnvVar + "\"" + compiler + "\" " + candleOptions + " " + extensionDlls +
                                          " \"" + IO.Path.GetFileName(wxsFile) + "\" ";

                if (wxsFiles.IsNotEmpty())
                {
                    batchFileContent += wxsFiles;
                    string outDir = IO.Path.GetDirectoryName(wxsFile);
                    // if multiple files are specified candle expect a path for the -out switch
                    // or no path at all (use current directory)
                    // note the '\' character must be escaped twice: as a C# string and as a CMD char
                    if (outDir.IsNotEmpty())
                    {
                        batchFileContent += $" -out \"{outDir}\\\\\"";
                    }
                }
                else
                {
                    batchFileContent += $" -out \"{objFile}\"";
                }

                batchFileContent += "\r\n";

                string lightOptions = LightOptions + " " + project.LightOptions;

                if (path.IsNotEmpty())
                {
                    lightOptions += " -out \"" + IO.Path.ChangeExtension(objFile, ".exe") + "\"";
                }

                batchFileContent += "\"" + linker + "\" " + lightOptions + " \"" + objFile + "\" " + extensionDlls + " -cultures:" + project.Language + "\r\npause";

                batchFileContent = batchFileContent.ExpandEnvVars();

                using (var sw = new IO.StreamWriter(batchFile))
                    sw.Write(batchFileContent);
            }

            return(path);
        }
예제 #7
0
        internal void ValidateCAAssemblyImpl(string file, string dtfAsm, bool loadFromMemory)
        {
            //Debug.Assert(false);
            try
            {
                var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static;

                // `ReflectionOnlyLoadFrom` cannot preload all required assemblies and triggers
                // "System.InvalidOperationException: 'It is illegal to reflect on the custom attributes
                // of a Type loaded via ReflectionOnlyGetType (see Assembly.ReflectionOnly) -- use CustomAttributeData
                // instead.'" exception. Thus need to use `LoadFrom`, which locks the assembly unless the operation is
                // performed in the temp AppDomain, which is unloaded after at the end.
                // Unfortunately `AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve` does not help (does not get fired).

                // var assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(file);
                var assembly = loadFromMemory ?
                               Reflection.Assembly.Load(System.IO.File.ReadAllBytes(file)) :
                               Reflection.Assembly.LoadFrom(file);

                var caMembers = assembly.GetTypes()
                                .SelectMany(t => t.GetMembers(bf)
                                            .Where(mem => mem.GetCustomAttributes(false)
                                                   .Where(x => x.ToString() == "Microsoft.Deployment.WindowsInstaller.CustomActionAttribute")
                                                   .Any()))
                                .ToArray();

                var invalidMembers = new List <string>();
                foreach (MemberInfo mi in caMembers)
                {
                    string fullName = mi.DeclaringType.FullName + "." + mi.Name;

                    if (!mi.DeclaringType.IsPublic)
                    {
                        if (!invalidMembers.Contains(fullName))
                        {
                            invalidMembers.Add(fullName);
                        }
                    }

                    if (mi.MemberType != MemberTypes.Method)
                    {
                        if (!invalidMembers.Contains(fullName))
                        {
                            invalidMembers.Add(fullName);
                        }
                    }
                    else
                    {
                        var method = (mi as MethodInfo);
                        if (!method.IsPublic || !method.IsStatic)
                        {
                            if (!invalidMembers.Contains(fullName))
                            {
                                invalidMembers.Add(fullName);
                            }
                        }
                    }
                }
                if (invalidMembers.Any())
                {
                    Compiler.OutputWriteLine("Warning: some of the type members are marked with [CustomAction] attribute but they don't meet the MakeSfxCA criteria of being public static method of a public type:\n");
                    foreach (var member in invalidMembers)
                    {
                        Compiler.OutputWriteLine("  " + member);
                    }
                    Compiler.OutputWriteLine("");
                }
            }
            catch { }
        }
예제 #8
0
        internal static void HandleEmptyDirectories(XDocument doc)
        {
            XElement product = doc.Root.Select("Product");

            var dummyDirs = product.Descendants("Directory")
                            .SelectMany(x => x.Elements("Component"))
                            .Where(e => e.HasAttribute("Id", v => v.EndsWith(".EmptyDirectory")))
                            .Select(x => x.Parent("Directory"));

            if (SupportEmptyDirectories == CompilerSupportState.Automatic)
            {
                SupportEmptyDirectories = dummyDirs.Any() ? CompilerSupportState.Enabled : CompilerSupportState.Disabled; //it wasn't set by user so set it if any empty dir is detected
                Compiler.OutputWriteLine("Wix# support for EmptyDirectories is automatically " + SupportEmptyDirectories.ToString().ToLower());
            }

            if (SupportEmptyDirectories == CompilerSupportState.Enabled)
            {
                if (dummyDirs.Any())
                {
                    foreach (var item in dummyDirs)
                    {
                        XElement parent = item.Parent("Directory");
                        while (parent != null)
                        {
                            if (parent.Element("Component") == null)
                            {
                                var dirId = parent.Attribute("Id").Value;
                                if (Compiler.EnvironmentConstantsMapping.ContainsValue(dirId))
                                {
                                    break; //stop when reached start of user defined subdirs chain: TARGETDIR/ProgramFilesFolder!!!/ProgramFilesFolder.Company/INSTALLDIR
                                }
                                //just folder with nothing in it but not the last leaf
                                doc.CrteateComponentFor(parent);
                            }
                            parent = parent.Parent("Directory");
                        }
                    }
                }

                foreach (XElement xDir in product.Descendants("Directory").ToArray())
                {
                    var dirComponents = xDir.Elements("Component");

                    if (dirComponents.Any())
                    {
                        var componentsWithNoFiles = dirComponents.Where(x => !x.ContainsFiles()).ToArray();

                        //'EMPTY DIRECTORY' support processing section
                        foreach (XElement item in componentsWithNoFiles)
                        {
                            // Ridiculous MSI constrains:
                            //  * you cannot install install empty folders
                            //    - workaround is to insert empty component with CreateFolder element
                            //  * if Component+CreateFolder element is inserted the folder will not be removed on uninstall
                            //    - workaround is to insert RemoveFolder element in to empty component as well
                            //  * if Component+CreateFolder+RemoveFolder elements are placed in a dummy component to handle an empty folder
                            //    any parent folder with no files/components will not be removed on uninstall.
                            //    - workaround is to insert Component+Create+RemoveFolder elements in any parent folder with no files.
                            //
                            // OMG!!!! If it is not over-engineering I don't know what is.

                            bool oldAlgorithm = false;

                            if (!oldAlgorithm)
                            {
                                //current approach
                                InsertCreateFolder(item);
                                if (!xDir.ContainsAnyRemoveFolder())
                                {
                                    InsertRemoveFolder(xDir, item, "uninstall");
                                }
                            }
                            else
                            {
                                //old approach
                                if (!item.Attribute("Id").Value.EndsWith(".EmptyDirectory"))
                                {
                                    InsertCreateFolder(item);
                                }
                                else if (!xDir.ContainsAnyRemoveFolder())
                                {
                                    InsertRemoveFolder(xDir, item, "uninstall"); //to keep WiX/compiler happy and allow removal of the dummy directory
                                }
                            }
                        }
                    }
                }
            }
        }