2010-02-22 118 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"}