Вопрос по installation, c#, clickonce, .net-4.0 – Обновление манифеста развертывания для приложения ClickOnce программным способом приводит к отсутствию элемента <compatibilityFrameworks>, требуемого в 4.0

5

Я работаю над автоматизацией установщика для приложения .NET 4.0 ClickOnce WPF, для которого необходимо установить несколько элементов вapp.config файл. Я прошел сложный процесс поиска конкретных шагов, которым я должен следовать, используяMage.exe (то есть обновлять и переподписывать манифесты приложения и развертывания), а сейчас пытаюсь автоматизировать его для установки.

Я решил использовать.deploy расширение, чтобы минимизировать проблемы сIIS/ Internet & # xA0; механизмы безопасности Explorer, поэтому, по сути, алгоритм выглядит следующим образом (на основеПодписание и повторная подпись манифестов в ClickOnce (Saurabh Bhatia) а такжеОбновление конфигурации приложения ClickOnce WPF с использованием Mage или MageUIв качестве первоисточника среди прочих):

Go to the \Application Files\App_%HighestVersion%\ folder Remove .deploy extension for files that have it Run  mage -u %app%.exe.manifest -cf cert.pfx Restore .deploy extension Run  mage -u %app%.application -appm %app%.exe.manifest -cf cert.pfx Copy %app%.application 2 levels up (to ..\.. - deployment root)

Это отлично работает, если сделано вручную. Я могу запустить.cmd файл, настроенный в соответствии со спецификой среды (пути и т. д.), но затем мне нужно включитьmage.exe в развертывании, и позволяет ли нам это делать Microsoft - это открытый вопрос для меня. Таким образом, я пытаюсь выполнить аналогичные действия вInstaller учебный класс:

X509Certificate2 ct = new X509Certificate2(sPathCert);

//  .. Remove .deploy extension (for files in the sPathApp folder).

sPathMft = Directory.GetFiles(sPathApp, "*.exe.manifest")[0];
ApplicationManifest am = ManifestReader.ReadManifest( "ApplicationManifest", sPathMft, false ) as ApplicationManifest;
if (am == null)
    throw new ArgumentNullException("AppManifest");
am.ResolveFiles();
am.UpdateFileInfo( );
ManifestWriter.WriteManifest(am, sPathMft);
SecurityUtilities.SignFile(ct, null, sPathMft);

//    .. Restore .deploy extensions to files touched above.

sPathMft = Directory.GetFiles(sPathApp, "*.application")[0];
DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", sPathMft, false) as DeployManifest;
if (dm == null)
    throw new ArgumentNullException( "DplManifest" );
dm.ResolveFiles();
dm.UpdateFileInfo();
ManifestWriter.WriteManifest(dm, sPathMft);
SecurityUtilities.SignFile(ct, null, sPathMft);

File.Copy(sPathMft, sPathBin + "\\" + dm.AssemblyIdentity.Name, true);

Now, here's the kicker, Все работает отлично, за исключением шага 5. Когда приложение загружается на компьютер пользователя, возникает проблема с манифестом развертывания:

Deployment manifest is not semantically valid. Deployment manifest is missing <compatibleFrameworks>.

Действительно, этого раздела больше нет (however, it was in the original %app%.application!). Аналогичный результат описан вClickOnce - ошибки .NET 4.0: & quot; манифест развертывания не является семантически действительным & quot; и "Манифест развертывания отсутствует & lt; compatibilityFrameworks & gt;", но является результатом другого процесса (msbuild). Этот раздел является новым (и обязательным) для манифестов 4.0, поэтому я могу только предположить, что когда ManifestWriter сохраняет изменения на диске, он делает это в стиле 3.5? Я трижды проверил, что используется правильная библиотека (C: \ Program Files (x86) \ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.Build.Tasks.v4.0.dll).What gives?

Вместо ответа я попытался добавить отсутствующий раздел вручную:

dm.CompatibleFrameworks.Clear(); // Unnecessary as dm.CompatibleFrameworks.Count == 0 indeed!
CompatibleFramework cf = new CompatibleFramework();
cf.Version= "4.0";
cf.SupportedRuntime = "4.0.30319";
cf.Profile= "Client";
dm.CompatibleFrameworks.Add(cf);
cf = new CompatibleFramework();
cf.Version = "4.0";
cf.SupportedRuntime = "4.0.30319";
cf.Profile = "Full";
dm.CompatibleFrameworks.Add(cf);

Но это не имеет никакого значения, независимо от того, где я размещаю этот код, доdm.ResolveFiles( ), dm.UpdateFileInfo( ) или жеManifestWriter.WriteManifest(..)!

Мой результат похож на вопросы переполнения стекаMageUI.exe удаляет совместимый элемент Frameworks или жеПочему Mage.exe не генерирует атрибут compatibilityFrameworks? или жеMageUI.exe не включает совместимый элемент Frameworksно я не пользуюсьmageui, mage или дажеmsbuild совсем!

Что происходит?

В качестве обходного пути, вы пытались напрямую манипулировать XML-манифестом вместо использования API-интерфейсов? В прошлом это работало для нас: вы получаете полный контроль над содержимым, делаете все, что хотите, а затем заново подписываете файл, как только закончите. David Airapetyan
Привет @ErickT! Ну, алгоритм в значительной степени изложен здесь. Этот код выполняется msiexec.exe с помощью настраиваемого действия, которое я указываю в проекте установки Visual Studio 2010 - нет «сценариев» как таковой. Дайте мне знать, если это не достаточно ясно, и я постараюсь раскрыть больше. Astrogator
Astrogator - есть ли шанс, что вы могли бы поделиться своими сценариями? Я собираюсь начать очень похожий процесс. Спасибо! Erick T

Ваш Ответ

2   ответа
1

Мне также нужно было добавить CompatibleFrameworks. Я также пытался добавить CompatibleFrameworks, как это (что не работает)

dm.CompatibleFrameworks.Add(...);

Моим решением было установить:

dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";                 

После этого Манифест поколения был верным.

Будьте осторожны Если вы установите TargetFrameworkMoniker перед WriteManifest, у вас есть & lt; compatibilityFrameworks & gt; дважды, и файл вашего приложения поврежден. Вот мое решение для этого:

DeployManifest dm = ManifestReader.ReadManifest("DeployManifest", applicationFileName, false) as DeployManifest;
dm.ResolveFiles();
//doing stuff..
dm.UpdateFileInfo();
ManifestWriter.WriteManifest(dm, applicationFileName);
dm.TargetFrameworkMoniker = ".NETFramework,Version=v4.0";
ManifestWriter.WriteManifest(dm, applicationFileName);
8

Сам разобрался. Виновником являетсяManifestReader.ReadManifest( "DeployManifest", sPathMft, true ).

MSDN говорит [аргумент preserveStream]"specifies whether to preserve the input stream in the InputStream property of the resulting manifest object. Used by ManifestWriter to reconstitute input which is not represented in the object representation."

Формулировка в сторону, настройкаtrue не достаточно само по себе:dm.CompatibleFrameworks.Count все равно будет 0, но теперь добавлениеCompatibleFramework предметы будут иметь эффект!

Для кого-то еще в той же лодке, я делаю это раньшеdm.ResolveFiles( ):

if(  dm.CompatibleFrameworks.Count <= 0  )
{
    CompatibleFramework cf= new CompatibleFramework( );
    cf.Profile= "Client";       cf.Version= "4.0";      cf.SupportedRuntime=    "4.0.30319";
    dm.CompatibleFrameworks.Add( cf );              //  cf= new CompatibleFramework( );
    cf.Profile= "Full";     //  cf.Version= "4.0";      cf.SupportedRuntime=    "4.0.30319";
    dm.CompatibleFrameworks.Add( cf );              /// no need for separate object
}

@davidair, спасибо за ваше предложение! & # xA0; Согласен, хотя я предпочитаю работать с объектами API (вместо XML).
Другая альтернатива - позвонитьmage (напрямую или из файла .cmd), так как кажется, что мыразрешены перераспределить это.

Я также добавил следующую часть, которая не оказывает влияния на сам вопрос, но может быть весьма важной для любого, идущего по тому же пути (/client является корнем развертывания и может быть настроен):

dm.DeploymentUrl=   string.Format( "http://{0}/{1}/client/{1}.application",
                        Dns.GetHostName( ), Context.Parameters[ scTokVirtDir ] );
dm.UpdateMode=      UpdateMode.Background;
dm.UpdateUnit=      UpdateUnit.Weeks;
dm.UpdateInterval=  1;
dm.UpdateEnabled=   true;

Похожие вопросы