我正在为 .NET 4.0 ClickOnce WPF 应用程序自动化安装程序,该应用程序需要在 app.config 文件中设置一些项目。我已经完成了使用 Mage.exe(即更新和重新签名应用程序和部署 list )查找必须遵循的特定步骤的棘手过程,现在正尝试将其自动化以进行安装。

我选择使用 .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%\ 文件夹
  • 删除 .deploy 扩展名的文件
  • 运行 mage -u %app%.exe.manifest -cf cert.pfx
  • 恢复 .deploy 扩展名
  • 运行 mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx
  • 复制 %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 步外,一切正常。当应用程序下载到用户的机器时,部署 list 出现问题:
  • 部署 list 在语义上无效。
  • 部署 list 缺少 。

  • 事实上,这个部分不再存在(但是,它在原始的 %app%.application! 中)。 ClickOnce - .NET 4.0 errors: "Deployment manifest is not semantically valid" and "Deployment manifest is missing <compatibleFrameworks>" 中描述了类似的结果,但它是不同过程 (msbuild) 的结果。本节是 4.0 list 的新内容(并且是必需的),所以我唯一的猜测是,当 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(..) 之前,这都没有效果!

    我的结果类似于堆栈溢出问题 MageUI.exe removes compatibleFrameworks elementWhy does Mage.exe not generate a compatibleFrameworks attribute?MageUI.exe is not including a compatibleFrameworks element ,但我根本没有使用 mageuimage 甚至 msbuild !

    这是怎么回事?

    最佳答案

    我自己想出来的。罪魁祸首是 ManifestReader.ReadManifest( "DeployManifest", sPathMft, true )。

    MSDN 说,[preserveStream 参数]“指定是否在结果 list 对象的 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-08
    偶然发现了 app.manifest 的一个问题:
    带有 compatibility 元素的 supportedOS 部分在部署期间被删除。

    相同的根本原因;读取它的行应该将 preserveStream 设置为 true :
    ApplicationManifest am = ManifestReader.ReadManifest( "ApplicationManifest", sPathMft, true ) as ApplicationManifest;
    

    关于c# - 以编程方式更新 ClickOnce 应用程序的部署 list 会导致缺少 <compatibleFrameworks> 元素,这在 4.0 中是必需的,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11141655/

    10-11 11:46