Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

移除对 SDL2main 库的链接 #53

Merged
merged 1 commit into from
Feb 23, 2020

Conversation

pzhlkj6612
Copy link
Member

@pzhlkj6612 pzhlkj6612 commented Jan 30, 2020

在使用 MSVC v141 及以上版本时,链接报错:

SDL2main.lib(SDL_windows_main.obj) : error LNK2005: _main already defined in main.obj
MSVCRTD.lib(initializers.obj) : warning LNK4098: defaultlib 'msvcrt.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
LINK : warning LNK4217: symbol '_fprintf' defined in 'mp3Editor.obj' is imported by 'SDL2main.lib(SDL_windows_main.obj)' in function '_ShowError'
SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol _SDL_main referenced in function _main
SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
debug\Beslyric-for-X.exe : fatal error LNK1120: 2 unresolved externals

分析后得出:Beslyric-for-X (以下简称 B4X )使用SDL_Init(Uint32 flags)方法初始化 SDL ,同时也不在入口int main(int argc, char* argv[])初始化 SDL ,不需要SDL_main的相关功能。


如果 B4X 引入SDL_main,简略地说,有三件事情要发生:

  • 检查宏SDL_MAIN_HANDLED是否存在,如果没有,就定义宏SDL_MAIN_NEEDED或宏SDL_MAIN_AVAILABLE(根据平台类型);
  • 如果宏SDL_MAIN_NEEDED或宏SDL_MAIN_AVAILABLE存在,则使用宏定义#define main SDL_main将 B4X 的入口重命名为int SDL_main(int argc, char *argv[])
  • 程序启动时,将首先调用 SDL 的int main(int argc, char* argv[]),待 SDL 初始化后,再调用int SDL_main(int argc, char* argv[])启动 B4X 。

源码:

  1. #define main SDL_main
    https://github.com/spurious/SDL-mirror/blob/release-2.0.3/include/SDL_main.h#L94-L96
  2. SDL 的int main(int argc, char* argv[])的原型:
    https://github.com/spurious/SDL-mirror/blob/release-2.0.3/src/main/dummy/SDL_dummy_main.c#L11-L15
  3. 用于 MSVC 的 SDL 的int main(int argc, char* argv[])
    https://github.com/spurious/SDL-mirror/blob/release-2.0.3/src/main/windows/SDL_windows_main.c#L131-L147

参考:


本 PR 在 Ubuntu 18.04 、 Windows 7 / 10 、 macOS 10.14 / 10.15 测试通过。

@pzhlkj6612 pzhlkj6612 changed the title Remove SDL2main library 移除对 SDL2main 库的链接 Feb 18, 2020
On MSVC v141+:
```
SDL2main.lib(SDL_windows_main.obj) : error LNK2005: _main already defined in main.obj
MSVCRTD.lib(initializers.obj) : warning LNK4098: defaultlib 'msvcrt.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
LINK : warning LNK4217: symbol '_fprintf' defined in 'mp3Editor.obj' is imported by 'SDL2main.lib(SDL_windows_main.obj)' in function '_ShowError'
SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol _SDL_main referenced in function _main
SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
debug\Beslyric-for-X.exe : fatal error LNK1120: 2 unresolved externals
```
@pzhlkj6612 pzhlkj6612 merged commit ef8bd2f into BesLyric-for-X:master Feb 23, 2020
@pzhlkj6612 pzhlkj6612 deleted the no_SDL2main branch February 23, 2020 02:12
@pzhlkj6612 pzhlkj6612 added this to the Next Release milestone Feb 24, 2020
@BensonLaur BensonLaur mentioned this pull request Feb 29, 2020
@pzhlkj6612 pzhlkj6612 mentioned this pull request Dec 6, 2020
@pzhlkj6612 pzhlkj6612 mentioned this pull request Jan 19, 2021
@pzhlkj6612
Copy link
Member Author

对于 Windows 上的 Qt 开发,这个 PR 所做的修改并不完整,做出的解释也是有问题的,所以在此重新说明。

另外, SDL 2.0.3 过于陈旧,我将参考 2.0.14 (此时最新的发行版)的代码。

SDL2main 做了什么?

在某些平台上,它将用户代码中的 main() 重命名为 SDL_main() ,并提供了平台相关的程序入口。这些入口将会进行命令行参数的处理、 SDL 本身的初始化、调用 SDL_main() (也就是用户代码中的 main() )、资源的释放等操作。

文档写了什么?

It should be noted that on some operating systems, SDL_Init() will fail if SDL_main() has not been defined as the entry point for the program. Calling SDL_SetMainReady() prior to SDL_Init() will circumvent this failure condition, however, users should be careful when calling SDL_SetMainReady() as improper initialization may cause crashes and hard to diagnose problems.

from: CategoryInit - SDL Wiki'

SDL_Init() 在何处失败?

SDL_MainIsReady 的值是什么?

SDL_SetMainReady() 做了什么?

某些操作系统(平台)是指?

它们是 __WINRT____IPHONEOS____ANDROID____NACL__ 。在这些平台上,如果 SDL_MAIN_HANDLED 未定义, SDL 将会定义 SDL_MAIN_NEEDED ,使 SDL_MainIsReady 的值为 SDL_FALSE

在这些平台上, SDL 需要进行必要的初始化。贸然调用 SDL_SetMainReady() 将绕过 int SDL_InitSubSystem(Uint32 flags) 中的检查,从而引发问题。

特殊的 __WIN32__

对于 __WIN32__

On Windows SDL provides WinMain(), which parses the command line and passes the arguments to your main function.
If you provide your own WinMain(), you may define SDL_MAIN_HANDLED

from: https://github.com/spurious/SDL-mirror/blob/release-2.0.14/include/SDL_main.h#L33-L41

如果 SDL_MAIN_AVAILABLE 已定义,用户代码中的 main() 将被 SDL 提供的入口 console_ansi_main() (将被重命名为 main() )、 console_wmain() (将被重命名为 wmain() )或 WinMain() 调用。

值得注意的是,这三个入口函数实际上都是调用的同一个函数 main_getcmdline() ,而在这个函数中,除了 SDL_SetMainReady() ,没有其它与 SDL 初始化有关的代码。同时,由于 SDL_MAIN_NEEDED__WIN32__ 被定义时是未定义的, SDL_MainIsReady 的值将被初始化为 SDL_TRUE ,这样,即使不调用 SDL_SetMainReady()SDL_Init() 也能够正常工作。

其他的操作系统呢?

显然,在其他的操作系统上(例如 Linux 和 macOS ),应该直接使用 SDL_Init()SDL_InitSubSystem() 进行 SDL 的初始化。

什么是 WinMain()

为何在 Windows 上的 Qt 程序中也能直接使用 main()

Qt 定义了 WinMain() 并在其中调用用户代码的 main()

/src/winmain/qtmain_win.cpp :

可以看出,在 Windows 上, Qt 的 WinMain() 与 SDL 的 main_getcmdline() 几乎做了同样的事情(除了 main_getcmdline()SDL_MainIsReady 的调用),即获取命令行参数、带参数调用 main() 和释放内存。

不过要注意,对于 MinGW ( win32-g++ ), Qt 有额外的处理。

定义 QT_NEEDS_QMAIN

main() 将会被重命名为 qmain() 并被 Qt 的 WinMain() 调用:

main() 被重命名并不稀奇,但是 SDL 也会做这件事,不加处理就会导致宏的重定义。

我将选择的解决方法

对于 Windows 上的 Qt 开发:

  • 不链接 SDL2main
  • 直接使用 SDL_Init()SDL_InitSubSystem() 进行 SDL 的初始化;
  • 在包含 SDL.h 之前定义宏 SDL_MAIN_HANDLED ,避免 Qt 与 SDL 在 MinGW 上对 main() 的重命名发生冲突。

参考资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant