【问题标题】:Updating deployment manifest for a ClickOnce application programmatically results in missing <compatibleFrameworks> element, required in 4.0以编程方式更新 ClickOnce 应用程序的部署清单会导致 4.0 中需要的 <compatibleFrameworks> 元素丢失
【发布时间】:2012-06-21 15:34:46
【问题描述】:

我正在为 .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,作为主要来源):

  1. 转到\Application Files\App_%HighestVersion%\ 文件夹
  2. 删除具有 .deploy 扩展名的文件
  3. 运行mage -u %app%.exe.manifest -cf cert.pfx
  4. 恢复 .deploy 扩展
  5. 运行mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx
  6. 复制%app%.application 2 级(到..\.. - 部署根)

如果手动完成,效果会很好。我可以运行一个 .cmd 文件,为环境细节(路径等)定制,但是我需要在部署中包含mage.exe,微软是否允许我们这样做是对我来说是一个悬而未决的问题。因此,我试图在 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 elementWhy does Mage.exe not generate a compatibleFrameworks attribute?MageUI.exe is not including a compatibleFrameworks element,但我没有使用mageuimage 甚至是msbuild

发生了什么事?

【问题讨论】:

  • 作为一种解决方法,您是否尝试过直接操作清单 XML 而不是使用 API?它过去对我们有用 - 您可以完全控制内容,做任何您想做的事情,然后在完成后重新签署文件。
  • Astrogator - 有没有机会分享你的脚本?我即将开始一个非常相似的过程。谢谢!
  • 嗨@ErickT!好吧,算法几乎在这里列出。此代码由 msiexec.exe 通过自定义操作执行,我在 Visual Studio 2010 安装项目中指定 - 本身没有“脚本”。让我知道,如果这还不够清楚,我会尝试揭露更多。

标签: c# .net-4.0 installation clickonce


【解决方案1】:

我自己想出来的。罪魁祸首是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;

2019 年 10 月 8 日
刚刚偶然发现app.manifest 的问题:
compatibility 部分的supportedOS 元素在部署期间被剥离。

相同的根本原因;读取它的行应该将 preserveStream 设置为 true:

ApplicationManifest am = ManifestReader.ReadManifest( "ApplicationManifest", sPathMft, true ) as ApplicationManifest;

【讨论】:

    【解决方案2】:

    我还需要添加 CompatibleFrameworks。我也尝试过像这样添加 CompatibleFrameworks(这不起作用)

    dm.CompatibleFrameworks.Add(...);
    

    我的解决方案是设置:

    dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";                 
    

    在此之后,Manifest 生成是正确的。

    小心如果你在 WriteManifest 之前设置了 TargetFrameworkMoniker,你有两次 并且你的应用程序文件已损坏。这是我的解决方案:

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 2023-03-31
      相关资源
      最近更新 更多