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