Возникла ошибка BadImageFormatException с узлом WcfSvcHost и IIS WCF
Создание библиотеки служб WCF в Visual Studio 2008 в Vista x64 проблематично при обращении к библиотеке x86. Служба, которая вызывает 32-битную DLL, должна иметь целевую платформу x86 для работы в 64-битной ОС. Когда вы делаете это, WcfSvcHost выдает исключение BadImageFormatException при попытке отладки службы. На MS connect есть сообщение об ошибке. Обходной путь, который я использовал, состоял в том, чтобы установить флаг WcfSvcHost как 32-битный.
Проблема с манифестом
Основная проблема, с которой я столкнулся, заключается в том, что эту стороннюю 32-битную DLL не удается загрузить с помощью определенных хостов WCF. Я получаю следующую ошибку, когда вызывается операция службы, использующая стороннюю DLL:
System.TypeInitializationException: инициализатор типа для '' вызвал исключение.
.ModuleLoadExceptionHandlerException: вложенное исключение произошло после основного исключения, которое вызвало сбой загрузки модуля C++.
System.BadImageFormatException: модуль должен содержать манифест сборки. (Исключение из HRESULT: 0x80131018)
NestedException:
Дескриптор недействителен. (Исключение из HRESULT: 0x80070006 (E_HANDLE))
Это исключение не возникает при запуске WcfSvcHost, оно вызывается при запуске операции службы, которая ссылается на 32-разрядную DLL. Что очень интересно, хостинг этого же сервиса с тем же app.config в консольном приложении не имеет исключений и работает отлично:
using (ServiceHost host = new ServiceHost (typeof (MsgBrokerService))) {
host.Open ();
Console.WriteLine ("running");
Console.ReadLine ();
Это исключение возникает сразу после:
"WcfSvcHost.exe" (управляемый): загружен "C:\Windows\WinSxS\x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.3053_ none_d08d7bba442a9b36\msvcm80.dll"
Опять же, консольное приложение не имеет исключения и загружает ту же DLL:
'ConsoleApp.vshost.exe' (управляемый): загружен 'C:\Windows\WinSxS\x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.3053_ none_d08d7bba442a9b36\msvcm80.dll'
См. Ответ службы поддержки продуктов Microsoft.
Обновление № 1: и консольное приложение, и хост-процесс WcfSvcHost.exe выполняются в одном сеансе и вошли в систему (я). Я скопировал WcfSvcHost.exe в каталог службы, запустил вручную и получил тот же результат. Я также проверил журнал событий Windows для дополнительной информации и использовал sxstrace, но ничего не было зарегистрировано.
Запустив Process Explorer, я убедился, что между двумя процессами одно и то же:
- Изображение: 32-разрядное
- Текущий каталог
- Пользователь /SID
- сессия
- Безопасность (группы запрещены, привилегии отключены)
Запустив Process Monitor и настроив символы, я вижу, что WcfSvcHost ищет следующий реестр и файлы, а консольный хост - нет. Process Monitor регистрирует много данных, и я не уверен, что я ищу:(.
HKLM \ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ \Microsoft\Fusion\PublisherPolicy\Default\policy.8.0.msvcm80__b03f5f7f11d50a3a C:\Windows\ Assembly\GAC_32\msvcm80\8.0.50727.3053__b03f5f7f11d50a3a C:\Windows\ 27.30: ff_50_f_50_f_50_f_50_50_50_50_f_3_50_f_3_50_50 как_байт_файлах:: C3_3: c: 80: 80 \ f50_50_50_50_50_f_50_50_50_50: \: c: 80: \ \ 803: C: Windows \ сборка \GAC\msvcm80\8.0.50727.3053__b03f5f7f11d50a3a
Обновление № 2. Это же исключение возникает, когда служба размещена в рабочей среде на IIS 6 / Windows Server 2003.
Обновление №3. Сторонней 32-разрядной сборкой.NET является API-интерфейс StreamBase:
- sbclient.dll (управляемый)
- monitor.netmodule (управляемый)
- dotnetapi.dll (неуправляемый)
- pthreads-vc8.dll (неуправляемый)
Обновление № 4: добавлены манифесты без успеха:
- Проверено, что dotnetapi.dll и pthreads-vc8.dll имеют RT_MANIFEST. Сборка sbclient.dll .NET не имела манифеста
- Удален sbclient.dll из GAC
- Зарегистрированный sbclient.dll для проверки пропуска
- Добавлен манифест через mt.exe как для sbclient.dll, так и для monitor.netmodule
- Был добавлен проверенный манифест и ожидаемые файлы были загружены во время тестирования (через Visual Studio - окно отладочных модулей)
- То же BadImageFormatException генерируется в BackgroundWorker.OnDoWork(), а стек вызовов показывает вызов dotnetapi.dll...DefaultDomain.Initalize().
Я проверил, что msvcm80.dll не имеет манифеста, я считаю, что это единственный загруженный файл, который не имеет манифеста:)
Интересная находка
Когда я загружаю monitor.netmodule в Reflector, он говорит:
'monitor.netmodule' не содержит манифеста сборки.
Несмотря на то, что он отображает ошибку, Reflector все еще может разобрать управляемый код.
7 ответов
Служба поддержки продуктов Майкрософт решила этот вопрос: это сделано специально. Неуправляемый код не загружается в AppDomain по умолчанию при использовании WcfSvcHost или хоста IIS WCF.
Чистый образ будет использовать CLR-версию библиотеки времени выполнения C. Однако CRT не поддается проверке, поэтому вы не можете использовать CRT при компиляции с /clr:safe. Для получения дополнительной информации см. C Run-Time Libraries.
Немного поздно, но вы также можете изменить настройку пула приложений "Включить 32-битные приложения" на true в расширенных настройках.
Это может показаться немного глупым; но убедитесь, что ваш сервис работает в правильном пуле приложений.
Не могли бы вы вручную добавить манифест в эту DLL, используя файл mt.exe?
Столкнулся с этим вопросом сам. Я нашел полезный пост. Как отмечалось в других постах, Microsoft заявила, что это сделано специально. В основном вам нужно:
Найдите вашу версию WcfSvcHost.exe. (для меня и Visual Studio 2017: C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE)
Запустите командную строку разработчика
Выполнить cmd:
copy C:\SourcePath\WcfSvcHost.exe C:\DestinationPath\WcfSvcHost32.exe
(Пункт назначения не имеет значения)CMD:
corflags /32BIT+ /Force WcfSvcHost32.exe
(может понадобиться перейти к DestinationPathВ Visual Studio откройте свойства проекта WCF> вкладка "Отладка"> "Запустить внешнюю программу":
C:\DestinationPath\WcfSvcHost32.exe
Также добавьте свои аргументы командной строки:
/service:MyWCFProjectName.dll /config:MyWCFProjectName.dll.config
Примечание: вам не нужно использовать
($ProjectDir)
ВотЗапустите приложение. Теперь вы можете запускать WcfServiceHost.exe отдельно.
При необходимости перейдите к решению> Задать стартовые проекты> Несколько стартовых проектов> Выберите проект Wcf и проект клиента.
Я не могу предоставить объяснение этой ошибки, только мое первоначальное подозрение, что существует разница в разрешениях между контекстом, в котором ваш код запускается как служба, и контекстом, в котором он запускается, когда вы помещаете его в консольное приложение. E_HANDLE HRESULT - моя подсказка. Предполагая, что вы запускаете консольное приложение как вошедший в систему пользователь, вы можете попытаться настроить службу так, чтобы она также запускалась от имени этого пользователя. Если он работает в этой конфигурации, вы можете попытаться сузить необходимый ресурс, недоступный в случае сбоя.
Я могу предложить обходной путь. Если есть какая-то странность рассматриваемой DLL, которая мешает ей работать в размещенной службе, вы можете использовать метод жертвенного процесса, названный так потому, что он обычно используется для изоляции DLL, которая часто выходит из строя. Вкратце, вы создаете прокси-программу, единственная цель которой - загружать и вызывать DLL от имени вашего основного процесса, используя именованные каналы или какой-либо другой метод IPC для передачи запросов и результатов. В случае сбоя DLL вы запускаете новый экземпляр прокси-программы. В вашем случае было бы дополнительное преимущество, что только программа-обертка должна быть 32-битной.