在Windows中以编程方式调用"main"函数

Dmi*_*ich 12 c++ program-entry-point systems-programming portable-executable entry-point

我有第三方控制台应用程序.我需要从我的应用程序运行它,但我不能将它作为一个单独的进程运行(因为我需要使用它的依赖项:手动填充导入表,设置挂钩等).所以我可能应该main手动调用此可执行文件的功能.这是我试图这样做的方式:

  1. 使用加载此EXE auto hMod = LoadLibrary("console_app.exe")
  2. 手动填写此exe的导入表
  3. 获取此EXE的入口点并调用它

我坚持到最后一步.

以下是我试图调用入口点的方法:

void runMain(HINSTANCE hInst)
{
    typedef BOOL(WINAPI *PfnMain)(int, char*[]);

    auto imageNtHeaders = ImageNtHeader(hInst);
    auto pfnMain = (PfnMain)(DWORD_PTR)(imageNtHeaders->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)hInst);

    char* args[] = { R"(<console_app_path>)", R"(arg1)", R"(arg2)" };
    pfnMain(3, args);
}
Run Code Online (Sandbox Code Playgroud)

有用.但它就好像没有争论一样.

我哪里错了?如何我的进程中使用参数运行可执行文件?谢谢.

更新:

我已经调查过我的特定第三方exe如何获取cmd参数并发现:

  1. 它根本不导入GetCommandLine,也不调用它
  2. 通过和之后可以通过call _initterm电话argcargv参数进行调查(见下图) cs:argccs:argv在此输入图像描述 在此输入图像描述
  3. 我传递给我的主控制台应用程序的CMD参数也会转移到子EXE.

您能解释一下,_initterm实际存在的CMD参数究竟存在了什么?

MSa*_*ers 11

你正在调用应用程序的入口点,而不是int main(int, char**).现在您可能已经读过C++程序的入口点,int main(int, char**)但这只是一个C++透视图.

Win32的观点是不同的; 入口点是int (*)(void);.Visual Studio链接器查找int mainCRTStartup(void);并使用它,除非您指定另一个入口点/ENTRY.的默认实现mainCRTStartup调用GetCommandLine()以填补argv[]之前调用main(argc,argv).还有其他一些mainCRTStartup你可能想要发生的事情:运行全局ctors,初始化CRT状态,......

当然,这是假设其他程序是使用Visual C++编译的,但无论编写什么语言,它都必须调用GetCommandLine.

现在,对于您的问题,这是一个有趣的观察:GetCommandLine()返回一个可写指针.您可以覆盖现有的命令行.当然,如果控制导入表,你决定了什么GetCommandLine 意思.(请记住,像往常一样有A和W变体).

一个警告:MSVCRT不是设计为初始化两次,既不是静态版本也不是DLL版本.所以实际上你不能使用它,这会伤害你.

[edit] 您的更新显示了对此的呼叫_initterm.这是一个MSVCRT功能,正如我已经暗示过的那样.特别,

/***
*crtexe.c - Initialization for console EXE using CRT DLL
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
...
/*
 * routine in DLL to do initialization (in this case, C++ constructors)
 */
extern int __cdecl _initterm_e(_PIFV *, _PIFV *);
extern void __cdecl _initterm(_PVFV *, _PVFV *);
Run Code Online (Sandbox Code Playgroud)

MSVCRT DLL GetCommandLine()代表EXE 调用.