Sign in to follow this  
Programmer16

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

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Have you tried calling GetLastError to see what is failing in your call to RegisterClass.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?

Thats good grammar (as far as I know) :)

I'm still working through it, but I haven't changed the paths lately (other than adding DirectX paths). This problem only happens when I change to release version. I'm pretty sure that I'm building with exceptions (default Microsoft Visual Studio .NET 2003.)

I should have it figured out by tomorrow, if not, I'll think of something to do then.

Thanks for all the help guys!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this