problems with HDC passing in lesson 13

Started by
4 comments, last by Meesh 16 years, 11 months ago
Hi all, got a little problem I can't seem to solve. I am trying to rewrite lesson 13 into a class. I've already updated the code to be VS 8.0 compatible, but there's still a problem.

GLvoid CText::BuildFont(GLvoid)								
{
	HFONT	font;									
	HFONT	oldfont;								
	m_base = glGenLists(96);							

	font = CreateFont(	-24,							
						0,					
						0,					
						0,					
						FW_BOLD,				
						FALSE,					
						FALSE,					
						FALSE,					
						ANSI_CHARSET,				
						OUT_TT_PRECIS,				
						CLIP_DEFAULT_PRECIS,			
						ANTIALIASED_QUALITY,			
						FF_DONTCARE|DEFAULT_PITCH,		
						"Courier New");				

	if(font == NULL)	
		MessageBox(NULL, "newfont", "error", MB_OK);
	
	oldfont = (HFONT)SelectObject(m_hDC, font);           

	if(oldfont == NULL)	
		MessageBox(NULL, "oldfont", "error", MB_OK);
	
	if(!wglUseFontBitmaps(m_hDC, 32, 96,m_base))				
	{
		LPVOID lpMsgBuf;
		LPVOID lpDisplayBuf;
		DWORD dw = GetLastError(); 

		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			dw,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR) &lpMsgBuf,
			0, NULL );

		lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
			(lstrlen((LPCTSTR)lpMsgBuf)+40)*sizeof(TCHAR)); 
		StringCchPrintf((LPTSTR)lpDisplayBuf, 
			LocalSize(lpDisplayBuf),
			TEXT("failed with error %d: %s"), 
			dw, lpMsgBuf); 
		MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

		LocalFree(lpMsgBuf);
		LocalFree(lpDisplayBuf);
		ExitProcess(dw); 
	}
		
	SelectObject(m_hDC, oldfont);							
	DeleteObject(font);									
}

The 'oldfont' var is NULL after the SelectObject() call so that drops an error, but I don't think that's the cause. The wglUseFontBitmaps() call pops a messagebox saying: "failed with error 0: The operation completed successfully". I find this a bit confusing as it should only return false if it fails. But having no error is self-contradictory, don't you think? I rather think the problem has something to do with the 'HDC m_hDC' variable. I've tried the exact same code in a C-style manner, with m_hDC replaced with a global hdc variable, and the function worked swimmingly. So, am i passing stuff right when i do this in the constructor?:

CText::CText(HDC hdc)
{
	BuildFont();
	m_hDC = (HDC)hdc;
}

this has stumped me. I believe that HDC is a typedef for *HDC so theres no need to worry about data copying vs. direct data access when passing the variable as parameter. Any ideas to shed some light on this?
Advertisement
BuildFont();m_hDC = (HDC)hdc;

And what does that mean m_hDC is in BuldFont()? You should probably try doing FormatError() after SelectObject() fails, and you'll see that the HDC is invalid.

And it should really be:
CText::CText(HDC hdc) : m_hDC(hdc){   BuildFont();}
Which will get rid of errors like this.
Hehehe, I now see the error of my ways. That was pretty incompetent of me. Thanks for the quick reply =)
Ok, now I'm stumped again.

I get no errors from the BuildFont() function, but there is nothing being displayed. I have removed some of the error checking code to make it easier to read, but wglUseFontBitmaps() does not return false, and the SelectObject() function returns the font properly now.

Posted is the full CText class and the calls to the class:

#ifndef __GL_TEXT_H_#define __GL_TEXT_H_#include <windows.h>#include <stdio.h>#include <stdarg.h>#include <gl/gl.h>#include <gl/glu.h>#include <gl/glaux.h>class CText{public:    CText();	CText(HDC hdc);    ~CText();    GLvoid Print(float x, float y, const char *fmt, ...);protected:    GLvoid BuildFont(GLvoid);    GLvoid KillFont(GLvoid);	HDC m_hDC;    	GLuint m_base;};#endif


#include "text.hpp"CText::CText(){	}CText::CText(HDC hdc) : m_hDC(hdc){	BuildFont();	}CText::~CText(){	KillFont();}GLvoid CText::BuildFont(GLvoid)							{	HFONT	font;										HFONT	oldfont;									m_base = glGenLists(96);								font = CreateFont(	-24,													0,											0,											0,											FW_BOLD,										FALSE,											FALSE,											FALSE,											ANSI_CHARSET,										OUT_TT_PRECIS,										CLIP_DEFAULT_PRECIS,									ANTIALIASED_QUALITY,									FF_DONTCARE|DEFAULT_PITCH,								"Courier New");					oldfont = (HFONT)SelectObject(m_hDC, font);    			wglUseFontBitmaps(m_hDC, 32, 96,m_base);					SelectObject(m_hDC, oldfont);							DeleteObject(font);								}GLvoid CText::KillFont(GLvoid)							{	glDeleteLists(m_base, 96);							}GLvoid CText::Print(float x, float y, const char *fmt, ...)  {	char		text[256];								va_list		ap;									int			len;								if (fmt == NULL)										return;										va_start(ap, fmt);										len = _vscprintf(fmt, ap) + 1;							if(len > 256)											return;		vsprintf_s(text,len,fmt, ap);							va_end(ap);										    glRasterPos2f(x, y);	glPushAttrib(GL_LIST_BIT);								glListBase(m_base - 32);								glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);			glPopAttrib();									}


Declared global:
CText TextPrinter


In my initialisation where ghdc is a global, working, HDC:
TextPrinter = CText(ghdc);


And finally the actual call: I do not tranformations, all matrices are loaded with identity matrices

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glColor3f(0.1, 0.3, 0.9);TextPrinter.Print(0.0, 0.0, "Hello Milla!");		SwapBuffers(ghdc);


Again, is this something obvious I'm missing or what? any help appreciated
Here's where the problem probably lies:
TextPrinter = CText(ghdc);

Your CText() class doesn't have a copy constructor or an operator=. That means that this will call the CText constructor with ghdc passed to it to create a temporary variable, then call operator= to assign that variable TextPrinter (Which will just call operator= on all member variables as the default implementation), then the CText destructor will be called to destroy the temporary variable you created, which calls KillFont().

You'll have to implement a copy constructor and operator=, remove the KillFont() from the destructor and make sure you call it manually, or use some other method of doing this like using a pointer to a CText instead of a plain value, and optionally using a smart pointer with it.
Thanks again. I seem to run into that problem all the time. I create a class, where I never want the default constructor to be called, only the parameterised ones. But when I create my instance I can't instantiate it with parameters (probably possible, but i don't know how yet. Still learning the intricacies of C vs C++)

This topic is closed to new replies.

Advertisement