1+1=10

Debao's Blog...

"Link Confilict between SDL and Qt under Windows"

Someone complain that, when using SDL and Qt in the same project under Windows, the linker will generate link error.

SDLmain.lib(SDL_win32_main.obj):-1: error: LNK2005: _WinMain@16 already defined in qtmaind.lib(qtmain_win.obj)

What happened behined this?

#include <QApplication>
#include <SDL.h>
int main(int argc, char** argv)
{
return 0;
}

WinMain vs main

The C and C++ standards require any program to have a function called main, which serves as the program's startup function. It can have one of the following signatures:

int main()
int main(int argc, char* argv[])

However, WinMain is selected by Microsoft as the conventional name used for the GUI application entry point.

int CALLBACK WinMain(
  _In_  HINSTANCE hInstance,
  _In_  HINSTANCE hPrevInstance,
  _In_  LPSTR lpCmdLine,
  _In_  int nCmdShow
);

As a crossplatform library, both SDL and Qt don't require user to use the WinMain function to create GUI application for Windows. Instead, they both provided a WinMain() for us.

WinMain provided by Qt

qtmain.lib provided by Qt

As a Qt developer, we all know that, when ceating a GUI application under windows. qtmain.lib(or libqtmain.a) will be linked to the application.

If we open the source file of the library, we can see that our main() entry is called by a wrapped function WinMain()

```cpp %QTDIR%/src/winmain/qtmain_win.cpp / 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)

Very insteresting, isn't it? But

### This work well for MSVC though, but not for MinGW

Consider we have a simple windows application which contians both main() and WinMain()

```cpp
#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; 
}
  • For MSVC, WinMain() or main() will be used depending on whether /entry:WinMainCRTStartup is passed to the linker or not.
  • For MinGW, WinMain() will never be called if main() exists!

How to solve this problem for MinGW

Let's see what happened when build a Qt Gui application under Windows.

In Qt souce file qwindowdefs.h or qtmain_win.cpp, we can find following code.

#if defined(QT_NEEDS_QMAIN)
#define main qMain
#endif

So main() doesn't exist any more when QT_NEEDS_QMAIN is defined!

What happened for Gui Application?

We know that,

CONFIG += windows

is the default config of qmake, and that's why we have to add

CONFIG += console

if when want to create a mormal console application.

The former will force the qmake to load a feature file called windows.prf, in which we can find fowllowing code:

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

As you can see, three things done here:

  • Windows subsystem instead of console subsystem is linked, so no black-cmd-window will be shown when the application running. Note that, $$QMAKE_LFLAGS_WINDOWS will be expanded to /subsystem:windows or -Wl,-subsystem,windows.
  • QT_NEEDS_QMAIN is defined for MinGW, so main() will be renamed to qMain which will be called by WinMain()
  • qtmain.lib is passed to the linked which provides the definition of WinMain().

WinMain provided by SDL

Now, consider that we have familiar what's happpened in Qt, it's time to go into the SDL.

WinMain is provided by SDLmain.lib

Source code is more or less like this

/* This is where execution begins [console apps] */
int
console_main(int argc, char *argv[])
{
//...

    /* Run the application main() code */
    status = SDL_main(argc, argv);
//...
}

/* This is where execution begins [windowed apps] */
int WINAPI
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
    char **argv;
    int argc;
    char *cmdline;
//...
    /* Run the main program */
    console_main(argc, argv);
//...
}

As we can see, a function named SDL_main() is called by WinMain here, but in where is this function defined?

```cpp SDL_main.h

define main SDL_main

## How to solve the confilict between the two WinMain()

Very interesting ^_^

* Qt: WinMain() provided by qtmain.lib
* SDL: WinMain() provided by SDLmain.lib
* Qt: main() is redefined to qMain() for MinGW
* SDL: main() is redefined to SDL_main() always

Consider that

* WinMain() in SDLmain.lib provide some initialization for SDL, while WinMain() in qtmain.lib not provide such thing for Qt.
* SDLmain is provided for all platform, while qtmain is for Windows only.

We tended to use the WinMain() provided by SDLmain.lib instead of qtmain.lib

So we can simply add following line to the .pro file to solve the  ```_WinMain@16``` conflict.

win32:QMAKE_LIBS_QT_ENGTRY -= -lqtmain

Though this works very for MSVC, but not that well for MinGW. As this cause macro re-definition, and whether the application can be linked successfully will depend on the order of the headers included.

```cpp
//Qt
#if defined(QT_NEEDS_QMAIN)
#define main qMain
#endif
//SDL
#define main    SDL_main

So another line is needed

win32:QMAKE_LIBS_QT_ENGTRY -= -lqtmain
win32-g++:DEFINES -= QT_NEEDS_QMAIN

or we can disable windows application facility provided by qmake totally, then do it ourself.

CONFIG-= windows
QMAKE_LFLAGS += $$QMAKE_LFLAGS_WINDOWS

Reference

  • http://wiki.libsdl.org/moin.fcg/FAQWindows
  • http://blog.csdn.net/dbzhang800/article/details/6358996

Qt SDL

Comments