[ClickOnce][MsBuild] 解決使用 MsBuild 不會產生 Html Page

有用過 VS IDE 發行 ClickOnce 應用程式的人應該不陌生,發行時 VS 會幫我們產生 HTML 頁面,把頁面放到 Server 看起來就像那麼一回事;當換到了用指令碼的時候,就不會產生 HTML 了,簽章也是,為了解決這個問題花了我將近兩天的時間

開發環境

VS 2017

 

先開一個 Winform 專案,在發佈輸入頁面名稱

 

MsBuild 腳本內容如下,將以下內容令存成 build.bat 和方案檔同一目錄

@echo off
SET MSBUILD="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe"
::SET MSBUILD="C:\Program Files (x86)\MSBuild\14.0\bin\msbuild.exe"

SET CARWIN="Simple.Winfrom.BuildClickOnce.sln"

%MSBUILD% %CARWIN% /target:publish /p:platform="any cpu" /p:configuration="debug" /p:VisualStudioVersion="14.0" /p:ApplicationVersion=2018.07.19.1 /p:TargetCulture=zh-TW
pause

 

執行結果,沒有產生 HTML 檔案

把以下內容另存 publish.template.html 並且加到專案內

<HTML>
  <HEAD>
    <TITLE>${ProductName}</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8" />
    <STYLE TYPE="text/css">
<!--
BODY{margin-top:20px; margin-left:20px; margin-right:20px; color:#000000; font-family:Verdana; background-color:white}
A:link {font-weight:normal; color:#000066; text-decoration:none}
A:visited {font-weight:normal; color:#000066; text-decoration:none}
A:active {font-weight:normal; text-decoration:none}
A:hover {font-weight:normal; color:#FF6600; text-decoration:none}
P {margin-top:0px; margin-bottom:12px; color:#000000; font-family:Verdana}
PRE {border-right:#f0f0e0 1px solid; padding-right:5px; border-top:#f0f0e0 1px solid; margin-top:-5px; padding-left:5px; font-size:x-small; padding-bottom:5px; border-left:#f0f0e0 1px solid; padding-top:5px; border-bottom:#f0f0e0 1px solid; font-family:Courier New; background-color:#e5e5cc}
TD {font-size:12px; color:#000000; font-family:Verdana}
H2 {border-top: #003366 1px solid; margin-top:25px; font-weight:bold; font-size:1.5em; margin-bottom:10px; margin-left:-15px; color:#003366}
H3 {margin-top:10px; font-size: 1.1em; margin-bottom: 10px; margin-left: -15px; color: #000000}
UL {margin-top:10px; margin-left:20px}
OL {margin-top:10px; margin-left:20px}
LI {margin-top:10px; color: #000000}
FONT.value {font-weight:bold; color:darkblue}
FONT.key {font-weight: bold; color: darkgreen}
.divTag {border:1px; border-style:solid; background-color:#FFFFFF; text-decoration:none; height:auto; width:auto; background-color:#cecece}
.BannerColumn {background-color:#000000}
.Banner {border:0; padding:0; height:8px; margin-top:0px; color:#ffffff; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#1c5280',EndColorStr='#FFFFFF');}
.BannerTextCompany {font:bold; font-size:18pt; color:#cecece; font-family:Tahoma; height:0px; margin-top:0; margin-left:8px; margin-bottom:0; padding:0px; white-space:nowrap; filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2,OffY=2,Color='black',Positive='true');}
.BannerTextApplication {font:bold; font-size:18pt; font-family:Tahoma; height:0px; margin-top:0; margin-left:8px; margin-bottom:0; padding:0px; white-space:nowrap; filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2,OffY=2,Color='black',Positive='true');}
.BannerText {font:bold; font-size:18pt; font-family:Tahoma; height:0px; margin-top:0; margin-left:8px; margin-bottom:0; padding:0px; filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2,OffY=2,Color='black',Positive='true');}
.BannerSubhead {border:0; padding:0; height:16px; margin-top:0px; margin-left:10px; color:#ffffff; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#4B3E1A',EndColorStr='#FFFFFF');}
.BannerSubheadText {font:bold; height:11px; font-size:11px; font-family:Tahoma; margin-top:1; margin-left:10; filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2,OffY=2,Color='black',Positive='true');}
.FooterRule {border:0; padding:0; height:1px; margin:0px; color:#ffffff; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#4B3E1A',EndColorStr='#FFFFFF');}
.FooterText {font-size:11px; font-weight:normal; text-decoration:none; font-family:Verdana; margin-top:10; margin-left:0px; margin-bottom:2; padding:0px; color:#999999; white-space:nowrap}
.FooterText A:link {font-weight:normal; color:#999999; text-decoration:underline}
.FooterText A:visited {font-weight:normal; color:#999999; text-decoration:underline}
.FooterText A:active {font-weight:normal; color:#999999; text-decoration:underline}
.FooterText A:hover {font-weight:normal; color:#FF6600; text-decoration:underline}
.ClickOnceInfoText {font-size:11px; font-weight:normal; text-decoration:none; font-family:Tahoma; margin-top:0; margin-right:2px; margin-bottom:0; padding:0px; color:#000000}
.InstallTextStyle {font:bold; font-size:14pt; font-family:Tahoma; a:#FF0000; text-decoration:None}
.DetailsStyle {margin-left:30px}
.ItemStyle {margin-left:-15px; font-weight:bold}
.StartColorStr {background-color:#4B3E1A}
.JustThisApp A:link {font-weight:normal; color:#000066; text-decoration:underline}
.JustThisApp A:visited {font-weight:normal; color:#000066; text-decoration:underline}
.JustThisApp A:active {font-weight:normal; text-decoration:underline}
.JustThisApp A:hover {font-weight:normal; color:#FF6600; text-decoration:underline}
-->

</STYLE>

</HEAD>
  <BODY ">
    <TABLE WIDTH="100%" CELLPADDING="0" CELLSPACING="2" BORDER="0">

<!-- Begin Banner -->
<TR><TD><TABLE CELLPADDING="2" CELLSPACING="0" BORDER="0" BGCOLOR="#cecece" WIDTH="100%"><TR><TD><TABLE BGCOLOR="#1c5280" WIDTH="100%" CELLPADDING="0" CELLSPACING="0" BORDER="0"><TR><TD CLASS="Banner" /></TR><TR><TD CLASS="Banner"><SPAN CLASS="BannerTextCompany">${PublisherName}</SPAN></TD></TR><TR><TD CLASS="Banner"><SPAN CLASS="BannerTextApplication">${ProductName}</SPAN></TD></TR><TR><TD CLASS="Banner" ALIGN="RIGHT" /></TR></TABLE></TD></TR></TABLE></TD></TR>
<!-- End Banner -->


<!-- Begin Dialog -->
<TR><TD ALIGN="LEFT"><TABLE CELLPADDING="2" CELLSPACING="0" BORDER="0" WIDTH="540"><TR><TD WIDTH="496">

<!-- Begin AppInfo -->
<TABLE><TR><TD COLSPAN="3">&nbsp;</TD></TR><TR><TD><B>Name:</B></TD><TD WIDTH="5"><SPACER TYPE="block" WIDTH="10" /></TD><TD>${ProductName}</TD></TR><TR><TD COLSPAN="3">&nbsp;</TD></TR><TR><TD><B>Version:</B></TD><TD WIDTH="5"><SPACER TYPE="block" WIDTH="10" /></TD><TD>${ApplicationVersion}</TD></TR><TR><TD COLSPAN="3">&nbsp;</TD></TR><TR><TD><B>Publisher:</B></TD><TD WIDTH="5"><SPACER TYPE="block" WIDTH="10" /></TD><TD>${PublisherName}</TD></TR><tr><td colspan="3">&nbsp;</td></tr><TR><TD><B>Published By:</B></TD><TD WIDTH="5"><SPACER TYPE="block" WIDTH="10" /></TD><TD>${Username}</TD></TR><tr><td colspan="3">&nbsp;</td></tr><TR><TD><B>Published At:</B></TD><TD WIDTH="5"><SPACER TYPE="block" WIDTH="10" /></TD><TD>${PublishTime}</TD></TR><tr><td colspan="3">&nbsp;</td></tr>

</TABLE>
<!-- End AppInfo -->


<!-- Begin Prerequisites -->
<TABLE ID="BootstrapperSection" BORDER="0"><TR><TD COLSPAN="2">The following prerequisites are required:</TD></TR><TR><TD WIDTH="10">&nbsp;</TD><TD><UL>
${Prerequsites}
</UL></TD></TR><TR><TD COLSPAN="2">
If these components are already installed, you can <SPAN CLASS="JustThisApp"><A HREF="${ApplicationManifestFilename}">launch</A></SPAN> the application now. Otherwise, click the button below to install the prerequisites and run the application.
</TD></TR><TR><TD COLSPAN="2">&nbsp;</TD></TR></TABLE>
<!-- End Prerequisites -->


</TD></TR></TABLE>
<!-- Begin Buttons -->
<TR><TD ALIGN="LEFT"><TABLE CELLPADDING="2" CELLSPACING="0" BORDER="0" WIDTH="540" STYLE="cursor:hand" ONCLICK="window.navigate(InstallButton.href)"><TR><TD ALIGN="LEFT"><TABLE CELLPADDING="1" BGCOLOR="#333333" CELLSPACING="0" BORDER="0"><TR><TD><TABLE CELLPADDING="1" BGCOLOR="#cecece" CELLSPACING="0" BORDER="0"><TR><TD><TABLE CELLPADDING="1" BGCOLOR="#efefef" CELLSPACING="0" BORDER="0"><TR><TD WIDTH="20"><SPACER TYPE="block" WIDTH="20" HEIGHT="1" /></TD><TD><A ID="InstallButton" HREF="setup.exe">Install</A></TD><TD width="20"><SPACER TYPE="block" WIDTH="20" HEIGHT="1" /></TD></TR></TABLE></TD></TR></TABLE></TD></TR></TABLE></TD><TD WIDTH="15%" ALIGN="right" /></TR></TABLE></TD></TR>
<!-- End Buttons -->
</TD></TR>
<!-- End Dialog -->



<!-- Spacer Row -->
<TR><TD>&nbsp;</TD></TR>

<TR><TD>
<!-- Begin Footer -->
<TABLE WIDTH="100%" CELLPADDING="0" CELLSPACING="0" BORDER="0" BGCOLOR="#ffffff"><TR><TD HEIGHT="5"><SPACER TYPE="block" HEIGHT="5" /></TD></TR><TR><TD CLASS="FooterText" ALIGN="center"><A HREF="http://msdn.microsoft.com/clickonce">ClickOnce and .NET Framework Resources</A>
</TD></TR><TR><TD HEIGHT="5"><SPACER TYPE="block" HEIGHT="5" /></TD></TR><TR><TD HEIGHT="1" bgcolor="#cecece"><SPACER TYPE="block" HEIGHT="1" /></TD></TR></TABLE>
<!-- End Footer -->
</TD></TR>

</TABLE>
  </BODY>
</HTML>

 

從 Nuget 上安裝MSBuildTasks

Install-Package MSBuildTasks

 

在專案加入變數

  <ItemGroup>
    <Tokens Include="PublisherName">
      <ReplacementValue>$(PublisherName)</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
    <Tokens Include="ProductName">
      <ReplacementValue>$(ProductName)</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
    <Tokens Include="ApplicationVersion">
      <ReplacementValue>$(ApplicationVersion)</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
    <Tokens Include="Prerequsites">
      <ReplacementValue>@(BootstrapperPackage->'&lt;li&gt;%(ProductName)&lt;/li&gt;','%0D%0A')</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
    <Tokens Include="Username">
      <ReplacementValue>$(Username)</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
    <Tokens Include="ApplicationManifestFilename">
      <ReplacementValue>$(AssemblyName).application</ReplacementValue>
      <Visible>false</Visible>
    </Tokens>
  </ItemGroup>

 

在專案檔加入觸發取代 HTML 範本事件

  <Target Name="AfterPublish">
    <Time Format="dd/MM/yyyy HH:mm">
      <Output TaskParameter="FormattedTime" PropertyName="PublishTime" />
    </Time>
    <TemplateFile Template="$(ProjectDir)\publish.template.html" Tokens="@(Tokens)" OutputFilename="$(PublishDir)default.htm" />
    <FileUpdate Files="$(PublishDir)default.htm" Regex="\${PublishTime}" ReplacementText="$(PublishTime)" />
  </Target>

 

執行結果

頁面內容如下

專案位置如下:
https://github.com/yaochangyu/sample.dotblog/tree/master/ClickOnce/MsBuild

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo