Как инициализировать среду выполнения C при использовании исполняемого файла Windows в качестве DLL

0

Я пытаюсь выполнить модульное тестирование компонентов и функций в исполняемом файле Windows. Я хочу запустить фактический скомпилированный код, не создавая тестовые примеры в исполняемый файл *. Инструменты Microsoft отлично подходят для экспорта классов и функций из исполняемого файла, и я могу ссылаться на него, как на DLL. Уловка - поскольку точка входа динамически загружаемого исполняемого файла не вызывается и нет DllMain (это не технически DLL), среда выполнения C не инициализируется в "DLL", и статика не инициализируется * *.

Есть ли способ вызвать CRT_INIT в контексте динамически загруженного.EXE и заставить все работать или это смешно?

* Если мне не хватает чего-то явно очевидного, не стесняйтесь указывать мне в правильном направлении.

** Это, по-моему, самая большая проблема.

  • 0
    То, что в коде нет DLLMain, не означает, что нет DLLMain. Начиная с VS2003, по умолчанию предоставляется DLLMain, если у вас его нет. Проверьте использование зависит - вы можете обнаружить, что по умолчанию есть DLLMain. В этом случае вы можете использовать LoadLibrary и GetAddress для тестирования.
Теги:
dll
unit-testing
testing

2 ответа

2
Лучший ответ

Отказ от ответственности - я поддерживаю свой первоначальный ответ. Это не должно работать. Лучшей практикой является компиляция кода ядра как LIB или DLL, а затем для связывания этой библиотеки как с кодом UnitTest, так и с основным программным кодом.

Но поскольку OP продемонстрировал мне, что он нашел способ сделать EXE-работу DLL, я чувствую себя вынужденным довести его до конца...

Хитрость заключается в вызове _CRT_INIT. Но поскольку для вызова дескриптора HINSTANCE нет DllMain, вы должны вызвать GetModuleHandle напрямую, чтобы получить его. И затем я держу таблицу, чтобы отслеживать, какие потоки вызвали CRT_INIT.

Я взял пример кода, который он предоставил, и добавил эту структуру данных в код AppToTest.exe:

bool g_isProcessInitialized = false;
std::map<DWORD, bool>* g_threadmap;

extern "C" BOOL __stdcall _CRT_INIT(HINSTANCE, DWORD, void*);

void ThreadInit()
{
    std::map<DWORD, bool>& themap = *g_threadmap;

    DWORD dwCurrentThreadID = ::GetThreadId(GetCurrentThread());
    if (themap[dwCurrentThreadID] == false)
    {
        _CRT_INIT(GetModuleHandle("AppToTest.exe"), DLL_THREAD_ATTACH, NULL);
        themap[dwCurrentThreadID] = true;
    }
}

void ProcessInit()
{
    if (g_isProcessInitialized == false)
    {
        _CRT_INIT(GetModuleHandle("AppToTest.exe"), DLL_PROCESS_ATTACH, NULL);
        _CRT_INIT(GetModuleHandle("AppToTest.exe"), DLL_THREAD_ATTACH, NULL);

        g_isProcessInitialized = true;

        g_threadmap = new std::map<DWORD, bool>();
        std::map<DWORD, bool>& themap = *g_threadmap;
        themap[GetThreadId(GetCurrentThread())] = true;
    }
}

void APPAPI InitializeCRT()
{
    ProcessInit();
    ThreadInit();
}

И затем в TestApp.exe я изменил "main", чтобы вызвать InitializeCRT раньше:

int main(int argc, char* argv[])
{
    InitializeCRT();

И это похоже на работу - даже после того, как я расколол вызов методам, обозначенным проблематичным. Если последующие потоки создаются, то, возможно, необходимо будет инициировать InitializeCRT этими потоками. (Это может работать в любом случае...)

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

  • 0
    Индексирование карты по идентификатору потока, вероятно, не лучшая идея в мире;) Если два потока одновременно индексируют запись, которая не существует, они оба изменят внутреннюю структуру карты, полагая, что больше ничего не делает одновременно. Конечно, редко, но TLS работает лучше для такого рода вещей и избегает любых блокировок, которые потребуются для обеспечения поточной безопасности карты.
1

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

Вам лучше скомпилировать все, кроме WinMain, как LIB или DLL. Затем свяжите оба модуля UnitTest.EXE и YourProgram.EXE (каждый из которых предоставляет реализацию основного или WinMain) в эту библиотеку.

  • 0
    Кроме того, что он работает ( в основном). У меня есть классы / функции, которые я хочу экспортировать, и я связываюсь с ними во время сборки и динамически связываюсь с ними во время выполнения. Классы / методы / функции, которые не зависят от статической инициализации, работают просто отлично. Это только те, которые делают это проблематично.
  • 0
    @ user3422499 - Я подозреваю, что вы на самом деле статически связываете один и тот же код в обоих проектах. Не стесняйтесь опубликовать минимальный рабочий пример решения Visual Studio, демонстрирующий, что один EXE-файл динамически связывается с кодом из другого EXE-файла. Я всегда хочу быть образованным. Тем не менее, я вполне уверен, что вы не можете делать то, что вы описываете.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню