文章出處

為了能夠讓用戶自行部署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",形式如下:

<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的代碼則寫為:

#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了解的不夠,才導致程序的錯誤寫法。因此,改為正確的寫法,問題自然就消失不見了。當然,這個實際上不能稱為“解決辦法” 。
         

另外,該setup.exe也可以使用Microsoft.Build.Tasks.GenerateBootstrapper生成,但是目前只做到生成,尚未進行實踐驗證。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()