Использование ILMerge с библиотеками.NET 4
Две проблемы:
1) Базовая сборка.NET, не включенная в сборку ILMerged
У меня возникли проблемы с использованием ILMerge в моем пост-сборке после обновления с.NET 3.5/Visual Studio 2008 до.NET 4/Visual Studio 2010. У меня есть решение с несколькими проектами, целевая платформа которых установлена на ".NET Framework 4", Я использую следующую команду ILMerge для объединения отдельных библиотек проекта в одну библиотеку:
if not $(ConfigurationName) == Debug
if exist "C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe"
"C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe"
/lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319"
/lib:"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"
/keyfile:"$(SolutionDir)$(SolutionName).snk"
/targetplatform:v4
/out:"$(SolutionDir)bin\development\$(SolutionName).dll"
"$(SolutionDir)Connection\$(OutDir)Connection.dll"
...other project DLLs...
/xmldocs
Если я не буду указывать расположение каталога платформы.NET 4, я получаю сообщение об ошибке "Неразрешенная ссылка на сборку не разрешена: система" от ILMerge. Если я остановлюсь, указав расположение каталога MSTest, я получу ошибку "Неразрешенная ссылка на сборку не разрешена: Microsoft.VisualStudio.QualityTools.UnitTestFramework".
Приведенная выше команда ILMerge работает и создает DLL. Однако, когда я ссылаюсь на эту DLL в другом проекте.NET 4 C# и пытаюсь использовать код внутри нее, я получаю следующее предупреждение:
Первичная ссылка "MyILMergedDLL" не может быть разрешена, поскольку она имеет косвенную зависимость от сборки.NET Framework "mscorlib, Version=4.0, Culture= нейтральный, PublicKeyToken=b77a5c561934e089", версия которого имеет более высокую версию "4.0.65535.65535", чем версия "4.0.0.0" в текущей целевой структуре.
Если я тогда уберу /targetplatform:v4
флаг и попробуйте использовать MyILMergedDLL.dll, я получаю следующую ошибку:
Тип 'System.Xml.Serialization.IXmlSerializable' определяется в сборке, на которую нет ссылок. Необходимо добавить ссылку на сборку "System.Xml, версия =4.0.0.0, культура = нейтральная, PublicKeyToken=b77a5c561934e089".
Не похоже, что мне нужно это делать. Тот, кто использует мой API MyILMergedDLL.dll, не должен добавлять ссылки на библиотеки, на которые он ссылается. Как я могу обойти это?
2) TypeLoadException только при использовании объединенной сборки
Изменить: помимо этого, даже если я добавлю ссылку на System.Xml
в потребительском проекте, который использует MyILMergedDLL.dll, использование некоторого кода в MyILMergedDLL.dll дает следующее исключение:
System.TypeLoadException: не удалось загрузить тип 'System.Func`2' из сборки 'MyILMergedDLL, версия =1.0.1.1, культура = нейтральная, PublicKeyToken=...'.
Это код в моем потребительском проекте; линия, которая вызвала TypeLoadException
это второй:
var keys = new[] {"a", "b", "c"};
var row = new Row(keys);
Особый Row
конструктор, который бросает TypeLoadException
определяется в публичном классе в MyILMergedDLL
и когда я использую этот конструктор при обращении к отдельным библиотекам проекта, он работает нормально. Исключение составляет только когда я использую этот конструктор при обращении к слитой в DLL библиотеке. Я не знаю, что происходит.
Вот этот конструктор:
public Row(IEnumerable<string> keys) : base(keys) { }
И base
на который он ссылается, имеет такой код:
foreach (string key in keys.Where(
key => !string.IsNullOrEmpty(key)
))
{
_dic.Add(key, string.Empty);
}
6 ответов
Был очень недавний выпуск для решения проблем с x64. Свяжитесь с Майком Барнеттом напрямую, если у вас все еще есть проблемы (mbarnett в Microsoft Dot Com)
Добавление. Там что-то очень, очень неправильно в вашем /lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319"
вариант. В последнее время после выпуска.NET 4.5 возникали проблемы с программистами. Этот каталог не подходит для справочных сборок.NET 4.0. Его содержимое перезаписывается сборками 4.5, вы больше не можете использовать его для установки.NET 4.0. Полученная ошибка во время выполнения очень неловкая, программа больше не может найти определенные типы. Обычно бомбардировка по атрибуту [Extension], иногда по интерфейсу ICommand.
Эти типы и некоторые другие были перемещены из одной сборки в другую. Использование правильных эталонных сборок является жестким требованием. Вы должны использовать:
/lib:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"
Настройте в соответствии с вашей конкретной машиной и целевой версией фреймворка.
Вот "Строка посткомпоновки" для Visual Studio 2010 SP1 с использованием.NET 4.0. Я строю консоль.exe со всеми вложенными.dll файлами, включенными в него.
"$(SolutionDir)ILMerge\ILMerge.exe" /out:"$(SolutionDir)\deploy\$(TargetFileName)" "$(TargetDir)$(TargetFileName)" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
Основные советы:
- Обратите внимание на каталог "\deploy\": здесь заканчивается выходной файл.exe.
- Обратите внимание на каталог "ILMerge\". Я скопировал утилиту ILMerge в свой каталог решений (чтобы я мог распространять исходный код, не беспокоясь о документировании установки ILMerge).
Расширенные советы:
Если у вас есть проблемы с неработающим приложением, добавьте "echo" перед командой "Post Build". Затем откройте окно "Вывод" в Visual Studio (View..Output) и проверьте точную команду, сгенерированную Visual Studio. В моем конкретном случае точная команда была:
"T:\PhiEngine\CSharp\ILMerge\ILMerge.exe" /out:"T:\PhiEngine\CSharp\Server Side\deploy\NfServiceDataHod.History.exe" "T:\PhiEngine\CSharp\Server Side\NfServiceDataHod\bin\Debug\NfServiceDataHod.History.exe" "T:\PhiEngine\CSharp\Server Side\NfServiceDataHod\bin\Debug\*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
Обновить
Добавил это к моему шагу "Посткомпоновка", он заменяет все.exe + .dll файлы одним объединенным.exe. Он также сохраняет файл отладки.pdb без изменений:
rem Create a single .exe that combines the root .exe and all subassemblies.
"$(SolutionDir)ILMerge\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
rem Remove all subassemblies.
del *.dll
rem Remove all .pdb files (except the new, combined pdb we just created).
ren "$(TargetDir)$(TargetName).all.pdb" "$(TargetName).all.pdb.temp"
del *.pdb
ren "$(TargetDir)$(TargetName).all.pdb.temp" "$(TargetName).all.pdb"
rem Delete the original, non-combined .exe.
del "$(TargetDir)$(TargetName).exe"
rem Rename the combined .exe and .pdb to the original name we started with.
ren "$(TargetDir)$(TargetName).all.pdb" "$(TargetName).pdb"
ren "$(TargetDir)$(TargetName).all.exe" "$(TargetName).exe"
exit 0
Другие альтернативы:
- Объединение нескольких сборок в один EXE-файл для приложения WPF.
- .Net Reactor.
- SmartAssembly (довольно дорогая коммерческая альтернатива).
Вы также можете добавить файл конфигурации со следующим:
<?xml version ="1.0"?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<requiredRuntime safemode="true" imageVersion="v4.0.30319" version="v4.0.30319"/>
</startup>
</configuration>
Взято отсюда
Просто установите для ссылок PresentationCore и PresentationFramework значение "Copy Local = True" в окне свойств Visual Studio (после выбора ссылок в обозревателе решений). Это решит проблему без жесткого кодирования пути фреймворка. Я предпочитаю это решение, потому что путь отличается в зависимости от того, является ли сервер разработчика / сборки 64-битным или 32-битным и неизбежно изменится, когда будут выпущены новые версии.NET/VS.
Для тех, кто использует ILMerge из задач сообщества в.csproj:
<ILMerge InputAssemblies="@(MergeAssemblies)"
...
TargetPlatformVersion="v4"
TargetPlatformDirectory="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"
/>
У нас смешанный парк агентов сборки CI, поэтому мы используем переменную среды $(ProgramFiles) для указания правильного пути (папка drive + x86/x64), как это было рекомендовано MSBuild Team.