Ошибка блокировки загрузчика с помощью управляемого c ++ dll, статически связанного с нативной библиотекой c ++ - PullRequest
4 голосов
/ 17 августа 2011

У меня есть управляемая c ++ dll с несколькими управляемыми классами, которые, в свою очередь, вызывают собственный код c ++ в библиотеке, которую я статически связал с dll.Тем не менее, если я пытаюсь запустить RegAsm.exe на dll, инструмент правильно сообщает «нет типов, которые мы зарегистрировали», но тогда зависает .Я очень уверен, что это проблема loader lock , и моя dll зависает, когда RegAsm пытается загрузить его.Я использую Visual Studio 2008, экспресс-издание.

Что меня озадачивает, что все отлично работает при помещении нативного кода в dll, но не при статической привязке его из библиотеки.Я знаю, что этот пост похож на этот вопрос , но у меня нет DllMain в моей dll, нет риска того, что я буду запускать код MSIL из DllMain.Кроме того, следование советам установки / clr для отдельных файлов не помогло.

Компиляция dll с / NOENTRY устраняет проблему блокировки, но приводит к разрыву приложения с исключением Type initializer for <Module> threw exception и, по-видимому, рекомендуется только с.NET 2003.

Я подозреваю, что инициализация статических членов может быть возможным виновником, но почему это будет скомпилировано в MSIL в моей статической библиотеке, мне не под силу.

Простоуточнить: хотя мне не нужно запускать RegAsm.exe на dll, я использую его для проверки проблемы блокировки загрузчика.На самом деле я использую dll в ассемблере ac #, который реализует несколько COM-видимых классов - поэтому мне нужно сделать COM-регистрацию для этого.В конце C # IDE вылетает при регистрации взаимодействия COM, сообщая об ошибке времени выполнения R6033 c ++: Попытка использовать код MSIL из этой сборки во время инициализации собственного кода.Это указывает на ошибку в вашем приложении.Скорее всего, это результат вызова MSIL-скомпилированной (/ clr) функции из собственного конструктора или из DllMain.

Решена проблема, но мало что неясно, и мне любопытно:

Я заметил, что две статические переменные были добавлены в заголовочный файл в статически связанной библиотеке в то время, когда вещи перестали работать, что выглядело так:

// The whole header is forced to compile as native 
#pragma managed(push, off)
....
static const std::locale commaSeparator(std::locale::classic(), 
                                        new DecimalSeparator<char>(','));;
....
#pragma managed(pop)

Перемещение инициализации в файл .cpp (и изменение static на extern) исправляет блокировку загрузчика.Может ли кто-нибудь указать, почему инициализатор вообще скомпилируется в MSIL?

До исправления, если я только # включил заголовочный файл из управляемого dll, все работало нормально.Но если бы я включил заголовок И также связанный с библиотекой, вещи не работали.Поскольку библиотека также использует заголовок внутри, я в итоге получил два экземпляра статической переменной?В любом случае, почему жалоба на запуск кода MSIL?

Хотя сейчас все работает, любая информация будет приветствоваться.

Ответы [ 2 ]

1 голос
/ 22 августа 2011

Следующая страница (особенно Инициализация статических объектов и Реализация в заголовках разделов) имеет следующее выражение:

Поскольку один и тот же заголовок может быть включен как файлами CPP с включенным и отключенным параметром / clr, так и #include может быть заключен в неуправляемый блок #pragma, возможно иметь как MSIL, так и собственные версии функций, которые предоставляют реализациив заголовках.

и

В Visual C ++ 2005 для удобства пользователей, имеющих дело с блокировкой загрузчика, компоновщик будет выбирать собственную реализацию вместо управляемой при представлении си то и другое.... Однако в этом выпуске есть два исключения из этого правила из-за двух нерешенных проблем с компилятором:
...
- вызов встроенной функции осуществляется через глобальный статический указатель на функцию.Этот сценарий особенно примечателен тем, что виртуальные функции вызываются через глобальные указатели на функции.

При помещении статической переменной непосредственно в управляемую dll (или, при этом, при компиляции с \ clr) блокировка загрузчикаизбегается, поскольку в DllMain происходит только инициализация собственных статических переменных.Однако любая статическая переменная, скомпилированная как нативная, блокируется при выполнении кода MSIL.В нашем случае MSIL генерируется для части STL, которая используется конструктором статического объекта, из-за неожиданного поведения компоновщика при представлении как с собственной реализацией, так и с реализацией MSIL.

Решение может быть следующим:

  • Скомпилируйте все с помощью /clr (для нас это невозможно, поскольку нам нужно использовать статическую библиотеку)
  • Убедитесь, что всем # включенным сторонним заголовкам (STL) предшествует #pragma unmanaged (слишком сложно)
  • Удалить инициализацию статической переменной из заголовков (через внешнее связывание)

    // Solved by replacing initialization in header file
    static const std::locale commaSeparator(...);
    // with 
    extern const std::locale commaSeparator;
    // and doing initialization in a cpp file
    
1 голос
/ 18 августа 2011

Compiiler упаковывает пользовательский DllMain со своим собственным кодом, который используется для инициализации библиотеки времени выполнения и глобальных переменных (чтобы пользовательский DllMain мог работать с уже созданными глобальными объектами и мог использовать стандартную библиотеку).Даже если у вас нет DllMain, он все равно будет сгенерирован компилятором.С / NOENTRY вы даете указание компоновщику игнорировать его.

Таким образом, выполнение чего-либо в конструкторе статической переменной фактически аналогично выполнению этого в DllMain, и, похоже, что так оно и есть.

...