Почему нативное приложение c ++, которое импортирует Mixed Native / CLR lib / dll, не вызывает ctor / dtor для внешних переменных в Mixed lib / dll - PullRequest
0 голосов
/ 25 октября 2018

Запись (далее по тексту: регистратор) всесторонняя функциональность регистратора / диагностики / профилирования производительности / отладчика с функцией обхода Native stack / обходчика управляемого стека среди множества других функций, а библиотека должна быть разделена между различными модулями, живущими в Native или Managed heap(написано в asm / нативном c ++ / управляемом c ++ /. Net / ...).До сих пор все работало нормально, когда это приложение для ведения журнала и импорта / реализации / вызова компилировалось в собственный или управляемый код.Но если я скомпилирую библиотеку логгера как Managed с / CLR и использую ее в родном C ++ проекте W / O / CLR, классы, экспортированные из библиотеки логгера с помощью 'extern', не вызывают Constructor или Destructor внутри logger dll во время инициализации dll.На самом деле конструкторы / деструкторы никогда не вызываются в библиотеке логгеров (замечено, что даже конструкторы расширенных классов не вызываются), существует только пустая оболочка класса, наполовину инициализированная.

Чтобы быть более понятным в библиотеке логгеров: нативная часть реализует полныйфункциональность библиотеки логгеров.В то время как управляемая часть на самом деле является «простой» оболочкой для нативного кода, которую мне нужно полностью реализовать в одном и том же dll для удобства переносимости и обслуживания.Библиотека логгеров предназначена для замены 10-летней аналогичной библиотеки, которая имеет только 20% функциональных возможностей этой новой библиотеки логгеров.

Теперь я не впервые сталкиваюсь с подобной или подобной проблемой, и в прошлых решениях были либоразделить на чистый нативный и чистый управляемый код, и один должен иметь обертку для другого (два поддерживаемых проекта, переносимость отсутствует).Или скомпилировать обе версии библиотеки: одну для собственного приложения, а другую для управляемого приложения.Теперь в данном случае это не решения, а ограничения, так как мне нужно, чтобы код работал по одному и тому же каналу процесса, будь то нативная dll, нативное приложение, управляемая dll, управляемое приложение, ..., мне нужно иметь все в одном для простотыСаке.

Также я мог бы переписать внешние классы, чтобы не использовать конструктор / деструктор и написать какое-то упругое имитационное моделирование, но, потратив на это целый день, я хотел бы узнать причину этой проблемы, и есть лидругие решения, которые более элегантны или если я где-то ошибаюсь: например, использование #pragma managed (push, off) вызывает эти симптомы или подобное?

Кто-нибудь знает причину этого?Любые идеи приветствуются.

1 Ответ

0 голосов
/ 28 октября 2018

Есть два дьявола, заставляющие код вести себя неправильно.

Первый дьявол: предупреждающее сообщение было первым признаком того, что экспортированные инициализаторы не будут запущены до того, как MANAGED CODE будет впервые выполнен (угадайте, что при компиляции управляемого ничего не инициализируется, когда DLLзагружен).Пример предупреждения, когда я тестировал порядок загрузки смешанного кода:

1>CBla.cpp(8): warning C4835: '_bla1_' : the initializer for exported data will not be run until managed code is first executed in the host assembly.

, который я игнорировал, так как все работало.

Второй дьявол: некоторые из стандартных кодов c / c ++ могут 'быть скомпилировано в управляемый.Хотя у меня сложилось впечатление, что это не должно быть скомпилировано как управляемое.Но я начал получать предупреждающие сообщения в функциях, использующих переменные аргументы, и начал использовать везде прагмы нативного кода для компиляции нативного кода в нативный !!!

#pragma managed(push, off)
// native code
#pragma managed(pop)

Теперь это скомпилировано нормально, но в этом случае все "нативные"классы фактически жили только как нативные, никаких вызовов к управляемому коду не было - управляемый VFTABLE никогда не создавался.Управляемые функции имеют разные VFTABLE, пока они не будут загружены, каждая функция переписывается для загрузки CLR перед выполнением фактической управляемой функции.О чем вы можете узнать только с помощью IDA disasembler или аналогичных утилит ... но также кратко объясните внизу следующей страницы: https://msdn.microsoft.com/en-us/library/ms173266.aspx?f=255&MSPPError=-2147217396 "Инициализация смешанных сборок".Там никогда не вызывали инициализатор для внешних нативных классов, которые статически хранились в LIB / DLL, которые хранились в памяти (пустая оболочка).Таким образом, чистое нативное приложение, вызывающее смешанный код LIB / DLL, который никогда не использует / запускает код CLR, никогда не вызовет Constructor / Destructor в нативных скомпилированных классах.

РЕШЕНИЕ: Единственный способ преодолеть это ограничение -поместите некоторую управляемую функцию во все это, чтобы заставить CLR загружаться, и в этом случае вы заметите одно исключение во время процесса отладки:

First-chance exception at 0x7620c41f in ManagedNativeNatTest.exe: 0x04242420: CLRDBG_NOTIFICATION_EXCEPTION_CODE.

Это актуальный момент, когда код CLR получает уведомление об инициализации и начинает загрузкуэкспортированные собственные классы для LIB / DLL.Я смог вызвать это, поместив пустую функцию, принадлежащую нативному классу, в управляемый код:

...
#pragma managed(push, off)
...
#pragma managed(pop)

void CBla1::ManagedCall()
{ }

#pragma managed(push, off)
...

И вызов этой функции привел к выполнению загрузчика CLR в ответ на инициализацию моих внешних переменных.

Почемуэто сделано так, я не уверен, может быть, потому что CLR никогда ничего не загружает, пока не будет использован.Интересно, будет ли это в том же случае, если я скомпилирую CLR в нативный код с использованием NGen, но это может быть еще одно приключение.

На этом мой ответ завершается, почему extern vars не получают вызовы Constructor / Destructor в смешанных LIB / DLL.

...