以编程方式更新ClickOnce应用程序的部署清单会导致缺少<compatibleframeworks>元素,在4.0中需要</compatibleframeworks>

时间:2012-06-21 15:34:46

标签: c# .net-4.0 installation clickonce

我正致力于自动化.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. 删除包含
  3. 的文件的 .deploy 扩展程序
  4. 运行mage -u %app%.exe.manifest -cf cert.pfx
  5. 恢复 .deploy 扩展程序
  6. 运行mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx
  7. 复制%app%.application 2级(至..\.. - 部署根目录)
  8. 如果手动完成,效果很好。我可以运行 .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外,一切都运行良好。当应用程序下载到用户的计算机时,部署清单出现问题:

    • 部署清单在语义上无效。
    • 缺少部署清单&lt; compatibleFrameworks&gt;。

    实际上,此部分已不再存在(但是,它位于原始%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 ,但我根本没有使用mageuimage甚至msbuild

    发生了什么事?

2 个答案:

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