能力所限,本讨论仅局限于MSVC的cl编译器和MinGW的gcc编译器。
- 第一部分:不涉及Qt(理清链接子系统和入口函数)
- 第二部分:Qt的链接子系统和入口函数(与第一部分完全对应上)
-
第三部分:QtTest模块出现控制台的原因与方案
- 第四部分:Graeme Gill 给出的很有意思的代码。
再探 链接子系统
在
浅谈Console与Windows子系统
一文中我们简单讨论了一个Windows系统下的 Console 和 Windows 两个链接子系统,但是描述可能有些乱。这儿换种方式整理一下:
考虑一个简单的程序代码
代码中定义两个入口函数:main和WinMain(不要觉得两个同时出现很奇怪),下面测试时
-
源码3种情况
:只有main、只有WinMain、二者同时存在
-
链接子系统3中情况
:不指定子系统、指定windows子系统、指定console子系统
-
编译器2种
:msvc的cl、mingw的gcc
#include <windows.h>
int main()
{
MessageBoxW (NULL, L"Hello World from main!", L"hello", MB_OK | MB_ICONINFORMATION);
return 0;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, int nShowCmd)
{
MessageBoxW (NULL, L"Hello World from WinMain!", L"hello", MB_OK | MB_ICONINFORMATION);
return 0;
}
不指定链接子系统
如果我们分别用MSVC的编译器cl 和 MinGW的编译器gcc (在不指定链接子系统的情况下)分别编译
cl /EHsc hello.c user32.lib
gcc hello.c -o hello
会有什么效果呢:
源码入口函数
|
编译器
|
默认链接子系统
|
默认入口函数
|
只有main
|
gcc
|
console
|
|
cl
|
console
|
|
只有WinMain
|
gcc
|
console
|
|
cl
|
windows
|
|
WinMain、main并存
|
gcc
|
console
|
main,(无法选择WinMain入口,除非你去掉main入口)
|
cl
|
console
|
main,,但可以通过
/entry:WinMainCRTStartup 选择WinMain入口
|
可以看到:
- 只有一种情况下不是控制台程序(即不弹黑色的cmd窗口)
- MSVC下可以选择根据需要选择入口函数
- MinGW下只要main存在,永远不会使用WinMain
在Qt中,如果是控制台程序(CONFIG+=console),程序只有一个入口,也就是你写的main函数;如果是GUI程序,则处于双入口并存的局面(第二部分对此有详细解释)。
注意Qt的处理方式:在MinGW下,双入口时它将main改成了qMain,如果你在用MinGW,如果你有兴趣可以自己做如下实验:
- 不要启用CONFIG+=console,不用使用QtTest 模块
- 将你的main函数改成qMain
- 然后和平时一样编译,运行
指定windows子系统
更进一步:如果我们制定链接子系统呢,比如,指定windows子系统(注意此处的选项,我们在Qt部分的CONFIG+=windows对应的文件中会再次看到它)
cl /EHsc hello.c /link /subsystem:windows user32.lib
gcc hello.c -o hello -Wl,-subsystem,windows
源码入口函数
|
编译器
|
需要指定入口函数
|
只有main
|
gcc
|
不需要
|
cl
|
必须指定 /entry:mainCRTStartup
|
只有WinMain
|
gcc
|
不需要
|
cl
|
不需要
|
WinMain、main并存
|
gcc
|
不需要(始终是main入口)
|
cl
|
默认是WinMain,可以通过
/entry:mainCRTStartup 选择main入口
|
指定console子系统
为了完整起见,看一下指定console子系统的情况(注意此处的选项,我们在Qt部分的CONFIG+=console对应的文件中会再次看到它)
cl /EHsc hello.c /link /subsystem:console user32.lib
gcc hello.c -o hello -Wl,-subsystem,console
结果:
源码入口函数
|
编译器
|
需要指定入口函数
|
只有main
|
gcc
|
不需要
|
cl
|
不需要
|
只有WinMain
|
gcc
|
不需要
|
cl
|
必须指定 /entry:WinMainCRTStartup
|
WinMain、main并存
|
gcc
|
不需要(始终是main入口)
|
cl
|
默认是main,可以通过
/entry:WinMainCRTStartup 选择WinMain入口
|
入口函数
看前面的3个表,入口函数应该会让你眼花缭乱,但,其实,很简单...
MinGW
MSVC
对 MSVC 系列的编译器,指定链接子系统比如 /subsystem:console,链接器就会寻找main函数,并选择mainCRTStartup函数;对windows子系统,情况类似。
当我们程序的入口函数是 WinMain 时,如果指定 console 子系统,链接器将报错,这时我们可以指定入口点启动函数 /entry:WinMainCRTStartup 来解决这种问题。
Qt指定链接子系统
Qt默认是设置了windows子系统(因为Qt是界面库,它默认设置这个很容易理解哈),因为不用手动输入CONFIG+=windows,我们应该更熟悉下面这个语句:
CONFIG += console
console.prf
看过qmake的manual,我们可以知道,CONFIG 中指定的东西一般要对应于 features 文件(即 console.prf 或 windows.prf 文件)
这两个文件在 $$QTDIR/mkspecs/features/win32 目录下,其内容会被包含进我们的*.pro文件。
我们打开 console.prf 文件看看:
CONFIG -= windows
contains(TEMPLATE, ".*app") : QMAKE_LFLAGS += $$QMAKE_LFLAGS_CONSOLE
呵呵,很容易理解对吧,就是设置一个链接选项。
根据我们所用编译器(比如mingw-g++)的不同,去看看相应的qmake.conf文件($$QTDIR/mkspecs/win32-g++/qmake.conf):
QMAKE_LFLAGS_CONSOLE = -Wl,-subsystem,console
通过第一部分的学习,这么简单的东西,现在不需要解释了吧。我们接下来重点看一下windows.prf文件
windows.prf
这个东西就复杂多了。我们的关注点:
1. 指定了链接选项(和前面console一样,此处略)
2. 定义了一个宏 QT_NEEDS_QMAIN (该宏存在是,我们的main函数其实被替换成了qMain)
3. 链接了一个新的库 qtmain.lib (libqtmain.a)
CONFIG -= console
contains(TEMPLATE, ".*app"){
QMAKE_LFLAGS += $$QMAKE_LFLAGS_WINDOWS
win32-g++:DEFINES += QT_NEEDS_QMAIN
win32-borland:DEFINES += QT_NEEDS_QMAIN
qt:for(entryLib, $$list($$unique(QMAKE_LIBS_QT_ENTRY))) {
isEqual(entryLib, -lqtmain): {
CONFIG(debug, debug|release): QMAKE_LIBS += $${entryLib}$${QT_LIBINFIX}d
else: QMAKE_LIBS += $${entryLib}$${QT_LIBINFIX}
} else {
QMAKE_LIBS += $${entryLib}
}
}
}
QMAKE_LIBS_QT_ENTRY
这个文件有些复杂,里面有个QMAKE_LIBS_QT_ENTRY,它涉及另外一个问题,就是我们在Qt在Windows下的入口函数
一文中提到的:
/*
WinMain() - Initializes Windows and calls user's startup function main().
NOTE: WinMain() won't be called if the application was linked as a "console"
application.
*/
extern "C"
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR, int cmdShow)
{
...
int result = main(argc, argv.data());
...
}
QT_NEEDS_QMAIN
注意,注意
,发现问题没?我们一开始提到了,当WinMain入口和main入口同时出现时,采用MSVC编译器时,我们可以根据链接子系统选择使用哪一个入口。
可是,我们同时说了,当采用MinGW时,两个入口同时出现时WinMain入口永远不会被使用。这可怎么办?
- 这样一来,qtmain 这个库对与 MinGW 来说就没有任何作用了。确实如此
- 但是,Qt官方还是让他起作用了,这就是,对于MinGW,当使用windows子系统时定义 QT_NEEDS_QMAIN 宏的原因
还是一切用代码说话:无论打开 qwindowdefs.h 还是 qtmain_win.cpp 这个文件,我们都能看到这样的代码
#if defined(QT_NEEDS_QMAIN)
int qMain(int, char **);
#define main qMain
#endif
QtTest模块
这是涉及控制台的有一个地方。非常诡异哈,一旦启用了该模块,就会出现控制台,还很难去掉。在Qt程序弹出CMD窗口
一文中我们讨论了这个问题,并给出一个能工作但很不优雅的方案。这儿我试图告诉大家问题原因及根本解决方法。
启用QtTest 有两种方法:
CONFIG += qtestlib
或
QT += testlib
前者
前者直接配置CONFIG,我们直接去看qtestlib.prf文件(你知道的,在$$QTDIR/mkspecs/features/目录下)就行了:
CONFIG += console
qtAddLibrary(QtTest)
文件内容很短,只有两行:
- 第一行:强制设置了console链接子系统
- 第二行:设置头文件路径和库文件(这是必须的哈)
注意:如果你不想要控制台,去掉这儿的第一行就可以啦。
后者
QT += testlib 是 CONFIG += QT 的细化,我们需要查看 qt.prf 文件(文件长,摘取片段):
for(QTLIB, $$list($$lower($$unique(QT)))) {
DEFINES *= $$upper(QT_$${QTLIB}_LIB)
isEqual(QTLIB, testlib):qlib = QtTest
isEqual(QTLIB, testlib):CONFIG += console
qtAddLibrary($$qlib)
除了多定义了一个QT_TESTLIB_LIB宏外,和完全前者一样。使用该方式是,如果不想要控制台,直接注释掉console这行即可。
注意:此处也解释了
qmake之CONFIG与QT
一文中的问题。
from邮件列表
我们知道:
- 非windows平台下,没有链接子系统的问题。一个程序,在控制台中启动时,就是一个控制台程序,程序可以直接输出数据到控制台。在窗口系统双击启动时,则不出现控制台。
- 在windows下,则区分这两个东西,所以我们同样需要对两个debug和release分别设置。
废话写了这么多了,看点有意思的,前些天,Qt-interest 邮件列表中,Graeme Gill 在对控制台是否出现的控制上,有个新的想法:让windows下的程序和unix下有同样的行为。
只需要在main函数开始加入如下代码(需要头文件windows.h):
#ifdef Q_WS_WIN
{
BOOL (WINAPI *AttachConsole)(DWORD dwProcessId);
*(FARPROC *)&AttachConsole =
GetProcAddress(LoadLibraryA("kernel32.dll"), "AttachConsole");
if (AttachConsole != NULL && AttachConsole(((DWORD)-1))) {
if (_fileno(stdout) == -1)
freopen("CONOUT$","wb",stdout);
if (_fileno(stderr) == -1)
freopen("CONOUT$","wb",stderr);
if (_fileno(stdin) == -1)
freopen("CONIN$","rb",stdin);
}
}
#endif // Q_WS_WIN
很有意思哈,只要检测到在console下运行,则链接上标准输入、标准输出、标准出错。
参考
相关推荐
Qt4.8.7是Qt4的终结版本,是Qt4系列版本中最稳定最经典的(很多嵌入式板子还是用Qt4.8),其实该版本是和Qt5.5差不多时间发布的。参考链接 https://www.qt.io/blog/2015/05/26/qt-4-8-7-released ...
Qt Windows 休眠唤醒信号
压缩包内包含msvc和mingw64两个版本的openssl动态库,下载后将dll放在Qt安装目录下对应的bin目录即可,比如本人的D:\Qt\Qt5.14.2\5.14.2\msvc2017_64\bin 和 D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin。 可以使用 ...
使用QT实现的汽车电子仪表盘,在windows下的安装程序 使用QT实现的汽车电子仪表盘,在windows下的安装程序 使用QT实现的汽车电子仪表盘,在windows下的安装程序 使用QT实现的汽车电子仪表盘,在windows下的安装程序 ...
使用最简单的方式,在windows下,用qt5.9版本,调用系统软键盘。
Windows系统安装Qt 5.15.2在线版
qt windows 下按键事件和hook捕获按键事件。程序运行后,首先按下M+N组合键,弹出(隐藏)主界面(用hook捕获M+N组合键);然后在主界面可以用w,s,a,d 按键进行移动标签小物体(用keyPressEvent和keyReleaseEvent...
在Windows下使用Qt所需要的链接库
这是为了适配QT压缩 qCompress 和 解压 qUncompress 函数而修改的文件, * 本代码中的 zCompress 函数对应QT的 qCompress; * 本代码中的 zUncompress 函数对应QT的 qUncompress; * QT的 QByteArray 用 ZByteArray...
windows下QT编写的连接wifi程序,使用Native wifi API编写,可以扫描连接WIFI
在windows下安装QT4在windows下安装QT4在windows下安装QT4在windows下安装QT4在windows下安装QT4
分别有服务端(Server)和客户端(Client) 服务端默认监听本机IP的6666端口 本人实测在linux(需防火墙开启端口)和windows下完美运行。基于Qt实现局域网Socket通信系统源码。分别有服务端(Server)和客户端...
Qt中调用函数如何返回多个值的Qt文件,详情可参考:https://blog.csdn.net/didi_ya/article/details/119854553
使用Qt5.6绘制sin函数波形,可调节周期与振幅。对于初学者绘制波形十分有用。
qt 5.12 windows版
Qt在windows下的串口读写操作的demo工程,适合初学者快速实现功能。
比如每50ms发一次信号,槽函数要耗时100毫秒,为了获取最新的信号,而采取的操作!这里提供两种解决思路!请看代码!
qt程序 在windows下平台下 qt版本 4.5.0 已经通过编译 一个简单的应用窗口程序 有菜单 工具栏等
Qt:Windows编程—Qt实现本地服务管理 示例代码 https://blog.csdn.net/qq_29542611/article/details/85010235
讲述Qt如何实现静态函数中发送信号,对应文章: https://lizhifun.blog.csdn.net/article/details/112631489