如何将入口点过程从“WinMain”更改为“main”或任何自定义函数?

Posted

技术标签:

【中文标题】如何将入口点过程从“WinMain”更改为“main”或任何自定义函数?【英文标题】:How can I change the entry point procedure from "WinMain" to "main" or any custom function? 【发布时间】:2020-08-30 15:39:54 【问题描述】:

我已经阅读了很多关于如何更改 WinMain 入口点过程的内容,有人说您可以从链接器更改入口点,而另一些人说您可以将 WinMain 放入 DLL (dllMain) 和以此类推。

老实说,我很困惑。我相信有一种或多种方法可以将入口点过程更改为自定义过程,因为有些例子像 MFC 没有直接的WinMain 函数,Qt 框架也有一个自定义入口点过程,它类似于控制台应用程序main 函数int main(int argc, char *argv[]),所以,有我所期望的方法。

我想要一种任何方式来替换/更改 Windows 上 GUI 应用程序的入口点过程,从传统的程序 WinMainint main(int argc, char *argv[]),如 Qt 甚至任何其他自定义函数,但它必须与(MS、GCC , Clang) 编译器。

///////////Windows main/////////////
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdParam, int cmdShow)


///////////Console main and Qt framework////////////
int main(int argc, char *argv[]) 
    

//////////MFC////////////
class CMyFrame : public CFrameWnd 
   public:
      CMyFrame() 
;

class CExample : public CWinApp 
   BOOL InitInstance() 
;
CExample theApp;

我该怎么做?

【问题讨论】:

使用mainWinMain 以外的东西会很棘手,因为这些是运行时库在完成初始化后调用的入口点。 MSVC 链接器确实允许您在命令行上指定入口点(请参阅here,但那样会绕过运行时库初始化代码并破坏您的应用程序。 我想要一种任何方式来替换/更改 Windows 上 GUI 应用程序的入口点过程 - 感觉?你想得到什么?这是干什么用的? @RbMm:我正在尝试创建一个 GUI 库,但我不想让库变得复杂,所以我想让入口点成为用户熟悉的东西,例如 int main(int argc, char *argv[]) 函数int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdParam, int cmdShow). @LionKing - 如果说真的,我根本不了解你。 mainWinMain 有什么不同?一些用户在这里是如何相关的,为什么 lib 完全实现入口点.. 但是如果你理解我的回答,你想要做的很容易 - /ENTRY: mainCRTStartup - 这很明显 在您的 GUI 库中包含一个 WinMain 函数,该函数执行任何操作,然后调用您未实现的名为 main 的函数。然后当库的用户创建一个 main 函数时,它就可以工作了。然后加载程序只需调用您的 WinMain 函数而无需任何特殊调味料。主要的是库的用户需要将他们的程序编译为 Windows 可执行文件而不是控制台程序,以便调用 WinMain。 【参考方案1】:

exe的入口点可以是任何带有签名的函数

ULONG CALLBACK ep(void* )

可能并使用ULONG CALLBACK ep() - 尽管在 x86 上返回后堆栈指针 (esp) 会出错,但这不会导致错误,因为 windows 只是调用 ExitThread 在入口返回后,如果它完全返回控制 - 通常它调用 ExitProcess 而不是返回。

这个入口点的名字当然没有任何作用——它可以是任何有效的c/c++名字。找到/调用的入口点不是按名称,而是按 AddressOfEntryPointIMAGE_OPTIONAL_HEADER 的偏移量

但是当我们构建 PE - 我们需要告诉链接器这个函数的名字,因为它可以设置 AddressOfEntryPoint,但是这个信息(函数的名字)只在构建过程中使用进程(不在运行时使用)

不同的链接器当然有不同的选项,link.exe 有选项/ENTRY。此选项是可选的,默认情况下,起始地址是 C 运行时库中的函数名。

如果 /ENTRY:MyEntry 明确声明 - 它按原样使用 - MyEntry 将用作入口点。如果没有设置/ENTRY 选项 - 使用默认值:

如果 /SUBSYSTEM:CONSOLE 设置 - 使用 mainCRTStartup 或者如果没有找到 wmainCRTStartup

如果 /SUBSYSTEM:WINDOWS 设置 - 使用 WinMainCRTStartup 或者如果没有找到 wWinMainCRTStartup

但在大多数情况下,c/c++ 开发人员使用 CRT 库。无论是与 CRT 一起使用静态链接还是动态链接 - 一些 lib 代码始终与您的 exe 静态链接,并且此代码包含您用作入口点的函数。对于 ms windows crt - 这是 mainCRTStartupwmainCRTStartup(对于控制台应用程序),WinMainCRTStartup@987654344 @ 用于 gui 应用程序。

在所有这 4 个函数中 - 按名称称为 硬编码 函数

mainCRTStartup 致电main wmainCRTStartup 致电wmain WinMainCRTStartup 致电 WinMain wWinMainCRTStartup 致电wWinMain

当然,被调用函数必须在您的代码或另一个 lib 代码中的某处实现。例如,如果您使用 MFC - 它自己实现 wWinMain 并以另一种方式调用您的代码(通过在您覆盖的对象上调用虚函数 - InitApplicationInitInstance)

如果回过头来质疑如何更改自定义入口点的名称 - 但是为了什么?你真的不需要改变名字。您只需要了解如何调用您的入口点。如果你明白这一点 - 你几乎可以做所有事情。


假设我们想使用 main 作为“入口点”。我把它用引号引起来,因为我们真的希望在 CRT 代码中有真正的入口点,并且我们希望 CRT 代码准确地调用 main 函数。

可能吗?简单地 ! 设置/ENTRY: mainCRTStartup 链接器选项。所以 mainCRTStartup 将是真正的入口点,它调用 main

另一个问题,我个人认为这是毫无意义的把戏,没有任何改变,也没有给予


也可以简单地从WinMain 调用main

typedef struct

    int newmode;
 _startupinfo;

 /* 
 * new mode flag -- when set, makes malloc() behave like new()
 */

EXTERN_C _CRTIMP int __cdecl _query_new_mode( );
EXTERN_C _CRTIMP int __cdecl _set_new_mode( _In_ int _NewMode);

EXTERN_C
_CRTIMP int __cdecl __getmainargs(__out int * _Argc, 
                              __deref_out_ecount(*_Argc) char *** _Argv,
                              __deref_out_opt char *** _Env, 
                              __in int _DoWildCard,
                              __in _startupinfo * _StartInfo);

int __cdecl main(__in int _Argc, __in_ecount_z(_Argc) char ** _Argv, ...);

int CALLBACK WinMain( _In_ HINSTANCE , _In_opt_ HINSTANCE , _In_ LPSTR , _In_ int  )

    int _Argc, r;
    char ** _Argv;
    char ** _Env;
    _startupinfo  _StartInfo  _query_new_mode( ) ;
    if (!(r = __getmainargs(&_Argc, &_Argv, &_Env, 0, &_StartInfo)))
    
        r = main(_Argc, _Argv, _Env);
        if (_Argv) free(_Argv);
    

    return r;

【讨论】:

评论不用于扩展讨论;这个对话是moved to chat。【参考方案2】:

它必须与(MS、GCC、Clang)编译器兼容

如何做到这一点取决于您的编译器。他们中的大多数都会有一些标志来选择您所针对的“子系统”(Windows 术语),甚至可以手动自定义入口点。

换句话说,没有这样做的标准方法,因为这超出了 C++ 标准的范围。

话虽如此,一些编译器提供了模拟其他编译器标志的方法。例如,Clang 可以模仿微软的。

【讨论】:

以上是关于如何将入口点过程从“WinMain”更改为“main”或任何自定义函数?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中,主函数是编程的入口点,我如何将其更改为其他函数?

WIN32 SDK API的基础问题(窗口显示)

cwindows应用程序的唯一入口点是

c++主函数如何调用"WinMain"函数

如何在 iOS 7 中将 UINavigationBar 高度的高度从 64 点更改为 44 点?

Windows程序执行过程