2010-02-22 15 views
6

Ho un file di configurazione che deve essere modificato in base al server, in modo che una volta installato il nostro software su un server, il file di configurazione per un programma di installazione del client sia configurato per corrispondere a quello specifico server impostazioni e quindi copiato in una cartella pubblica sul Web per la distribuzione.Problemi di distribuzione di Mage.exe

Dal momento che sto modificando il file di configurazione, devo anche ricostruire il * manifest ei file * .application, e se ho capito bene, la mia unica vera opzione per questo è di usare Mage.exe dal Win7 SDK . Al fine di correggere il file * manifest con il corretto hash dal file di configurazione modificato, corro:

mago -FD -nuova Application -ToFile "\ Application Files" \ Application Files \ < appName> _1_0_0_0" .. \ _1_0_0_0 \ < appName> exe.manifest" -Name "< appName> 1.0.0.0" versione "" -CertFile "key.pfx" -password "< password>"

e poi, per risolvere il * file di applicazione con l'hash corretto dal file * .manifest modificato, eseguo:

mage -new Deployment -I t -t "< appName> .application" -v "1.0.0.0" -appManifest ". \ File dell'applicazione \ < appName> _1_0_0_0 \ < appName> exe.manifest "-pu "http: // < hostaddress>/< percorso>/Application Files/< appName> _1_0_0_0/< appName> exe.manifest key.pfx" -CertFile" "-password" "

Ora, tutto funziona e ricevo il messaggio che i file sono stati firmati correttamente. Quando provo ad installare l'applicazione client, però, è evidente che qualcosa è andato storto quando ottengo un log degli errori con il messaggio:

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

Nel guardare il file * .application, ha alcune informazioni aggiuntive sotto il nodo "distribuzione", che lo stesso file direttamente dalla funzione di pubblicazione di VS2008 non ha:

<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> 

il VS2008 pubblicare versione ha semplicemente:

<deployment install="true" /> 

Quando Rimuovere le informazioni aggiuntive e impostare il nodo di distribuzione su un nodo auto terminante, quindi firmare di nuovo il file, tutto funziona come previsto.

Si tratta di un problema noto e c'è un modo per ottenere che Mage crei il file senza le informazioni aggiuntive nel nodo di distribuzione in modo che funzioni correttamente?

MODIFICA: Come soluzione temporanea, sto caricando i file in un XmlDocument e li ho modificati per adattarli, quindi firmare nuovamente i file. Inoltre, ora sto affrontando il problema di non essere ancora in grado di determinare come aggiungere un'icona alla distribuzione, quindi l'elemento del menu Start ottiene un'icona diversa dall'icona generica.

+0

Ho un caso d'uso molto simile con problemi simili. Risponderò se trovo una soluzione. –

+0

basta usare il flag -appc sul mago –

+0

Nathan, controlla la mia risposta e vedi se ti aiuta. Utilizzando Mage.exe dovrebbe funzionare bene per te. –

risposta

2

Ecco la mia implementazione. Ho dedicato molto tempo a questo piccolo pezzetto di codice e non ho ancora trovato tutte le opzioni giuste per far gestire a Mage tutta la generazione del file .Application senza alcun intervento. Devo dire che probabilmente ci sono molte ottimizzazioni che potrebbero essere apportate a questo codice. Tuttavia, questo può ancora essere usato come trampolino di lancio per aiutare qualcuno.

Affinché il seguente metodo funzioni, è necessario distribuirlo almeno una volta da ClickOnce in VS, quindi mantenere solo il file .application da tale distribuzione. È NECESSARIO eliminare l'applicazione e .manifest nella cartella di distribuzione.

Dopo ho spostato tutti i file dell'applicazione per 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 per "miserabile". – Tim

1

Se il vostro obiettivo è quello di modificare il vostro manifesto dell'applicazione tra gli ambienti non sono sicuro che il motivo per cui si sta creando una nuova. Basta modificare quello attuale. Sto pubblicando uno script PowerShell che fa quello che ti serve e altro ... Nel mio caso ho un bootstrap di installazione, ma il codice pertinente di cui hai bisogno è verso il basso.

Per il programma di avvio automatico di installazione non si può dimettere un programma di avvio automatico firmato quindi ho dovuto trovare una terza parte dll unsign esso. (Delcert) http://forum.xda-developers.com/showthread.php?t=416175 ho che la madre nel controllo del codice sorgente nel caso in cui scompare dal web un giorno :)

Trova la sezione #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"} 
Problemi correlati