为了可以让用户自行部署ClickOnce应用程序,须要编写一个生成ClickOnce应用程序的ClickOnce专用安装程序setup.exe,而生成这个setup.exe的方法就是编写一个XML格式的生成配置文件,使用MSBuild.exe来建立。
通常状况下,建立XML文件原本是个很简单的事情,用XDocument、XElement、XAttribute一顿Add,而后Save成文件就完成了。可是建立setup.exe用的XML文件的根节点(Project)必须指定XML名称空间"http://schemas.microsoft.com/developer/msbuild/2003",形式以下:
app
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <BootstrapperFile Include=".NETFramework,Version=v4.0"> <ProductName>Microsoft .NET Framework 4 (x86 和 x64)</ProductName> </BootstrapperFile> <BootstrapperFile Include="Microsoft.Windows.Installer.3.1"> <ProductName>Windows Installer 3.1</ProductName> </BootstrapperFile> </ItemGroup> <Target Name="BuildBootstrapper"> <GenerateBootstrapper ApplicationFile="MyApp.application" ApplicationName="个人应用程序" ApplicationUrl="http://192.168.0.8/MyApp/" BootstrapperItems="@(BootstrapperFile)" CopyComponents="false" OutputPath="." Path="X:\SerupExeBuilder\Packages"/> </Target> </Project>
因而,生成该XML的代码则写为:ui
#region ItemGroup XElement itemGroup = new XElement("ItemGroup", new XElement("BootstrapperFile", new XAttribute("Include", ".NETFramework,Version=v4.0"), new XElement("ProductName", "Microsoft .NET Framework 4 (x86 和 x64%)")), new XElement("BootstrapperFile", new XAttribute("Include", "Microsoft.Windows.Installer.3.1"), new XElement("ProductName", "Windows Installer 3.1")) ); #endregion #region Target XElement target = new XElement("Target"); target.Add(new XAttribute("Name", "BuildBootstrapper")); XElement generateBootstrapper = new XElement("GenerateBootstrapper"); generateBootstrapper.Add(new XAttribute("ApplicationFile", applicationFile)); generateBootstrapper.Add(new XAttribute("ApplicationName", applicationName)); generateBootstrapper.Add(new XAttribute("ApplicationUrl", applicationUrl)); if (componentsLocation == 1) { generateBootstrapper.Add(new XAttribute("ComponentsLocation", "Relative")); } else if (componentsLocation == 2 && !string.IsNullOrWhiteSpace(componentsUrl)) { generateBootstrapper.Add(new XAttribute("ComponentsLocation", "Absolute")); generateBootstrapper.Add(new XAttribute("ComponentsUrl", componentsUrl)); } generateBootstrapper.Add(new XAttribute("BootstrapperItems", "@(BootstrapperFile)")); generateBootstrapper.Add(new XAttribute("CopyComponents", false)); generateBootstrapper.Add(new XAttribute("OutputPath", outputPath)); string packagesPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); generateBootstrapper.Add(new XAttribute("Path", Path.GetDirectoryName(Assembly.GetEntryAssembly().Location))); target.Add(generateBootstrapper); #endregion XNamespace xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"; XElement root = new XElement(xmlns + "Project"); root.Add(itemGroup); root.Add(target); XDocument setupDocument = new XDocument(); setupDocument.Add(root); setupDocument.Declaration = new XDeclaration("1.0", "UTF-8", "yes"); setupDocument.Save(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "setup.xml"));
可是生成的XML文件中,ItemGroup和Target节点上会出现 xmlns="" 的属性,而这在编译的时候是不容许的(也是错误的),而又没有办法不让它们出现。
这个问题郁闷了一阵子,也没找到资料,最后用土办法,也就是StringBuilder手工建立,程序是成功了,可是这个问题一直耿耿于怀。
今天终于知道缘由了,固然也知道了“解决办法”了。
其实缘由是,在XML中,若是某个节点使用了NameSpace,则要求其下的每一个节点都必须指定NameSpace的,以便于区分各个节点属于哪个NameSpace。在生成XML文件的时候,若是子节点的NameSpace与父节点相同,则忽略xmlns属性。因此在建立子节点的时候,必须为子节点指定NameSpace,不然就会由于没有指定NameSpace,出现 xmlns="" 的状况。
因此是对XML的NameSpace了解的不够,才致使程序的错误写法。所以,改成正确的写法,问题天然就消失不见了。固然,这个实际上不能称为“解决办法” 。 spa
另外,该setup.exe也可使用Microsoft.Build.Tasks.GenerateBootstrapper生成,可是目前只作到生成,还没有进行实践验证。code