2010-02-22 6 views
6

Ich habe eine Konfigurationsdatei, die auf einer pro-Server-Basis geändert werden muss, so dass die Konfigurationsdatei für ein Client-Installationsprogramm eingerichtet wird, um mit dem Server dieses Servers übereinzustimmen, sobald ein Server unsere Software installiert hat Einstellungen, und dann zur Bereitstellung in einen öffentlichen Ordner im Web kopiert.Mage.exe-Bereitstellungsprobleme

Da ich die Konfigurationsdatei ändere, muss ich auch die * .manifest- und * .application-Dateien neu erstellen, und meine einzige wirkliche Option dafür ist die Verwendung von Mage.exe aus dem Win7-SDK . Um die Datei * .manifest mit dem richtigen Hash aus der geänderten Konfigurationsdatei zu reparieren, führe ich Folgendes aus:

mage -new Anwendung -fd ". \ Anwendungsdateien \ < appName> _1_0_0_0" -ToFile ". \ Anwendungsdateien \ _1_0_0_0 \ < appName> exe.manifest“-Name "< appName>" -Version "1.0.0.0" -CertFile "key.pfx" -password "< Passwort>"

und dann zu fixieren, die * .application file mit dem richtigen Hash aus der modifizierten * .manifest-Datei, ich starte:

mage -neu Bereitstellung -I t -t "< appName> .application" -v "1.0.0.0" -appManifest ". \ Anwendungsdateien \ < appName> _1_0_0_0 \ < appName> exe.manifest "-pu "http: // < Hostaddress>/< path>/Anwendungsdateien/< appName> _1_0_0_0/< appName> exe.manifest" -CertFile" key.pfx "-password" "

Jetzt funktioniert das alles, und ich bekomme die Nachricht, dass die Dateien erfolgreich signiert wurden. Wenn ich versuche, obwohl die Client-Anwendung zu installieren, es ist offensichtlich, dass etwas schief gegangen ist, wenn ich ein Fehlerprotokoll mit der Meldung:

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

an der * .application Datei In sucht, hat es einige zusätzliche Informationen unter dem "Deployment" Knoten, der die gleiche Datei direkt aus dem veröffentlichen Feature von VS2008 nicht hat:

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

die VS2008-Version veröffentlichen hat einfach:

<deployment install="true" /> 

Wenn Ich entferne die zusätzlichen Informationen und setze den Deployment-Node auf einen selbst terminierenden Node, dann signiere ich die Datei neu, alles funktioniert wie erwartet.

Ist dies ein bekanntes Problem und gibt es eine Möglichkeit, Mage dazu zu bringen, die Datei ohne die zusätzlichen Informationen im Bereitstellungsknoten zu erstellen, damit sie ordnungsgemäß funktioniert?

BEARBEITEN: Als temporäre Lösung lade ich die Dateien in ein XmlDocument und modifiziere sie entsprechend, um dann die Dateien neu zu signieren. Außerdem stehe ich jetzt vor dem Problem, dass ich noch nicht feststellen kann, wie ein Symbol zur Bereitstellung hinzugefügt wird, sodass der Startmenüeintrag ein anderes Symbol als das allgemeine Symbol erhält.

+0

Ich habe einen sehr ähnlichen Anwendungsfall mit ähnlichen Problemen habe. Ich werde antworten, wenn ich eine Lösung finde. –

+0

benutze einfach die -appc-Flagge auf Magier –

+0

Nathan, überprüfe meine Antwort und schau, ob es dir hilft. Die Verwendung von Mage.exe sollte für Sie gut funktionieren. –

Antwort

2

Hier ist meine Implementierung. Ich habe viel Zeit mit diesem kleinen Code verbracht und habe immer noch nicht die richtigen Optionen gefunden, damit Mage die ganze Generation der .application-Datei ohne Interventionen verarbeiten kann. Ich werde sagen, dass es wahrscheinlich viele Optimierungen gibt, die an diesem Code vorgenommen werden könnten. Dies kann jedoch immer noch als Sprungbrett verwendet werden, um jemandem zu helfen.

Damit die folgende Methode funktioniert, müssen Sie sie mindestens einmal in ClickOnce in VS bereitstellen und dann nur die Anwendungsdatei von dieser Bereitstellung beibehalten. Sie MÜSSEN die .application- und .manifest-Datei im Bereitstellungsordner löschen.

Nachdem ich habe alle Anwendungsdateien Config.Instance.ServerSettings.ClientLocation + "<AppName>_<version>" bewegt:

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 für "elend". – Tim

1

Wenn Ihr Ziel ist Ihre Anwendung manifestieren zwischen Umgebungen zu ändern Ich bin nicht sicher, warum Sie eine neue sind zu schaffen. Ändere einfach deine aktuelle. Ich poste ein Powershell-Skript, das tut, was Sie brauchen und mehr ... In meinem Fall habe ich einen Setup-Bootstrapper, aber der relevante Code, den Sie brauchen, ist nach unten.

Für den Setup-Bootstrapper können Sie nicht einen signierten Bootstrapper zurücktreten, so musste ich eine Drittanbieter-DLL finden, um es zu signieren. (Delcert) http://forum.xda-developers.com/showthread.php?t=416175 Ich habe die Mutter in der Quellcodeverwaltung, falls es von der Bahn 1 Tag verschwindet :)

den Abschnitt findet #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"}