首页 > 解决方案 > 如何将入口点过程从“WinMain”更改为“main”或任何自定义函数?

问题描述

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

老实说,我很困惑。我相信有一种或多种方法可以将入口点过程更改为自定义过程,因为有一些示例,例如 MFC 没有直接WinMain功能,Qt 框架也有自定义入口点过程,它类似于控制台应用程序mainfunction 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;

我怎么做?

标签: c++windowsuser-interface

解决方案


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

ULONG CALLBACK ep(void* )

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

这个入口点的名称当然根本不起任何作用——它可以是任何有效的c/c++名称。找到/调用的入口点不是按名称而是按AddressOfEntryPoint偏移量IMAGE_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(用于控制台应用程序),WinMainCRTStartupwWinMainCRTStartup用于 gui 应用程序。

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

  • mainCRTStartup称呼main
  • wmainCRTStartup称呼wmain
  • WinMainCRTStartup称呼WinMain
  • wWinMainCRTStartup称呼wWinMain

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

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


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

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

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


也可以简单地mainWinMain

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;
}

推荐阅读