我正致力于自动化.NET 4.0 ClickOnce WPF应用程序的安装程序,该应用程序需要在 app.config 文件中设置一些项目。我已经经历了一个棘手的过程,即使用Mage.exe找到我必须遵循的具体步骤(即更新和重新签署应用程序和部署清单),现在我正在尝试自动安装它。
我选择使用 .deploy 扩展来最小化IIS / Internet Explorer安全机制的问题,因此基本上算法如下(基于 Signing and re-signing manifests in ClickOnce (Saurabh Bhatia) 和 Update Configuration of a ClickOnce WPF Application Using Mage or MageUI ,作为其他主要来源):
\Application Files\App_%HighestVersion%\
文件夹mage -u %app%.exe.manifest -cf cert.pfx
mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx
%app%.application
2级(至..\..
- 部署根目录)如果手动完成,效果很好。我可以运行 .cmd 文件,根据环境细节(路径等)进行自定义,但是我需要在部署中包含mage.exe
,以及Microsoft是否允许我们这样做这对我来说是一个悬而未决的问题。因此,我正在尝试在Installer
类中执行类似的操作:
X509Certificate2 ct = new X509Certificate2(sPathCert);
// .. Remove .deploy extension (for files in the sPathApp folder).
sPathMft = Directory.GetFiles(sPathApp, "*.exe.manifest")[0];
ApplicationManifest am = ManifestReader.ReadManifest( "ApplicationManifest", sPathMft, false ) as ApplicationManifest;
if (am == null)
throw new ArgumentNullException("AppManifest");
am.ResolveFiles();
am.UpdateFileInfo( );
ManifestWriter.WriteManifest(am, sPathMft);
SecurityUtilities.SignFile(ct, null, sPathMft);
// .. Restore .deploy extensions to files touched above.
sPathMft = Directory.GetFiles(sPathApp, "*.application")[0];
DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", sPathMft, false) as DeployManifest;
if (dm == null)
throw new ArgumentNullException( "DplManifest" );
dm.ResolveFiles();
dm.UpdateFileInfo();
ManifestWriter.WriteManifest(dm, sPathMft);
SecurityUtilities.SignFile(ct, null, sPathMft);
File.Copy(sPathMft, sPathBin + "\\" + dm.AssemblyIdentity.Name, true);
现在,这是踢球者。除步骤5外,一切都运行良好。当应用程序下载到用户的计算机时,部署清单出现问题:
实际上,此部分已不再存在(但是,它位于原始%app%.application !)。 ClickOnce - .NET 4.0 errors: "Deployment manifest is not semantically valid" and "Deployment manifest is missing <compatibleFrameworks>" 中描述了类似的结果,但它是不同进程(msbuild)的结果。这个部分对于4.0清单是新的(并且是必需的),所以我唯一的猜测是,当ManifestWriter持续更改磁盘时,它以3.5的方式执行它?我三重检查是否使用了正确的库(C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.Build.Tasks.v4.0.dll)。 什么给出了?
到目前为止我代替答案我尝试手动添加缺失的部分:
dm.CompatibleFrameworks.Clear(); // Unnecessary as dm.CompatibleFrameworks.Count == 0 indeed!
CompatibleFramework cf = new CompatibleFramework();
cf.Version= "4.0";
cf.SupportedRuntime = "4.0.30319";
cf.Profile= "Client";
dm.CompatibleFrameworks.Add(cf);
cf = new CompatibleFramework();
cf.Version = "4.0";
cf.SupportedRuntime = "4.0.30319";
cf.Profile = "Full";
dm.CompatibleFrameworks.Add(cf);
但是在 dm.ResolveFiles(), dm.UpdateFileInfo()或 ManifestWriter.WriteManifest之前,无论我放置此代码的位置都没有效果(..)!
我的结果类似于Stack Overflow问题 MageUI.exe removes compatibleFrameworks element 或 Why does Mage.exe not generate a compatibleFrameworks attribute? 或 MageUI.exe is not including a compatibleFrameworks element ,但我根本没有使用mageui
,mage
甚至msbuild
!
发生了什么事?
答案 0 :(得分:8)
自己想出来。罪魁祸首是 ManifestReader.ReadManifest(“DeployManifest”,sPathMft, true )。
MSDN表示,[preserveStream参数] “指定是否在生成的清单对象的InputStream属性中保留输入流。由ManifestWriter用于重构未在对象表示中表示的输入。 “
除了措辞之外,设置 true 本身是不够的:dm.CompatibleFrameworks.Count
仍然是0,但现在添加CompatibleFramework
项将产生效果!
对于同一条船上的其他人,我在dm.ResolveFiles( )
:
if( dm.CompatibleFrameworks.Count <= 0 )
{
CompatibleFramework cf= new CompatibleFramework( );
cf.Profile= "Client"; cf.Version= "4.0"; cf.SupportedRuntime= "4.0.30319";
dm.CompatibleFrameworks.Add( cf ); // cf= new CompatibleFramework( );
cf.Profile= "Full"; // cf.Version= "4.0"; cf.SupportedRuntime= "4.0.30319";
dm.CompatibleFrameworks.Add( cf ); /// no need for separate object
}
@davidair,谢谢你的建议!同意,虽然我更喜欢使用API对象(与XML相比)
另一种方法是调用mage
(直接或从.cmd文件),因为我们are allowed似乎要重新分发它。
我还添加了以下部分,它对问题本身没有影响,但对于遵循相同路径的任何人来说可能非常重要( / client 是部署根目录,并且可以定制):
dm.DeploymentUrl= string.Format( "http://{0}/{1}/client/{1}.application",
Dns.GetHostName( ), Context.Parameters[ scTokVirtDir ] );
dm.UpdateMode= UpdateMode.Background;
dm.UpdateUnit= UpdateUnit.Weeks;
dm.UpdateInterval= 1;
dm.UpdateEnabled= true;
答案 1 :(得分:1)
我还需要添加CompatibleFrameworks。我也试过添加像这样的CompatibleFrameworks(它不起作用)
dm.CompatibleFrameworks.Add(...);
我的解决方案是设置:
dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";
在此之后,Manifest一代是正确的。
注意如果在WriteManifest之前设置TargetFrameworkMoniker,则可以使用&lt; compatibleFrameworks&gt;两次,您的应用程序文件已损坏。以下是我的解决方案:
DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", applicationFileName, false) as DeployManifest;
dm.ResolveFiles();
//doing stuff..
dm.UpdateFileInfo();
ManifestWriter.WriteManifest(dm, applicationFileName);
dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";
ManifestWriter.WriteManifest(dm, applicationFileName);