Я пытаюсь выполнить модульное тестирование компонентов и функций в исполняемом файле Windows. Я хочу запустить фактический скомпилированный код, не создавая тестовые примеры в исполняемый файл *. Инструменты Microsoft отлично подходят для экспорта классов и функций из исполняемого файла, и я могу ссылаться на него, как на DLL. Уловка - поскольку точка входа динамически загружаемого исполняемого файла не вызывается и нет DllMain (это не технически DLL), среда выполнения C не инициализируется в "DLL", и статика не инициализируется * *.
Есть ли способ вызвать CRT_INIT в контексте динамически загруженного.EXE и заставить все работать или это смешно?
* Если мне не хватает чего-то явно очевидного, не стесняйтесь указывать мне в правильном направлении.
** Это, по-моему, самая большая проблема.
Отказ от ответственности - я поддерживаю свой первоначальный ответ. Это не должно работать. Лучшей практикой является компиляция кода ядра как 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. Не следует так делать...
Это не будет работать. Вы не можете динамически загружать EXE, как если бы это была DLL. Существуют некоторые ограниченные сценарии, связанные с загрузкой ресурсов из EXE, но нет экспорта функций.
Вам лучше скомпилировать все, кроме WinMain, как LIB или DLL. Затем свяжите оба модуля UnitTest.EXE и YourProgram.EXE (каждый из которых предоставляет реализацию основного или WinMain) в эту библиотеку.