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

Ast*_*tor 6 c# installation clickonce .net-4.0

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

我选择使用.deploy扩展来最小化IIS/Internet Explorer安全机制的问题,因此基本上算法如下(基于ClickOnce(Saurabh Bhatia)中的签名和重新签名清单以及使用Mage的ClickOnce WPF应用程序的更新配置或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%.application2级(到..\..- 部署根)

如果手动完成,那将非常有效.我可以运行一个.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);
Run Code Online (Sandbox Code Playgroud)

现在,这是踢球者.除步骤5外,一切都运行良好.当应用程序下载到用户的计算机时,部署清单存在问题:

  • 部署清单在语义上不有效.
  • 缺少部署清单<compatibleFrameworks>.

实际上,此部分已不再存在(但是,它位于原始的%app%.应用程序中!).ClickOnce - .NET 4.0错误中描述了类似的结果:"部署清单在语义上无效"和"部署清单缺失<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);
Run Code Online (Sandbox Code Playgroud)

但是在dm.ResolveFiles(),dm.UpdateFileInfo()ManifestWriter.WriteManifest(..)之前,无论我放置此代码的位置,这都没有效果!

我的结果类似于Stack Overflow问题MageUI.exe删除compatibleFrameworks元素为什么Mage.exe不生成compatibleFrameworks属性?MageUI.exe不包含compatibleFrameworks元素,但我没有使用 mageui,mage甚至msbuild根本没有!

这是怎么回事?

Ast*_*tor 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
}
Run Code Online (Sandbox Code Playgroud)

@davidair,谢谢你的建议!同意,虽然我更喜欢使用API​​对象(与XML相比).
另一种方法是调用mage(直接或从.cmd文件),因为我们似乎可以重新分发它.


我还添加了以下部分,它对问题本身没有影响,但对于遵循相同路径的任何人来说可能非常重要(/ 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;
Run Code Online (Sandbox Code Playgroud)