有用過 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"> </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"> </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"> </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"> </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"> </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"> </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"> </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"> </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> </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->'<li>%(ProductName)</li>','%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