Search code examples
c++winapiunresolved-external

"Unresolved external _WinMain@16" if I move WndProc to another cpp file


For a Windows application I'm trying to get CreateWindow() and WndProc() (or my versions of them) to be part of a singleton class that is created at the beginning of _tWinMain() but since trying to shift the functions to GameHandler.h and GameHandler.cpp I keep getting "unresolved external symbol _WinMain@16". They were originally global functions in main.cpp and everything was compiling fine then I decided to move them to GameHandler and ever since all I get is the unresolved external, even if I try to move them back to main.cpp.

I'm doing this in VS2010, the project was created as a Windows Application and there's no specific entry point set in properties (I double checked as every solution I've found so far says that it's because it's a console app - this isn't).

The code I currently have is shown below. The actual project has a couple of thousand lines of other code that I've left out as I don't think it's relevant (but will happily proved wrong. While the actual window creation code is related, I don't think the code itself is the problem (apart from what I left in), it's the location of GameWindowProc() &/or CreateGameWindow() or how they're called. The actual window creation code is taken from NeHe's tutorial. Trying to compile the following code only gives the aforementioned unresolved external.

main.cpp:

#include <Windows.h>
#include "GameManager.h"

#ifndef USEGMGR
bool CreateGameWindow(char* title, int width, int height, int bits, bool fullScreenFlag);
LRESULT CALLBACK GameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif

int APIENTRY _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, 
                        LPTSTR lpCmdLine, int nCmdShow)
{
    GameManager::Startup();
    GameManager* GMgr = GameManager::GetInstance();

    GMgr->SetProgramState(GAME_MODE);
    while(GMgr->GetProgramState() != GAME_MODE) // Normally this would be if (State != GAME_QUIT)
    { /* do game related stuff */ }

    GameManager::Shutdown();
    return 0;
}

#ifndef USEGMGR
bool CreateGameWindow(char* title, int width, int height, int bits, bool fullScreenFlag)
{
    // Fairly complex but flexible creation code, taken from NeHe's tutorials. Of relevant interest is:
    WNDCLASS        wc;                             // Windows Class Structure
    wc.lpfnWndProc  = (WNDPROC) GameWindowProc;  // WndProc Handles Messages
    if (!RegisterClass(&wc))                         // Attempt To Register The Window Class
    {
        MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
        return false;
    }
    return true;
}

LRESULT CALLBACK GameWindowProc(HWND    hWnd,            // Handle For This Window
    UINT    uMsg,            // Message For This Window
    WPARAM    wParam,            // Additional Message Information
    LPARAM    lParam)            // Additional Message Information
{
    // various custom message handling, if not processed:
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
#endif

in GameManager.h:

#ifndef GAMEMANAGER_H
#define GAMEMANAGER_H
#define USEGMGR // makes CreateGameWindow() and GameWindowProc() methods in GameManager instead of global

#include <Windows.h>

enum ProgramState
{
    GAME_MODE,
    GAME_QUIT,
};

class GameManager
{
public:
    static void             Startup();
    static void             Shutdown();
    static GameManager*     GetInstance();
    void                    Update(); // code not shown, check quit key etc
#ifdef USEGMGR
    const bool              CreateGameWindow(char* title, int width, int height, int bits, bool fullScreenFlag);
    static LRESULT CALLBACK GameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif
    void                    KillGameWindow(void);
    const int                GetProgramState() const;
    void                    SetProgramState(const int& newMode);
private:
    GameManager();
    ~GameManager();
    GameManager(const GameManager&);
    GameManager& operator=(const GameManager&);
    HINSTANCE                m_hInstance;
    HWND                    m_hWnd;        
    HDC                        m_hDC;        
    static GameManager*        s_instance;
    int                        m_programState; // uses ProgramState enum
};
#endif

in GameManager.cpp:

#include "GameManager.h"
#include <Windows.h>
#include <assert.h>

#ifndef USEGMGR
extern bool CreateGameWindow(char* title, int width, int height, int bits, bool fullScreenFlag);
#endif
GameManager*        GameManager::s_instance    = NULL;
GameManager::GameManager(){}
GameManager::~GameManager(){}


void GameManager::Startup()
{
    assert(s_instance == NULL);
    s_instance = new GameManager;
#ifdef USEGMGR
    if (! (s_instance->CreateGameWindow("Game Window", 800, 600, 32, true )) )
#else
    if (! (CreateGameWindow("Game Window", 800, 600, 32, true )) )
#endif
        assert("CreateGameWindow failed! Need an error here"); // Quit If Window Was Not Created - clean this up later    
}

void GameManager::Shutdown()
{
    assert(s_instance != NULL);
    delete s_instance;
    s_instance = NULL;
}

GameManager* GameManager::GetInstance(){return s_instance;}

void GameManager::Update(){/* msg handling, watch for quit key, etc */}
const int GameManager::GetProgramState() const{return s_instance->m_programState;}
void GameManager::SetProgramState(const int& newState){s_instance->m_programState = newState;}

#ifdef USEGMGR
const bool GameManager::CreateGameWindow(char* title, int width, int height, int bits, bool fullScreenFlag)
{
    // Fairly complex but flexible creation code, taken from NeHe's tutorials. Of relevant interest is:
    WNDCLASS        wc;                             // Windows Class Structure
    wc.lpfnWndProc  = (WNDPROC) GameManager::GameWindowProc;  // WndProc Handles Messages
    if (!RegisterClass(&wc))                         // Attempt To Register The Window Class
    {
        MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
        return false;
    }
    return true;
}

LRESULT CALLBACK GameManager::GameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // various custom message handling, if not processed:
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
#endif    

As you can see, I've set up some preprocessor conditionals to switch between the troublesome functions being in main.cpp or as part of GameManager. Comment out #define USEGMGR at the beginning of GameManager.h to have them as global funcs in main.cpp.

Can someone please tell me what I'm doing wrong?

Edit: removed comment about not being able to quit if you get it to run.


Solution

  • Add

    #include <tchar.h>
    

    to the top of main.cpp so that the _tWinMain macro gets defined properly.

    What happens if you don't have the macro definition is that you end up with a function named _tWinMain() (or some mangled version of the name like ?_tWinMain@@YGHPAUHINSTANCE__@@0PADH@Z()) in the object file, but the linker and runtime initialization code are looking for WinMain() or wWinMain(). They don't find it.

    <tchar.h> defines a macro that transforms the name _tWinMain() into one of the two names everything else is looking for. You must have had something including that header before you started your refactoring (quite possibly indirectly), and lost it somehow.

    Or you can dispense with the macro version and name the function WinMain or wWinMain (either one should work, regardless of whether you're building for UNICODE or not). If you do that, just remember to change the LPTSTR parameter declaration to match the one you choose.