2010-02-22 119 views
6

我有一个配置文件,需要在每个服务器的基础上进行更改,以便一旦服务器安装了我们的软件,客户端安装程序的配置文件就会设置为与该服务器的特定设置,然后复制到Web上的公用文件夹进行部署。Mage.exe部署问题

由于我正在更改配置文件,我还必须重新生成* .manifest和* .application文件,而据我所知,我唯一真正的选择是使用Win7 SDK中的Mage.exe 。为了修复* .manifest文件与修改的配置文件中的正确散列,我运行:

mage -new应用程序-fd“。\ Application Files \ < appName> _1_0_0_0”-ToFile“。\ Application Files \ _1_0_0_0 \ < APPNAME> .exe.manifest” -Name “< APPNAME>” -Version “1.0.0.0” -CertFile “key.pfx” -password “<密码>”

,然后,以固定* .application文件与来自修改的* .manifest文件的正确散列,我运行:

mage -new部署-I t -t“< appName> .application”-v“1.0.0.0”-appManifest“应用程序文件\ < appName> _1_0_0_0 \ < appName> .exe.manifest“-pu”http:// < hostaddress>/< path>/Application Files/< appName> _1_0_0_0/< appName> .exe.manifest“-CertFile”key.pfx “-password”“

现在,这一切正常,我收到消息,文件已成功签署。当我尝试虽然安装客户端应用程序,这是显而易见的事情出了错时,我得到一个错误日志与消息:

+ Deployment manifest is not semantically valid. 
+ Deployment manifest requires <deployment> section. 

在看*。应用文件,它根据一些附加信息“部署”节点,从VS2008的发布功能,直接在同一文件没有:

<deployment install="true"> 
    <subscription> 
    <update> 
     <expiration maximumAge="0" unit="days" /> 
    </update> 
    </subscription> 
    <deploymentProvider codebase="http://<hostaddress>/<path>/Application Files/<appName>_1_0_0_0/<appName>.exe.manifest" /> 
</deployment> 

的VS2008发布的版本只是有:

<deployment install="true" /> 

当我删除了附加信息并将部署节点设置为自行终止节点,然后重新签名该文件,一切都按预期工作。

这是一个已知问题,并且有没有任何方法让Mage在部署节点中没有额外信息的情况下创建文件,以便它能正常工作?

编辑:作为一种临时解决方案,我将文件加载到一个XmlDocument并修改它们以适应,然后重新签名这些文件。另外,我现在面临的问题是仍然无法确定如何将图标添加到展开,因此“开始”菜单项获取的图标不是通用图标。

+0

我有一个非常类似的用例类似的问题。如果我找到解决方案,我会回复。 – 2010-02-23 03:49:11

+0

只需使用标准 – 2013-12-20 11:01:29

+0

Nathan的-appc标志,查看我的答案,看看它是否对您有所帮助。使用Mage.exe应该适合你。 – 2014-08-28 17:01:58

回答

2

这是我的实现。我在这个小小的代码上花了很多时间,并且我还没有找到所有正确的选项让Mage处理所有.application文件的生成而不用干预。我要说的是,可能会对此代码进行很多优化。不过,这仍然可以作为帮助某人的跳板。

为了使以下方法起作用,您必须从VS中的ClickOnce至少部署它一次,然后只保留该部署中的.application文件。您必须删除deploy文件夹中的.application和.manifest。

后,我已经感动了所有的应用程序文件Config.Instance.ServerSettings.ClientLocation + "<AppName>_<version>"

DirectoryInfo filedir = new DirectoryInfo(Config.Instance.ServerSettings.ClientLocation); 

if (filedir.Exists) 
{ 
    FileInfo[] files = filedir.GetFiles(); 

    // Find the current .application file. 
    FileInfo appinfo = null; 
    foreach (FileInfo fi in files) 
    { 
     if (fi.Name == "<AppName>.application") 
     { 
      appinfo = fi; 
      break; 
     } 
    } 

    if (appinfo != null) 
    { 
     XmlDocument applocinfo = new XmlDocument(); 
     applocinfo.Load(appinfo.FullName); 

     // Get the location of the files from the .application file. 
     string codebase = applocinfo["asmv1:assembly"]["dependency"]["dependentAssembly"].Attributes["codebase"].Value.Replace("AppName.exe.manifest", ""); 

     XmlDocument xDoc = new XmlDocument(); 
     xDoc.Load(Path.Combine(Path.Combine(filedir.FullName, codebase), "AppName.exe.config")); 

     foreach (XmlNode xn in xDoc["configuration"]["appSettings"].ChildNodes) 
     { 
      if (xn.Attributes != null && xn.Attributes["key"] != null && xn.Attributes["key"].Value == "Clnt_Host") 
      { 
       // Here is where I'm modifying my config file, the whole purpose in this wretched deployment process. 
       xn.Attributes["value"].Value = Config.Instance.ClientSettings.Host; 
       break; 
      } 
     } 

     xDoc.Save(Path.Combine(Path.Combine(filedir.FullName, codebase), "<AppName>.exe.config")); 

     Process p = new Process(); 
     p.StartInfo = new ProcessStartInfo(Path.Combine(filedir.FullName, "Mage.exe")); 
     p.StartInfo.WorkingDirectory = filedir.FullName; 

     FileInfo fi = new FileInfo(Path.Combine(Path.Combine(filedir.FullName, codebase.TrimStart('.')), "<AppName>.exe.manifest")); 
     if (fi.Exists) 
      fi.Delete(); 

     // Write a new .manifest file as an Application file. (-new Application -ToFile ".\codebase\<AppName.exe.manifest") 
     // Include the files from the codebase directory in the manifest (-fd ".\codebase\") 
     // Give the application a name to use in the start menu (-name "<AppName>") 
     // Assign a version number to the deployment (-Version "<version>") 
     // Give the application an icon to use in the start menu (-IconFile "64x64.ico") 
     // Sign the manifest (-CertFile "<KeyName>.pfx -Password <password>) 
     p.StartInfo.Arguments = "-new Application -fd \".\\" + codebase.TrimEnd('\\') + "\" -ToFile \".\\" + Path.Combine(codebase, "<AppName>.exe.manifest") + "\" -Name \"<AppName>\" -Version \"" + codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\') + "\" -CertFile \"<KeyName>.pfx\" -Password <Password> -IconFile \"64x64.ico\""; 

     while (p.StartInfo.Arguments.Contains(".\\.\\")) 
      p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\"); 

     Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information); 

     p.Start(); 

     while (!p.HasExited) 
     { 
      Thread.Sleep(100); 
     } 

     // Make a new deployment manifest (-new Deployment -t "<AppName>.application") 
     // Make the application available offline (-I t) 
     // Use the files from the .manifest we just made (-AppManifest ".\codebase\<AppName>.exe.manifest") 
     p.StartInfo.Arguments = "-new Deployment -I t -t \"<AppName>.application\" -v \"" + codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\') + "\" -AppManifest \".\\" + codebase + "<AppName>.exe.manifest\" -pu \"http://" + Config.Instance.ClientSettings.Host + "/client/" + codebase.Replace('\\', '/') + "<AppName>.exe.manifest\""; 

        while (p.StartInfo.Arguments.Contains(".\\.\\")) 
      p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\"); 

     Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information); 

     p.Start(); 

     while (!p.HasExited) 
     { 
      Thread.Sleep(100); 
     } 

     xDoc = new XmlDocument(); 
     xDoc.Load(Path.Combine(filedir.FullName, "<AppName>.application")); 

     // Add to the Deployment manifest (.application) to make the application 
     // have a minimum required version of the current version,and makes a 
     // subscription so that the application will always check for updates before 
     // running. 
     if (xDoc["asmv1:assembly"]["deployment"]["subscription"] != null) 
     { 
      xDoc["asmv1:assembly"]["deployment"].RemoveChild(xDoc["asmv1:assembly"]["deployment"]["subscription"]); 
      xDoc["asmv1:assembly"]["deployment"].RemoveChild(xDoc["asmv1:assembly"]["deployment"]["deploymentProvider"]); 
      XmlAttribute node = xDoc.CreateAttribute("minimumRequiredVersion"); 
      node.Value = codebase.Substring(codebase.IndexOf('_') + 1, codebase.Length - (codebase.IndexOf('_') + 1)).Replace('_', '.').TrimEnd('\\'); 
      xDoc["asmv1:assembly"]["deployment"].Attributes.Append(node); 

      xDoc["asmv1:assembly"]["deployment"].InnerXml = "<subscription><update><beforeApplicationStartup /></update></subscription>"; 
     } 

     xDoc.Save(Path.Combine(filedir.FullName, "<AppName>.application")); 

     // Sign the deployment manifest (.application) (-Sign "\<AppName>.application" -CertFile "<AppName>.key" -Password <password> 
     p.StartInfo.Arguments = "-Sign \"<AppName>.application\" -CertFile \"<AppName>.pfx\" -Password <password>"; 

     while (p.StartInfo.Arguments.Contains(".\\.\\")) 
      p.StartInfo.Arguments = p.StartInfo.Arguments.Replace(".\\.\\", ".\\"); 

     Logger.Instance.LogInfo("Starting application: " + p.StartInfo.FileName + "\n\tWith arguments: " + p.StartInfo.Arguments, Logger.InfoType.Information); 

     p.Start(); 

     while (!p.HasExited) 
     { 
      Thread.Sleep(100); 
     } 
    } 
} 
+0

+1为“猥琐”。 – Tim 2013-07-10 11:49:36

1

如果你的目标是修改环境之间的应用程序清单,我不知道为什么你要创建一个新的。只需修改您当前的一个。我发布了一个PowerShell脚本,它可以满足你的需求,而且更多...在我的情况下,我有一个安装引导程序,但是你需要的相关代码是底层。

对于安装引导程序,您不能辞去已签名的引导程序,所以我必须找到第三方的dll来对其进行取消签名。 (delcert)http://forum.xda-developers.com/showthread.php?t=416175我有妈妈在源代码控制的情况下,它从网络1天:)

消失找到段#Begin Resigning various Manifests

$root = "$PSScriptRoot" 
$ToolsPath = "C:\Tools" 
$CertFile = $ToolsPath + "\my cert.pfx" 
$CertPassword = "wouldn't you like to know" 

#Update the setup.exe bootstrappers update url 
Start-Process "$PSScriptRoot\setup.exe" -ArgumentList "-url=`"$ClickOnceUpdateUrl`"" -Wait 

#The bootstrappers signature is now invalid since we updated the url 
#We need to remove the old signature 
Start-Process 'C:\Tools\delcert.exe' -ArgumentList "`"$root\setup.exe`"" -Wait 

Write-Host "$root [writeline]" 
#Resign with signtool 
Invoke-Expression 'C:\Tools\signtool.exe sign /d "My Company" /f "$CertFile" /p "$CertPassword" "$root\setup.exe"' 

#update config properties 
$CodeBasePath = Convert-Path "$PSScriptRoot\Application Files\MyProduct_*" 
$ConfigPath = $CodeBasePath + "\MyProduct.dll.config.deploy" 
[xml] $xml = Get-Content $ConfigPath 

$Endpoint = $xml.SelectSingleNode('/configuration/appSettings/add[@key="MailCheckerEndpoint"]') 
$Endpoint.value = $MailCheckerEndpoint 

$ApiEndpoint = $xml.SelectSingleNode('/configuration/appSettings/add[@key="MyApi:ApiBaseUrl"]') 
$ApiEndpoint.value = $MyProductApiEndpoint 
$xml.Save($ConfigPath) 

#Begin Resigning various Manifests 
$AppManifestPath = Convert-Path "Application Files\MyCompany_*\MyCompany.dll.manifest" 

#Need to resign the application manifest, but before we do we need to rename all the files back to their original names (remove .deploy) 
Get-ChildItem "$CodeBasePath\*.deploy" -Recurse | Rename-Item -NewName { $_.Name -replace '\.deploy','' } 

#Resign application manifest 
Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MyCompany.dll.manifest" -certFile "$CertFile" -password "$CertPassword" -if "Application Files\MyCompany_1_2_35_0\Resources\ID.ico"' 

#Regisn deployment manifests in root and versioned folder 
Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MyCompany.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "My Company" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"' 
Invoke-Expression 'C:\Tools\mage.exe -update "$root\MyComapny.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "My company" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"' 

#Rename files back to the .deploy extension, skipping the files that shouldn't be renamed 
Get-ChildItem -Path "Application Files\*" -Recurse | Where-Object {!$_.PSIsContainer -and $_.Name -notlike "*.manifest" -and $_.Name -notlike "*.vsto"} | Rename-Item -NewName {$_.Name + ".deploy"}