Practically the same code, but one is producing an error...

Started by
9 comments, last by Programmer16 18 years, 4 months ago
I made a Window class to help with making simple demos and I'm running into an error. Here is my class:

class Window
{
	HWND		m_WndHandle;
	WNDCLASS	m_WndClass;
public:
	Window()
	{
		char szFileName[MAX_PATH];
		char szDrive[8], szDirectory[MAX_PATH];
		WIN32_FIND_DATA FindData;

		ZeroMemory(&m_WndClass, sizeof(WNDCLASS));
		ZeroMemory(&FindData, sizeof(WIN32_FIND_DATA));
		GetModuleFileName(0, szFileName, MAX_PATH);
		_splitpath(szFileName, szDrive, szDirectory, 0, 0);
		_makepath(szFileName, szDrive, szDirectory, 0, 0);

		if(FindFirstFile(std::string(std::string(szFileName) + std::string("*.ico")).c_str(), &FindData) == INVALID_HANDLE_VALUE)
			m_WndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
		else
			m_WndClass.hIcon = (HICON)LoadImage(0, FindData.cFileName, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_SHARED | LR_LOADTRANSPARENT);

		if(FindFirstFile(std::string(std::string(szFileName) + std::string("*.cur")).c_str(), &FindData) == INVALID_HANDLE_VALUE)
			m_WndClass.hCursor = LoadCursor(0, IDC_ARROW);
		else
			m_WndClass.hCursor = (HCURSOR)LoadImage(0, FindData.cFileName, IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE | LR_SHARED | LR_LOADTRANSPARENT);

		FindFirstFile(std::string(std::string(szFileName) + std::string("*.exe")).c_str(), &FindData);
		m_WndClass.lpszClassName = FindData.cFileName;

		m_WndClass.hbrBackground	= GetSysColorBrush(COLOR_BTNFACE);
		m_WndClass.hInstance		= GetModuleHandle(0);
		m_WndClass.lpfnWndProc		= DefWindowProc;
	}

	void Create()
	{
		assert(RegisterClass(&m_WndClass) != 0);
		assert((m_WndHandle = CreateWindow(m_WndClass.lpszClassName, m_WndClass.lpszClassName, WS_POPUP | WS_VISIBLE, 0, 0, 800, 600, 0, 0, m_WndClass.hInstance, 0)) != 0);
	}

	void Start(void (*Idle)())
	{
		if(!m_WndHandle)
			Create();

		MSG Msg;
		ZeroMemory(&Msg, sizeof(MSG));
		while(1)
		{
			if(PeekMessage(&Msg, 0, 0, 0, PM_REMOVE))
			{
				if(Msg.message == WM_QUIT)
					break;

				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
			}
			else
			{
				Idle();
			}
		}
	}

	void OverrideIcon(HICON hIcon)
	{
		if(!m_WndHandle)
			m_WndClass.hIcon = hIcon;
	}

	void OverrideCursor(HCURSOR hCursor)
	{
		if(!m_WndHandle)
			m_WndClass.hCursor = hCursor;
	}

	void OverrideWndProc(WNDPROC WndProc)
	{
		if(!m_WndHandle)
			m_WndClass.lpfnWndProc = WndProc;
		else
			SetWindowLong(m_WndHandle, GWL_WNDPROC, (long)WndProc);
	}

	void OverrideClassName(const char* pClassName)
	{
		if(!m_WndHandle)
			m_WndClass.lpszClassName = pClassName;
	}

	HWND		GetHandle() const	{ return m_WndHandle;	}
	WNDCLASS	GetWndClass() const	{ return m_WndClass;	}
};

And this is my code that is using it:

g_Wnd.OverrideWndProc(InputProc);
g_Wnd.Start(IdleProc);

This works perfectly everytime. But, if I add g_Wnd.Create() before g_Wnd.Start() and after g_Wnd.OverrideWndProc(), it fails to register the class (on the very first line of g_Wnd.Create()). Am I missing something, or is this not exactly what g_WndStart would do? Anybody have any ideas?
Advertisement
Hmm, I see problems here:
		assert(RegisterClass(&m_WndClass) != 0);assert((m_WndHandle = CreateWindow(m_WndClass.lpszClassName, m_WndClass.lpszClassName, WS_POPUP | WS_VISIBLE, 0, 0, 800, 600, 0, 0, m_WndClass.hInstance, 0)) != 0);


assert(whatever) will resolve to nothing in release mode. Better use the VERIFY(whatever) macro.

You didn't initialize m_WndHandle in your constructor, so maybe it is filled with garbage, which would lead Create() to think your window is already created.
Have you tried calling GetLastError to see what is failing in your call to RegisterClass.
In your constructor, you are assigning the address of a local variable (FindData.cFileName) to m_WndClass.lpszClassName. That's a really bad idea because, once your constructor returns, that local variable is no longer valid. I suspect that this may be the cause of your problem. You are just lucky that it was working before you changed the code.
Quote:Original post by Programmer16
Window(){	// ...	WIN32_FIND_DATA FindData;	// ...	m_WndClass.lpszClassName = FindData.cFileName;	// ...}

You're storing a pointer to a local variable (well, a member of a local variable. Same difference). As soon as the constructor finishes executing FindData will be deallocated and m_WndClass.lpszClassName will be left dangling. May not be the problem, but it's a problem.

Enigma
I didn't catch the local variable thing, but that is fixed now. I had already fixed setting m_WndHandle to 0. I don't understand how this could be luck, it works fine whenever I use the first code, but not afterwards (even if I change it back to the first code).

Also, I'll change to the VERIFY macro. While we're on the topic of Release mode, when I put my project into release mode, I get an external linker error for Xran()? Anybody know what this is? (I'm using Visual Studio .NET 2k3)
Can you post the linker error?
Quote:Original post by Konfusius
Can you post the linker error?


Code Bytes error LNK2019: unresolved external symbol "public: void __thiscall std::_String_base::_Xran(void)const " (?_Xran@_String_base@std@@QBEXXZ) referenced in function "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > & __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::erase(unsigned int,unsigned int)" (?erase@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEAAV12@II@Z)


Thanks for the help guys, I fixed the other problem.
Quote:Original post by Programmer16
I don't understand how this could be luck, it works fine whenever I use the first code, but not afterwards (even if I change it back to the first code).


Lucky in that the pointer was still sitting undisturbed on the stack when RegisterClass was called. By changing the code, you changed the execution path. This could result in that spot on the stack getting clobbered before the call to RegisterClass. By changing the code back, you restore the execution path to what it was when it worked, so no apparent problem.

That's why these kinds of things can be hard to track down. In general, if adding or removing code reveals a problem (or makes one go away), it is a good sign that you've done something like used a local when you needed a global or stomped on the stack somewhere.
Have you sorted out the linker error? I recall having had (<--grammar?) this one a long time ago, but I have no idea what would be causing it.


Are you building with or without exceptions?
Have you fiddled with your include/lib/src paths recently?

This topic is closed to new replies.

Advertisement