NULL pointer, thought it seems to be initialized - c++

I get
Debug assertion failed.
p!=0
and it points to:
_NoAddRefReleaseOnCComPtr<T>* operator->() const throw()
{
ATLASSERT(p!=NULL);
return (_NoAddRefReleaseOnCComPtr<T>*)p;
}
in 'atlcomcli.h'
From what I understand it means I have forgot to initialize a pointer somewhere, but all of them seem to be initialized.
When I use normal pointers instead of 'CComPtr', it throws 'Access Violation Reading Location' at 'font->DrawTextA' in 'D3DFont::Draw' in D3DFont.cpp
//D3DFont.h:
#include <D3DX10.h>
#include <atlbase.h>
#include <string>
class D3DFont
{
public:
D3DFont(void);
~D3DFont(void);
bool Create(ID3D10Device *device, std::string name, int width,
int height, int weight, int mipLevels, bool italic, BYTE charset,
BYTE quality, BYTE pitchAndFamily);
void Draw(LPD3DX10SPRITE sprite, std::string text, int charCount,
LPRECT rect, UINT format, D3DXCOLOR color);
private:
CComPtr<ID3DX10Font> font;
};
//D3DFont.cpp:
#include "D3DFont.h"
D3DFont::D3DFont(void){}
D3DFont::~D3DFont(void){}
bool D3DFont::Create( ID3D10Device *device, std::string name,
int width, int height, int weight, int mipLevels, bool italic,
BYTE charset, BYTE quality, BYTE pitchAndFamily )
{
D3DX10_FONT_DESC fd;
ZeroMemory(&fd, sizeof(D3DX10_FONT_DESC));
fd.Height = height;
fd.Width = width;
fd.Weight = weight;
fd.MipLevels = mipLevels;
fd.Italic = italic;
fd.CharSet = charset;
fd.Quality = quality;
fd.PitchAndFamily = pitchAndFamily;
strcpy_s(fd.FaceName, name.c_str());
// INITIALIZING FONT HERE
D3DX10CreateFontIndirect(device, &fd, &font);
return true;
}
void D3DFont::Draw( LPD3DX10SPRITE sprite, std::string text,
int charCount, LPRECT rect, UINT format, D3DXCOLOR color )
{
// ERROR HERE
font->DrawTextA(sprite, text.c_str(), charCount, rect, format, color);
}
And my use of above functions:
if( !font.Create(d3d.GetDevice(), "Impact", 0, 175, 0, 1, false,
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE) )
{
MessageBox(0, "Could not create font.", "Error!", MB_OK | MB_ICONERROR);
}
// later on...
RECT r = {35, 50, 0, 0};
font.Draw(0, "Test", -1, &r, DT_NOCLIP, d3d.GetColorObj(1.0f, 1.0f, 0.0f, 1.0f));
What could I miss?
'D3DX10CreateFontIndirect' throws 0x8876086C
Can't find what does it mean, but some google threads are related to d3dDevice, so I guess it must be related to it. Will update when I will have more info.

Calling the D3DX10CreateFontIndirect doesn't actually guarantee that your pointer will be initialized.
Rule of thumb : ALWAYS check HRESULTs when using DirectX functions that initialize a pointer:
HRESULT hr = D3DX10CreateFontIndirect(device, &fd, &font);
if(FAILED(hr)){
//Get the last error, display a message, etc.
//Eventually propagate the error if the code can't continue
//with the font pointer uninitialized.
}
When your function returns E_FAIL, do not try to use the pointer afterward. There are great chances that the values of the parameters are simply incorrect (here, your device pointer might be null or your font description might be incorrect).

Related

C++ different instance of class

I'm new to C++ so I'm not exactly sure what to put into the title of this problem. Anyway, I created a class whose purpose is to create a Label then use it later to create another Label again and again.
CALLBACK MyClassName::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept -> LRESULT {
....
switch (msg) {
....
case WM_CREATE:
{
ControlLabel controlLabel, anotherLabel; //declare two control label
controlLabel.Label(123, hwnd); //Set new id and window handle for label
controlLabel.SetXPosition(68); //Set x position
controlLabel.SetYPosition(110); //Set y position
controlLabel.SetText("This is Label"); //Set the text of Label
controlLabel.SetFontSize(14); //Set the font size of the text
anotherLabel.Label(456, hwnd); //Create and set new id and window handle for another label
anotherLabel.SetXPosition(68); //Set x position of another label
anotherLabel.SetYPosition(140); //Set y position of another label
anotherLabel.SetText("This is another Label"); //Set the text of another label
anotherLabel.SetFontSize(14); //Set the font size of another label
break;
}
....
return ::DefWindowProcW(hwnd, msg, wparam, lparam);
}
I'm expecting it to have an output of two different labels, e.g.
This is Label
This is another Label
Instead, I get two Labels with the same text.
This is another Label
This is another Label
Anyway, here's the full source of the class.
ControlLabel.H
#pragma once
#ifndef CONTROLLABEL_H
#define CONTROLLABEL_H
#include "Header.h"
class ControlLabel {
public:
ControlLabel();
HWND Label(int Label_ID, HWND WindowHandle);
void SetXPosition(int xPosition);
void SetYPosition(int yPosition);
void SetText(string Text);
void SetFontFamily(string FontFamily);
void SetFontSize(int FontSize);
void SetFontColor(int R, int G, int B);
void SetBackgroundColor(int Rr, int Gg, int Bb, bool SetBGColor);
private:
void UpdateLabel();
void SetWidthAndHeights();
static std::wstring StringConverter(const std::string& s);
static LRESULT CALLBACK LabelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
static HWND LabelHandle;
static SolidBrush vFontColor;
static string text, vFontFamily;
static bool SetBGColor;
static int xPosition, yPosition, width, height, LABEL_ID, vFontSize, R, G, B, BckR, BckG, BckB;
};
#endif
ControlLabel.cpp
#include "ControlLabel.h"
HWND ControlLabel::LabelHandle = NULL;
int ControlLabel::xPosition = 0;
int ControlLabel::yPosition = 0;
int ControlLabel::width = 0;
int ControlLabel::height = 0;
int ControlLabel::LABEL_ID = 0;
int ControlLabel::vFontSize = 12;
int ControlLabel::R = 0;
int ControlLabel::G = 0;
int ControlLabel::B = 0;
int ControlLabel::BckR = 0;
int ControlLabel::BckG = 0;
int ControlLabel::BckB = 0;
bool ControlLabel::SetBGColor = FALSE;
string ControlLabel::text = "Label";
string ControlLabel::vFontFamily = "Segoe UI";
ControlLabel::ControlLabel() {}
/** This function is used to convert string into std::wstring. **/
std::wstring ControlLabel::StringConverter(const std::string& s) {
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
/** This function is used to automatically set the Width and Height of static control base on the length of the text. **/
void ControlLabel::SetWidthAndHeights() {
std::wstring fontFamilyTemp = StringConverter(vFontFamily);
std::wstring textTemp = StringConverter(text);
LPCWSTR textLabel = textTemp.c_str();
HDC hdc = GetDC(LabelHandle);//static control
HFONT hFont = CreateFont(
-MulDiv(vFontSize, GetDeviceCaps(hdc, LOGPIXELSX), 90), //calculate the actual cHeight.
0, 0, 0, // normal orientation
FW_NORMAL, // normal weight--e.g., bold would be FW_BOLD
false, false, false, // not italic, underlined or strike out
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, // select only outline (not bitmap) fonts
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH | FF_SWISS, fontFamilyTemp.c_str());
SIZE size;
HFONT oldfont = (HFONT)SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, textLabel, wcslen(textLabel), &size);
width = size.cx;
height = size.cy;
SelectObject(hdc, oldfont); //don't forget to select the old.
DeleteObject(hFont); //always delete the object after creating it.
ReleaseDC(LabelHandle, hdc); //alway reelase dc after using.
/*char buffer[100];
sprintf_s(buffer, "WIDTH: %d | HEIGHT: %d\n", width, height);
OutputDebugStringA(buffer);*/
}
/** This function will be called when new option is set. For example, fontSize is set. **/
void ControlLabel::UpdateLabel() {
if(LabelHandle != NULL) {
SetWidthAndHeights();
SetWindowPos(LabelHandle, nullptr, xPosition, yPosition, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER);
InvalidateRect(LabelHandle, NULL, FALSE);
UpdateWindow(LabelHandle);
}
}
/** This is the callback function of static control. **/
LRESULT CALLBACK ControlLabel::LabelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch(uMsg) {
case WM_ERASEBKGND: {
if(SetBGColor) { //We only want to do this if the SetColor is modified to true, meaning we want to set the color of background.
RECT rect;
GetClientRect(hwnd, &rect);
FillRect((HDC)wParam, &rect, CreateSolidBrush(RGB(BckR, BckG, BckB))); //set titlebar background color.
return 1; //return 1, meaning we take care of erasing the background.
}
return 0;
}case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Graphics g(hdc);
std::wstring fontFamilyTemp = StringConverter(vFontFamily);
std::wstring textTemp = StringConverter(text);
FontFamily theFontFamily(fontFamilyTemp.c_str());
Font font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);
SolidBrush brush(Color(255, R, G, B));
PointF pointF(0.0f, 0.0f);
TextRenderingHint hint = g.GetTextRenderingHint(); // Get the text rendering hint.
g.SetTextRenderingHint(TextRenderingHintAntiAlias); // Set the text rendering hint to TextRenderingHintAntiAlias.
g.DrawString(textTemp.c_str(), -1, &font, pointF, &brush);
EndPaint(hwnd, &ps);
return TRUE;
}case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, LabelProc, uIdSubclass);
return 0;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
/** Use this function to create a Label. Parent or WindowHandle must be specified, this is where the Label will be drawn. Unique Label ID must be specified. **/
HWND ControlLabel::Label(int Label_ID, HWND WindowHandle) {
LABEL_ID = Label_ID;
LabelHandle = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, xPosition, yPosition, width, height, WindowHandle, NULL, NULL, NULL); //create the static control.
SetWindowSubclass(LabelHandle, &LabelProc, LABEL_ID, 0);
return LabelHandle;
}
/** Use this function to set the X Position of the Label. **/
void ControlLabel::SetXPosition(int xxPosition) {
if(LabelHandle != NULL) {
xPosition = xxPosition; //set xposition
UpdateLabel();
}
}
/** Use this function to set the Y Position of the Label. **/
void ControlLabel::SetYPosition(int yyPosition) {
if(LabelHandle != NULL) {
yPosition = yyPosition; //set xposition
UpdateLabel();
}
}
/** Use this function to set the text of the Label. **/
void ControlLabel::SetText(string ttext) {
if(LabelHandle != NULL) {
text = ttext; //set text
UpdateLabel();
}
}
/** Use this function to set the font family of the Label. **/
void ControlLabel::SetFontFamily(string font_family) {
if(LabelHandle != NULL) {
vFontFamily = font_family; //set font family
UpdateLabel();
}
}
/** Use this function to set the font size of the Label. **/
void ControlLabel::SetFontSize(int size) {
if(LabelHandle != NULL) {
vFontSize = size; //set font size
UpdateLabel();
}
}
/** Use this Function to set the font color of the Label using RGB. **/
void ControlLabel::SetFontColor(int Rr, int Gg, int Bb) {
if(LabelHandle != NULL) {
R = Rr;
G = Gg;
B = Bb;
UpdateLabel();
}
}
/** Use this Function to set the background color of the Label using RGB. Last parameter must be TRUE if you want to set your own background color. **/
void ControlLabel::SetBackgroundColor(int Rr, int Gg, int Bb, bool setColor) {
if(LabelHandle != NULL) {
SetBGColor = setColor;
BckR = Rr;
BckG = Gg;
BckB = Bb;
UpdateLabel();
}
}
Static class members are shared between all instances of a class. There's a static string text in your class so it is to be expected that all instances share that. If you need to store per-instance data you need to use non-static class members.
Presumably, you've used static class members so that you can put your window procedure inside the class' implementation (which needs to be static). To have a static window procedure access per-instance data has been asked and answered before (like here, here, here, or here).
There's no such thing as "instance" when all your variables are declared static. static means the variable is shared among all instances of the class, making it essentially global. So, when you call:
controlLabel.SetText("This is Label");
You assign your text to the global text variable. Then, calling
anotherLabel.SetText("This is another Label");
Assigns the new string to that same global text variable. Your original string was forgotten at this point.
How can you solve this? I can think of multiple ways off the top of my head, maybe you can think of something better. The idea is to somehow bind the text (or the entire controlLabel instance) to a label.
Putting the label text directly into the window data (using WM_SETTEXT. Then you can pull it up in LabelProc and draw it, or just let the default STATIC window procedure handle it.
Making the labels a custom window class that has some extra space reserved for each window instance. Then use SetWindowLong to put a pointer to a whole controlLabel in there. Raw pointers are generally not a great thing in C++, but then again, Win32 API was made for C. Then pull the instance up when needed with GetWindowLong. Just remember to un-static the text member, so it doesn't get overwritten.
Use a global/static std::map<HWND, controlLabel> to associate each label with an instance of controlLabel. Again, if you do this, remember to un-static the text.
Oh, and when you called any controlLabel method that somehow uses the label handle, you just randomly happened to have the handle that you wanted in the LabelHandle variable, since it's also static.
That doesn't make any sense, you won't be able to have a different instance if all your members are static.
However, since the callback of window procedure needs to be static, then you won't be able to access those non-static members inside that function.
What you can do is use the dwRefData parameter of the subclass to pass the instance of your class into your callback function. You can then cast that parameter to access non-static members.
For example in your ::Label function;
SetWindowSubclass(LabelHandle, &LabelProc, LABEL_ID, <DWORD_PTR>(this)); //notice the last parameter, pass `this` instance so you can use `dwRefData`.
Then on your callback;
ControlLabel* controlLabel = reinterpret_cast<ControlLabel*>(dwRefData); //type cast the value of dwRefData.
controlLabel->text //you can now access your non-static variable text
controlLabel->vFontFamily //you can now access your non-static variable vFontFamily
Something like that.
Another problem is you shouldn't declare your callback as private, make it public instead. And do not declare your ControlLabel object inside the WM_CREATE, make it global instead.
ControlLabel controlLabel, anotherLabel; //declare two control label as Global.
CALLBACK MyClassName::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept -> LRESULT {
....
switch (msg) {
....
case WM_CREATE:
{
controlLabel.Label(123, hwnd); //Set new id and window handle for label
...
anotherLabel.Label(456, hwnd); //Create and set new id and window handle for another label
...
break;
}
....
return ::DefWindowProcW(hwnd, msg, wparam, lparam);
}
Anyway, I noticed your class doesn't have destructor to destroy your LabelHandle. Make sure to do that.
ControlLabel.h //In your ControlLabel.h
~ControlLabel(); //should be in public
ControlLabel.cpp //In your ControlLabel.cpp
ControlLabel::~ControlLabel() {
if(LabelHandle) DestroyWindow(LabelHandle); //destroy when done.
}

Copy char* to tchar*

I am trying to point a char* to a tchar* that is inside of a struct.
typedef struct data {
int count;
TCHAR* msg;
COLORREF colour;
} DATA, *PDATA; // DATA non-pointer, PDATA pointer
pdata = (PDATA)malloc(sizeof(pdata));
pdata->colour = RGB(255, 0, 0);
pdata->msg = (TCHAR*)malloc(sizeof(TCHAR) * 20);
// copy tchar to tchar in pdata->msg
// I have tried _tcscpy and _tcscpy_s but it is not working
SetWindowLongPtr(hwnd, 0, (LONG)pdata);
Then I am trying to get the data through
(PDATA)GetWindowLongPtr(hwnd, 0);
This is being called in a separate file and then used to show the text on the screen

How to reset my DirectX 9 device?

Recently, I've found a bug in my 3D application that uses DirectX 9. The bug is that everytime I alt-tab or ctrl-alt-delete, the program freezes or seems to do nothing. I've looked around and found out that I need to reset my device by using IDirect3DDevice9::TestCooperativeLevel and IDirect3DDevice9::Reset. But what I'm struggling on at the moment is that (as far as I know) before I Reset the device, I need to release the resouce in D3DPOOL_MANAGE before I reset the device and reallocate the resource back to it after the Reset? How do I do this? Below is the code I've got for the reset of my device (in the IsDeviceLost() function).
DirectX.h
#pragma once
#pragma comment(lib, "d3d9.lib")
#include<d3d9.h>
#include<d3dx9math.h>
#include"Window.h"
#include"Vertex.h"
#include"Shader.h"
#define VERTEXFORMAT (D3DFVF_XYZ | D3DFVF_TEX1) //Flags for the Flexible Vertex Format (FVF)
class Shader;
class DirectX
{
public:
DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen);
virtual ~DirectX();
static LPDIRECT3D9 direct3D;
static LPDIRECT3DDEVICE9 device;
static IDirect3DVertexDeclaration9* vertexDec;
static ID3DXEffect* currentShaderEffect;
void CheckShaderVersion();
bool IsDeviceLost();
protected:
D3DPRESENT_PARAMETERS direct3DPresPara;
Window *window;
unsigned int width;
unsigned int height;
D3DXMATRIX projMatrix;
private:
void Initialize(bool fullscreenMode);
};
Direct.cpp
#include"DirectX.h"
LPDIRECT3D9 DirectX::direct3D = NULL;
LPDIRECT3DDEVICE9 DirectX::device = NULL;
IDirect3DVertexDeclaration9* DirectX::vertexDec = NULL;
ID3DXEffect* DirectX::currentShaderEffect = NULL;
DirectX::DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen)
{
width = windowWidth;
height = windowHeight;
window = new Window(windowTitle.c_str(), windowWidth, windowHeight, x, y, fullscreen);
Initialize(fullscreen);
D3DXMatrixPerspectiveFovLH( &projMatrix,
D3DXToRadian(45),
(float)width/(float)height,
1.0f,
15000.0f);
//device->SetTransform(D3DTS_PROJECTION, &projMatrix);
}
DirectX::~DirectX()
{
direct3D->Release();
device->Release();
vertexDec->Release();
delete vertexDec;
delete currentShaderEffect;
delete window;
}
void DirectX::Initialize(bool fullscreenMode)
{
direct3D = Direct3DCreate9(D3D_SDK_VERSION);
ZeroMemory(&direct3DPresPara, sizeof(direct3DPresPara));
switch(fullscreenMode)
{
case true:
direct3DPresPara.Windowed = false;
break;
case false:
direct3DPresPara.Windowed = true;
}
direct3DPresPara.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //turns off VSync, comment this line of code to turn VSync back on
direct3DPresPara.SwapEffect = D3DSWAPEFFECT_DISCARD;
direct3DPresPara.hDeviceWindow = window->GetHandle();
direct3DPresPara.BackBufferFormat = D3DFMT_X8R8G8B8;
direct3DPresPara.BackBufferWidth = width;
direct3DPresPara.BackBufferHeight = height;
direct3DPresPara.EnableAutoDepthStencil = TRUE;
direct3DPresPara.AutoDepthStencilFormat = D3DFMT_D16;
direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window->GetHandle(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &direct3DPresPara, &device);
D3DVERTEXELEMENT9 vertexElement[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()};
device->CreateVertexDeclaration(vertexElement, &vertexDec);
//currentShaderEffect = 0;
}
void DirectX::CheckShaderVersion()
{
D3DCAPS9 caps;
device->GetDeviceCaps(&caps);
bool stop = false;
int i = 6;
while(!stop)
{
if(caps.VertexShaderVersion < D3DVS_VERSION(i, 0))
{
i--;
}
else
{
std::cout << "you are using shader model " << i << std::endl;
stop = true;
}
}
//std::cout << caps.VertexShaderVersion << std::endl;
//std::cout << D3DVS_VERSION(3, 0) << std::endl;
}
bool DirectX::IsDeviceLost()
{
HRESULT result = device->TestCooperativeLevel();
if(result == D3DERR_DEVICELOST)
{
Sleep(20); //Sleep for a little bit then try again.
return true;
}
else if(result == D3DERR_DRIVERINTERNALERROR)
{
MessageBox(0, "Internal Driver Error. The program will now exit.", 0, 0);
PostQuitMessage(0);
return true;
}
else if(result == D3DERR_DEVICENOTRESET)
{
device->Reset(&direct3DPresPara);
return false;
}
else
{
return false;
}
}
Any help or advice will be greatly appreciated. Thanks
You don't need to release resources in D3DPOOL_MANAGED. You need to release resources in D3DPOOL_DEFAULT.
If you're using D3DX objects (ID3DXFont, ID3DXMesh, ID3DXSprite), they typically have "OnLostDevice" and "OnResetDevice" methods. You should call those methods in appropriate situations.
If an object does not have any methods for dealing with "DeviceLost" state, and they should be released, simply destroy the object before resetting the device, and reload it afterwards.
Please note that D3DPOOL_MANAGED objects are reloaded automatically by the driver and don't need any "help" from you to deal with reset/devicelost. YOu do need to take care about all other objects.
In addition to what I said/wrote, you should obviously read DirectX SDK documentation. They cover lost devices in their documentation, and DirectX examples normally survive device reset just fine.

WinAPI C++ - RegisterClassEx 'The parameter was incorrect'

I am having issues with my code. I am making a class to wrap around some WinAPI to create GUIs, however I am having issues when attempting to register the class.
My code:
inline Window::Window(const TCHAR *windowName, const int x, const int y, const int width, const int height, const TCHAR *className) : abstractWindow() {
Window(0, className, windowName, WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, NULL, NULL);
}
Window::Window(const DWORD dwExStyle, const TCHAR *lpClassName, const TCHAR *lpWindowName, const DWORD dwStyle, const int x, const int y, const int nWidth, const int nHeight, const HWND hWndParent, const HMENU hMenu, const HINSTANCE hInstance, const LPVOID lpParam) : abstractWindow() {
_proc = (WNDPROC*) &abstractWindow::msgRouter;
_styleEx = dwExStyle;
_className = (!lpClassName) ? TEXT("MyGuiClass") : lpClassName;
_windowName = lpWindowName;
_style = dwStyle;
_x = x;
_y = y;
_width = nWidth;
_height = nHeight;
_hwndParent = hWndParent;
_hInstance = (!hInstance) ? ::GetModuleHandle(NULL) : hInstance;
_hMenu = hMenu;
_lpParam = lpParam;
_wndClassEx.cbSize = sizeof(_wndClassEx);
_wndClassEx.style = CS_HREDRAW | CS_VREDRAW;
_wndClassEx.lpfnWndProc = abstractWindow::msgRouter;
_wndClassEx.cbClsExtra = 0;
_wndClassEx.cbWndExtra = 0;
_wndClassEx.hInstance = _hInstance;
_wndClassEx.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
_wndClassEx.hCursor = ::LoadCursor(NULL, IDC_ARROW);
_wndClassEx.hbrBackground = (HBRUSH) COLOR_WINDOW;
_wndClassEx.lpszMenuName = NULL;
_wndClassEx.lpszClassName = _className;
_wndClassEx.hIconSm = ::LoadIcon(NULL, IDI_APPLICATION);
}
_wndClassEx is already defined in the class header as of WNDCLASSEX and the register function simply runs RegisterClassEx(&_wndClassEx).
The following are how I call these classes: (However only one is called at a time)
Window gui (TEXT("Title"), 10, 10, 500, 250);
Window gui (0, NULL, TEXT("Title"), WS_OVERLAPPEDWINDOW, 10, 10, 500, 200, NULL, NULL, hInstance, NULL);
The second one works perfectly fine, however when I call the first (shorter parameters, which puts it through to the second) class registration fails. I have completely written the _wndClassEx as well as gone through every single one and modified it to no success. I have gone through with a debugger and everything seems fine. So I have absolutely no idea what to do.
By the way, abstractWindow::msgRouter is static.
Thanks.
The problem is with this constructor:
inline Window::Window(const TCHAR *windowName, const int x, const int y, const int width, const int height, const TCHAR *className) : abstractWindow() {
Window(0, className, windowName, WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, NULL, NULL);
}
You can't call a constructor from another constructor like that. What ends up happening is that a temporary object is created and then immediatly discarded. The way to achieve that (this only works if you have a compiler which implements this feature from the new C++11 Standard) is if you say
inline Window::Window(const TCHAR *windowName, const int x, const int y, const int width, const int height, const TCHAR *className) : Window(0, className, windowName, WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, NULL, NULL) {};
Another way would be to do what #aztaroth said: create a separate method and call it from both constructors (that works even with an older compiler).
Write a method that initializes the window (basically cut and paste the second constructor), then call it with proper values from both constructors.

Drawing a jpg in MFC

I've been trying to show a jpg image in MFC, but I can't get it drawn. I'm a complete MFC newbie an everything I got up until now is mostly adapted from things I found on the net. Currently I have this:
Picture.h:
#pragma once
#include <afxwin.h>
class Picture
{
public:
Picture();
bool load(LPCTSTR filePath);
bool draw( CDC* deviceContext
, CRect clientRect
, LPCRECT prcMFBounds);
CSize getSize(CDC* pDC);
private:
LPPICTURE m_picture;
};
Picture.cpp:
#include "Picture.h"
Picture::Picture()
: m_picture(0)
{
}
bool Picture::load(LPCTSTR szFile)
{
HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false;
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == (DWORD)-1)
{
CloseHandle(hFile);
return false;
}
LPVOID pvData = NULL;
// alloc memory based on file size
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
if (hGlobal == NULL)
{
CloseHandle(hFile);
return false;
}
pvData = GlobalLock(hGlobal);
if (pvData == NULL)
{
GlobalUnlock(hGlobal);
CloseHandle(hFile);
return false;
}
DWORD dwBytesRead = 0;
// read file and store in global memory
bool bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL) != 0;
GlobalUnlock(hGlobal);
CloseHandle(hFile);
if (!bRead)
return false;
LPSTREAM pstm = NULL;
// create IStream* from global memory
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
if (!(SUCCEEDED(hr)))
{
if (pstm != NULL)
pstm->Release();
return false;
}
else if (pstm == NULL)
return false;
// Create IPicture from image file
if (m_picture)
m_picture->Release();
hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&(m_picture));
if (!(SUCCEEDED(hr)))
{
pstm->Release();
return false;
}
else if (m_picture == NULL)
{
pstm->Release();
return false;
}
pstm->Release();
return true;
}
bool Picture::draw(CDC* deviceContext, CRect clientRect, LPCRECT prcMFBounds)
{
if (clientRect.IsRectNull())
{
CSize imageSize = getSize(deviceContext);
clientRect.right = imageSize.cx;
clientRect.bottom = imageSize.cy;
}
long pictureWidth = 0;
long pictureHeigth = 0;
m_picture->get_Width(&pictureWidth);
m_picture->get_Height(&pictureHeigth);
m_picture->Render( *deviceContext
, clientRect.left
, clientRect.top
, clientRect.Width()
, clientRect.Height()
, 0
, pictureHeigth
, pictureWidth
, -pictureHeigth
, prcMFBounds);
return true;
}
CSize Picture::getSize(CDC* deviceContext)
{
if (!m_picture)
return CSize(0,0);
LONG width, height; // HIMETRIC units
m_picture->get_Width(&width);
m_picture->get_Height(&height);
CSize size(width, height);
if (deviceContext==NULL)
{
CWindowDC dc(NULL);
dc.HIMETRICtoDP(&size); // convert to pixels
}
else
{
deviceContext->HIMETRICtoDP(&size);
}
return size;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CStatic
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
PictureView.cpp:
#include "PictureView.h"
#include "Picture.h"
PictureView::PictureView(std::string path)
{
m_picture = new Picture();
m_picture->load(path.c_str());
}
void PictureView::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
{
CRect rect;
GetClientRect(&rect);
m_picture->draw(GetDC(), rect, rect);
}
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", SS_OWNERDRAW, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
The problem is that I can't get the DrawItem function to be called. What am I doing wrong?
Any help will be appreciated.
Here's an easy way to draw an image in an MFC dialog box:
link with gdiplus.dll
#include "atlimage.h>
#include "gdiplus.h>
using namespace Gdiplus
.
.
.
CImage ci;
ci.Load((CString)"D:\\Pictures\\mycat.jpg");
CDC *dc = AfxGetMainWnd()->GetDC();
HDC hdc = *dc;
ci.Draw(hdc,10,10);
Hope it works!
with the help of a colleague of mine I've found the answer. He told me it's not possible to have an ownerdrawn CStatic. So when I now inherit PictureView from CButton and make it BS_OWNERDRAW my image is rendered.
Personally I think this is an ugly solution but I'm so tired of this problem now that I don't really care that much. These are the changes I've made to make it work:
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CButton
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
Thank's everybody for all the help.
I'm not sure that the GetDC in the call to m_picture->draw will necessarily refer to the same DC that's given in the CREATESTRUCT. What I've done is:
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
EDIT: Since I can't comment, hopefully an edit will suffice. I think your colleague is mistaken, as I just recently implemented a CStatic control using SS_OWNERDRAW. I'm guessing that the addition of WS_CHILD | WS_VISIBLE on the Create call is key.
I've never used MFC but a quick perusal of the CStatic::DrawItem documentation says that it must be created with the SS_OWNERDRAW style for DrawItem to get called. You haven't shown the Create line for your PictureView so perhaps it's that?