Проблемы с развертыванием Mage.exe - PullRequest
7 голосов
/ 22 февраля 2010

У меня есть файл конфигурации, который необходимо изменить для каждого сервера, поэтому после установки на нем нашего программного обеспечения файл конфигурации для установщика клиента настраивается в соответствии с конкретными настройками этого сервера, а затем копируетсяв общую папку в Интернете для развертывания.

Поскольку я изменяю файл конфигурации, мне также нужно пересобрать файлы * .manifest и * .application, и, насколько я понимаю, единственная реальная возможность для этого - использовать Mage.exe изWin7 SDK.Чтобы исправить файл * .manifest с правильным хешем из измененного файла конфигурации, я запускаю:

mage -new Application -fd ". \ Application Files \ _1_0_0_0" -ToFile ". \Файлы приложения \ _1_0_0_0 \ .exe.manifest "-Name" "-Version" 1.0.0.0 "-CertFile" key.pfx "-password" "

, а затем,чтобы исправить файл * .application с правильным хешем из измененного файла * .manifest, я запускаю:

mage -new Deployment -I t -t " .application" -v "1.0.0.0"-appManifest". \ Файлы приложений \ _1_0_0_0 \ .exe.manifest "-pu" http:////Application Файлы / _1_0_0_0 / .exe.manifest "-CertFile" key.pfx "-password ""

Теперь все это работает, и я получаю сообщение, что файлы были успешно подписаны.Когда я пытаюсь установить клиентское приложение, становится очевидно, что что-то пошло не так, когда я получаю журнал ошибок с сообщением:

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

При просмотре файла * .application, у него есть некоторая дополнительная информацияпод узлом "развертывание", который не имеет тот же файл непосредственно из функции публикации 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" />

Когда я удаляюдополнительную информацию и задайте для узла развертывания самозавершающийся узел, затем заново подпишите файл, все работает как положено.

Это известная проблема, и есть ли способ заставить Маг создать файл бездополнительная информация в узле развертывания, чтобы он работал правильно?

РЕДАКТИРОВАТЬ: В качестве временного решения я загружаю файлы в XmlDocument и изменяю их в соответствии с требованиями, а затем повторно подписываю файлы.Кроме того, теперь я сталкиваюсь с проблемой того, что пока не могу определить, как добавить значок в развертывание, поэтому элемент меню «Пуск» получает значок, отличный от общего значка.

Ответы [ 2 ]

2 голосов
/ 26 февраля 2010

Вот моя реализация. Я потратил много времени на этот маленький кусочек кода, и я до сих пор не нашел все правильные варианты, чтобы Mage мог обрабатывать все генерации файла .application без вмешательства. Я собираюсь сказать, что, вероятно, есть много оптимизаций, которые можно было бы сделать для этого кода. Тем не менее, это все еще может использоваться в качестве трамплина, чтобы помочь кому-то.

Чтобы следующий метод работал, вам нужно развернуть его хотя бы один раз из ClickOnce в VS, а затем просто сохранить файл .application из этого развертывания. Вы ДОЛЖНЫ удалить .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);
        }
    }
}
1 голос
/ 28 августа 2014

Если ваша цель - изменить манифест приложения между средами, я не уверен, почему вы создаете новую. Просто измените свой текущий. Я публикую сценарий powershell, который делает то, что вам нужно, и даже больше ... В моем случае у меня есть загрузчик настроек, но вам нужен соответствующий код внизу.

Для установочного загрузчика вы не можете оставить подписанный загрузчик, поэтому мне пришлось найти стороннюю dll, чтобы подписать ее. (delcert) http://forum.xda -developers.com / showthread.php? t = 416175 У меня есть эта мать в системе контроля версий на случай, если она однажды исчезнет из сети:)

Найти раздел #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"}
...