c++ winapi window process inside of a class not working - c++

I am creating a window wrapper class in c++ but I cannot seem to fix this bug.
it seems that when my window process callback gets called it is not retrieving the this pointer I inputted for the extra information when creating the window. I did a little bit of debugging to find out that when I reinterpret it to a Window* it is NULL and every variable is uninitialized.
here is Window.h
#pragma once
#include <Windows.h>
#include <string>
class Window
{
private:
class WindowClass
{
private:
static WindowClass wndClass;
WindowClass();
HINSTANCE hInst;
const char* className = "Sadie Game Engine";
WindowClass(WindowClass&) = delete;
WindowClass operator=(WindowClass&) = delete;
~WindowClass();
public:
static WindowClass& getInstance();
HINSTANCE getHInstance();
const char* getClassName();
};
private:
const char* windowTitle;
int width, height;
int windowX, windowY;
HWND windowHandle;
public:
bool keyStates[256];
Window(int width, int height, int x, int y, const char* title);
void setWindowTitle(std::string str);
static LRESULT CALLBACK handleMsgCallBack(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT handleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
~Window();
};
and here is Window.cpp
#include "Window.h"
///
/// WindowClass
///
Window::WindowClass Window::WindowClass::wndClass;
Window::WindowClass::WindowClass()
{
hInst = GetModuleHandleA(nullptr);
WNDCLASSEXA wc = { 0 };
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = handleMsgCallBack;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.lpszClassName = className;
wc.hIcon = nullptr;
wc.hIconSm = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
RegisterClassExA(&wc);
}
Window::WindowClass::~WindowClass()
{
UnregisterClassA(className, hInst);
}
Window::WindowClass& Window::WindowClass::getInstance()
{
return wndClass;
}
HINSTANCE Window::WindowClass::getHInstance()
{
return hInst;
}
const char* Window::WindowClass::getClassName()
{
return className;
}
///
/// Window
///
Window::Window(int _width, int _height, int x, int y, const char* title)
{
width = _width;
height = _height;
windowX = x;
windowY = y;
windowTitle = title;
RECT wr;
wr.left = 100;
wr.right = width + wr.left;
wr.top = 100;
wr.bottom = height + wr.top;
AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, FALSE);
windowHandle = CreateWindowExA(0l, WindowClass::getInstance().getClassName(),
windowTitle, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
windowX, windowY, width, height, nullptr, nullptr, WindowClass::getInstance().getHInstance(), this);
ShowWindow(windowHandle, SW_SHOW);
}
void Window::setWindowTitle(std::string str)
{
SetWindowTextA(windowHandle, str.c_str());
}
LRESULT Window::handleMsgCallBack(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window* const windowPtr = reinterpret_cast<Window*>(GetWindowLongPtrA(hWnd, GWLP_USERDATA));
return windowPtr->handleMsg(hWnd, msg, wParam, lParam);
}
LRESULT Window::handleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
keyStates[wParam] = true;
break;
case WM_SYSKEYUP:
case WM_KEYUP:
keyStates[wParam] = false;
break;
case WM_CHAR:
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProcA(hWnd, msg, wParam, lParam);
}
Window::~Window()
{
DestroyWindow(windowHandle);
}
Any help would be appreciated.

As RbMm said, You did not call the SetWindowLongPtr function
Just add the following code:
case WM_NCCREATE:
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
Window* w = (Window*)pcs->lpCreateParams;
::SetWindowLongPtr(hWnd,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(w));
break;
}

Related

How does a single wndproc let each window know its serial number?

int Num = 0;
LRESULT CALLBACK TestWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT rc;
GetClientRect(hWnd, &rc);
RECT Winrc;
GetWindowRect(hWnd, &Winrc);
SYSTEMTIME time;
GetLocalTime(&time);
static const wchar_t* BoxTxt = L"";
static int MeIs = Num;
switch (message)
{
case WM_CREATE:
{
SetWindowLong(hWnd, GWL_EXSTYLE,
GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 220, LWA_ALPHA);
//GhWnd = hWnd;
break;
}
case WM_LBUTTONUP:
{
wchar_t meChar[20] = L"";
_itow(MeIs, meChar, 10);
MessageBox(0, meChar, meChar, 0);
}
case WM_SIZE:
{
InvalidateRect(hWnd, &rc, 1);
break;
}
case WM_NCLBUTTONDBLCLK:
{
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CLOSE:
{
Num -= 1;
DestroyWindow(hWnd);
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int CreateTestWindow()
{
//Call testwndproc. To reduce the length of the problem description, omit these codes
Num+=1;
return 0;
}
In the above code, when I create multiple windows and click it, it should pop up "1", "2", "3"... But actually all pop up "1".
static int MeIs = 0;
case WM_CREATE:
{
MeIs = Num;
}
Change to the above code and the serial number of the last window will pop up. For example, when the fourth window is created, all windows will pop up "4"
In practical application, each window has its own settings and is stored in the vector. Each window finds its own settings according to its own serial number:
struct Data
{
int x;
int y;
int width;
int height;
const wchar_t* text;
}
std::vector<data>UserData(32);//Max:32
//then read them from file,But the window must know which window it is:UserData[i].
For example,the first window will set their coordinates to UserData[1].x and UserData[1].y,also need to save the file when closing.
Any idea?thank you!
There are a couple of ways to maintain per-window data using the Win32 API.
The simplest is using the GWL_USERDATA slot accessible via GetWindowLongPtr(...) and SetWindowLongPtr(...). A typical way to initialize this user data value is to use the creation parameters passed to the WM_CREATE message by the CreateWindow call.
Code below:
#include <windows.h>
#include <string>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
struct SomeData {
int n;
std::wstring str;
};
int RegisterWindow(HINSTANCE hInstance, const wchar_t* wnd_class) {
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(WHITE_BRUSH);
wc.lpszClassName = wnd_class;
if (!RegisterClass(&wc)) {
return 1;
}
return 0;
}
int CreateWindowWithUserData(HINSTANCE hInstance, const wchar_t* wnd_class, int n, const std::wstring& str) {
auto* data_ptr = new SomeData{ n, str };
if (!CreateWindow(wnd_class, L"Window text", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, data_ptr))
return 2;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
const auto* wnd_class = L"foobar";
RegisterWindow(hInstance, wnd_class);
for (int i = 1; i <= 5; i++) {
CreateWindowWithUserData(hInstance, wnd_class, i, L"blah");
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: {
CREATESTRUCT* create_struct = reinterpret_cast<CREATESTRUCT*>(lParam);
SomeData* user_data = reinterpret_cast<SomeData*>(create_struct->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(user_data));
}
return 0;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_PAINT: {
SomeData* user_data = reinterpret_cast<SomeData*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT r = { 20, 20, 300, 35 };
auto msg = user_data->str + L" " + std::to_wstring(user_data->n);
DrawText(hdc, msg.c_str(), -1, &r, DT_SINGLELINE);
EndPaint(hWnd, &ps);
}
return 0;
case WM_DESTROY: {
SomeData* user_data = reinterpret_cast<SomeData*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
delete user_data;
}
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Another way to do something similar is to use the cbWndExtra extra field when registering the window class, as discussed in this answer.
You can:
Indeed store data in Window. SetProp, SetWindowLong+GWL_USERDATA, SetWindowLong+cbWndExtra
Map HWND to your data, like using c++ std::map
Use a thunk to have an associated object, like ATL, see ATL thunk header for available APIs (have to do it manually for older OSes)

CreateWindow() returns nullptr and GetLastError() returns 1400

Hello I have recently decided to learn graphics programming through DirectX11 and for that I'm learning how to use the Windows API. I managed to create a window by following an online tutorial and later successfully created one by myself. However, the problem starts when I tried to make a framework to create the Window, I ran into a few errors but by studying the documentation and looking up examples most of them were solved, except for one involving the CreateWindow() function.
Whenever I try to get the handle for the window it returns nullptr, I tried getting the error code through GetLastError(), but all I get is 1400, which according to MSDN is "Invalid window handle". I don't know what I'm doing wrong and I haven't found any threads that solved the issue. It compiles successfully, but no window appears.
Window.hpp
#pragma once
#include "WindowsHeader.hpp"
class Window
{
public:
Window(int width, int height, const char* name);
~Window();
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
private:
class WindowClass
{
public:
static const char* GetName() noexcept;
static HINSTANCE GetInstance() noexcept;
private:
WindowClass() noexcept;
~WindowClass();
WindowClass(const WindowClass&) = delete;
WindowClass& operator=(const WindowClass&) = delete;
static constexpr const char* window_class_name{ "HardwareAccelerated3D" };
static WindowClass window_class;
HINSTANCE hinst;
};
static LRESULT CALLBACK HandleMsgSetup(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept;
static LRESULT CALLBACK HandleMsgInvoke(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept;
LRESULT HandleMsg(HWND, UINT msg, WPARAM wparam, LPARAM lparam) noexcept;
int width;
int height;
HWND hwnd;
};
Window.cpp
#include "Window.hpp"
#include <sstream>
//Window Class
Window::WindowClass Window::WindowClass::window_class;
Window::WindowClass::WindowClass() noexcept
: hinst(GetModuleHandle(NULL))
{
WNDCLASSEX window_class{ {0} };
window_class.cbSize = sizeof(window_class);
window_class.style = CS_OWNDC;
window_class.lpfnWndProc = &HandleMsgSetup;
window_class.cbClsExtra = NULL;
window_class.cbWndExtra = NULL;
window_class.hInstance = GetInstance();
window_class.hIcon = NULL;
window_class.hCursor = NULL;
window_class.hbrBackground = NULL;
window_class.lpszMenuName = NULL;
window_class.lpszClassName = GetName();
window_class.hIconSm = NULL;
RegisterClassEx(&window_class);
}
Window::WindowClass::~WindowClass()
{
UnregisterClass(GetName(), GetInstance());
}
const char* Window::WindowClass::GetName() noexcept
{
return window_class_name;
}
HINSTANCE Window::WindowClass::GetInstance() noexcept
{
return window_class.hinst;
}
//Window
Window::Window(int width, int height, const char* name)
: width(width), height(height)
{
RECT wndrec;
wndrec.left = 100;
wndrec.right = wndrec.left + width;
wndrec.top = 100;
wndrec.bottom = wndrec.top + height;
if (!AdjustWindowRect(&wndrec, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, false))
{
std::ostringstream oss;
oss << GetLastError();
OutputDebugString(oss.str().c_str());
}
hwnd = CreateWindow(WindowClass::GetName(), name, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, wndrec.right - wndrec.left, wndrec.bottom - wndrec.top, NULL,
NULL, WindowClass::GetInstance(), this);
if (!hwnd)
{
std::ostringstream oss;
oss << GetLastError();
OutputDebugString(oss.str().c_str());
}
ShowWindow(hwnd, SW_SHOWDEFAULT);
}
Window::~Window()
{
DestroyWindow(hwnd);
}
LRESULT WINAPI Window::HandleMsgSetup(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept
{
if (msg == WM_NCCREATE)
{
const CREATESTRUCTW* const pcreate = reinterpret_cast<CREATESTRUCTW*>(lparam);
Window* const pwnd = static_cast<Window*>(pcreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pwnd));
SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&Window::HandleMsgInvoke));
return pwnd->HandleMsg(hwnd, msg, wparam, lparam);
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT WINAPI Window::HandleMsgInvoke(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept
{
Window* const pwnd = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
return pwnd->HandleMsg(hwnd, msg, wparam, lparam);
}
LRESULT Window::HandleMsg(HWND, UINT msg, WPARAM wparam, LPARAM lparam) noexcept
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
WinMain.cpp
#include "WindowsMessageMap.hpp"
#include "Window.hpp"
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
Window window(640, 480, "HardwareAccelerated3D");
MSG msg;
BOOL gresult;
while (gresult = GetMessage(&msg, NULL, NULL, NULL) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (gresult == -1)
return -1;
return msg.wParam;
}
WindowsHeader.hpp is just a few defines and Windows.h to avoid some windows macros, and WindowsMessageMap.hpp just prints windows messages to the debug console (I use it to see what messages are being called).
P.S.: this is my first post here, so feel free to point out any issues with the post itself, so that I can be more clear next time.
Thanks in advance! :-)
The hwnd you used in the code is the same as it in Window.hpp:
LRESULT Window::HandleMsg(HWND, UINT msg, WPARAM wparam, LPARAM lparam) noexcept
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
The last line of Window.hpp:
HWND hwnd;
When you call HandleMsg, hwnd of Window.hpp is not initialized:
You may use hwnd in HandleMsgSetup, just add the hwnd param like the following code:
LRESULT Window::HandleMsg(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
And it works for me:

Constructor string parameter not setting window title

The window title in the title bar is not setting to the variable and string i assign it.
I have show my code below but the problem is app::Window * wnd = new app::Window(L"Window Title"); constructor does seem to be 'tstring mWindowName;' but this mhWnd = CreateWindowEx(NULL, className.c_str(), mWindowName.c_str(), WS_OVERLAPPEDWINDOW, 100, 200, mScreenWidth, mScreenHeight, nullptr, nullptr, mhInstance, this); is not setting the title.
Window.h
#ifndef _ST_Window_H_
#define _ST_Window_H_
// include files
//#include "s-window.h"
#include "s-platform.h"
#include "s-safe-delete-and-release.h"
#include "s-iostream.h"
#include "s-strings.h"
#ifndef _WINDOWS_
#include <windows.h>
#endif //_WINDOWS_
namespace app
{
class Window
{
protected:
HINSTANCE mhInstance;
HWND mhWnd;
tstring mWindowName;
bool mRunState;
long int mScreenWidth;
long int mScreenHeight;
public:
Window(void);
Window(tstring windowName);
~Window(void);
int Setup(long int width, long int height);
LRESULT MessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int Run(void);
inline HINSTANCE GetInstance(void) { return mhInstance; }
inline HWND GetHwnd(void) { return mhWnd; }
inline long GetWidth(void) { return mScreenWidth; }
inline long GetHeight(void) { return mScreenHeight; }
inline tstring GetWindowName(void) { return mWindowName; }
inline bool GetRunState(void) { return mRunState; }
protected:
int Create(void);
};
}
#endif //!_ST_Window_H_
Window.cpp
// include files
#include "sit-window.h"
using namespace std;
namespace app
{
Window::Window(void) : mhInstance(GetModuleHandle(0)), mhWnd(nullptr), mWindowName(nullptr),
mRunState(true), mScreenWidth(0), mScreenHeight(0)
{
}
Window::Window(tstring windowName) : mhInstance(GetModuleHandle(0)), mhWnd(nullptr),
mWindowName(windowName), mRunState(true), mScreenWidth(0), mScreenHeight(0)
{
}
Window::~Window(void)
{
}
int Window::Setup(long int width, long int height)
{
mScreenWidth = width;
mScreenHeight = height;
return Create();
}
LRESULT Window::MessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
LRESULT CALLBACK Window::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window * wnd;
if (msg == WM_NCCREATE)
{
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
wnd = (Window*)pcs->lpCreateParams;
wnd->mhWnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);
return TRUE;
}
else
{
wnd = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (wnd)
return wnd->MessageProc(hwnd, msg, wParam, lParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int Window::Run(void)
{
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
};
int Window::Create(void) {
WNDCLASSEX wnd;
SecureZeroMemory(&wnd, sizeof(WNDCLASSEX));
wnd.cbClsExtra = NULL;
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.cbWndExtra = NULL;
wnd.hbrBackground = (HBRUSH)(COLOR_WINDOW + 3);
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wnd.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wnd.hInstance = mhInstance;
wnd.lpfnWndProc = WindowProc;
tstring className = mWindowName;
className.append(L" - WndCls");
wnd.lpszClassName = className.c_str();
wnd.lpszMenuName = nullptr;
wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
if (!RegisterClassEx(&wnd))
{
std::wcout << L"Window class not registered!" << std::endl;
mRunState = false;
return 0x0;
}
RECT rect = { 0, 0, mScreenWidth, mScreenHeight };
if (!AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, NULL))
{
std::wcout << L"Failed to adjust window rect!" << std::endl;
mRunState = false;
return 0x0;
}
else
{
mScreenWidth = rect.right;
mScreenHeight = rect.bottom;
}
mhWnd = CreateWindowEx(NULL, className.c_str(), mWindowName.c_str(),
WS_OVERLAPPEDWINDOW, 100, 200, mScreenWidth, mScreenHeight,
nullptr, nullptr, mhInstance, this);
if (!mhWnd)
{
std::wcout << L"Window not created!" << std::endl;
mRunState = false;
return 0x0;
}
if (ShowWindow(mhWnd, SW_SHOW))
{
std::wcout << L"Failed to show window!" << std::endl;
mRunState = false;
return 0x0;
}
if (!UpdateWindow(mhWnd))
{
std::wcout << L"Failed to update window!" << std::endl;
mRunState = false;
return 0x0;
}
if (!SetForegroundWindow(mhWnd))
{
std::wcout << L"Failed to set to foreground!" << std::endl;
mRunState = false;
return 0x0;
}
return 0x0;
}
}
Main.cpp
// include files
//#include "s-safe-delete-and-release.h"
//#include "s-window.h"
#include "sit-window.h"
#include <iostream>
#include <string>
#include <windows.h>
#include <assert.h>
#pragma comment(lib, "serenity-core.lib")
// namespaces
using namespace std;
// statics
//long int srnty::Window::mScreenWidth = 1024;
//long int srnty::Window::mScreenHeight = 768;
// win main entry point
namespace win {
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
wcout << L"WinMain() called..." << endl;
app::Window * wnd = new app::Window(L"Window Title");
wnd->Setup(1024, 768);
int result = wnd->Run();
SafeDelete(wnd);
return result;
}
}
// main entry point
int main(int argv, char argc[])
{
// detect memory leaks in the application
#if defined(DEBUG) | defined(_DEBUG)
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
_CrtSetBreakAlloc(0);
int mlCount = _CrtDumpMemoryLeaks();
wcout << L"Number of memory Leaks: " << to_wstring(mlCount) << endl;
assert(mlCount == 0);
#endif
wcout << L"main() called..." << endl;
win::WinMain(GetModuleHandle(0), nullptr, nullptr, SW_SHOW);
wchar_t title[1024];
wchar_t titleBuffer[1024];
GetConsoleTitle(titleBuffer, 1024);
//wsprintf(title, L"%d", GetCurrentProcessId());
SetConsoleTitle(title);
Sleep(40);
HWND hwndFound = FindWindow(NULL, title);
SetForegroundWindow(hwndFound);
system("pause");
return 0x0;
}
I expect tstring mWindowName; to set the window Title in the title bar when used here mhWnd = CreateWindowEx(NULL, className.c_str(), mWindowName.c_str(), WS_OVERLAPPEDWINDOW, 100, 200, mScreenWidth, mScreenHeight, nullptr, nullptr, mhInstance, this);. The initialization of the tstring mWindowName; is through the constructor in Main.cpp app::Window * wnd = new app::Window(L"Window Title");
As Raymond Chen above clearly pointed out. This was an oversight on my behalf.
In the Window.cpp file LRESULT CALLBACK Window::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) never returned control the DefWindowProc function there for the never sets the title.
The fucntion should look like this:
LRESULT CALLBACK Window::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window * wnd;
if (msg == WM_NCCREATE)
{
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
wnd = (Window*)pcs->lpCreateParams;
wnd->mhWnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);
//return TRUE;
return DefWindowProc(hwnd, msg, wParam, lParam);
}
else
{
wnd = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (wnd)
return wnd->MessageProc(hwnd, msg, wParam, lParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Thank you i hope this helps someone else.

Weird behaviour when global variables are added in another file

Here's my simple program:
#include "stdafx.h"
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);
HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
void draw(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hWinDC);
}
int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) {
memset(screenBuffer, 0, sizeof(screenBuffer));
MSG msg = { 0 };
WNDCLASS wnd = { 0 };
wnd.lpfnWndProc = WndProc;
wnd.hInstance = hInstace;
wnd.lpszClassName = L"Window";
if (!RegisterClass(&wnd)) {
return 0;
}
HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);
if (!hwnd) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
while (true) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
draw(hwnd);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_CREATE:
initBackBuffer(hwnd);
break;
case WM_DESTROY:
DeleteDC(hBackDC);
DeleteObject(hBackBitmap);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void initBackBuffer(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
hBackDC = CreateCompatibleDC(hWinDC);
hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
SelectObject(hBackDC, hBackBitmap);
ReleaseDC(hwnd, hWinDC);
}
The output is as expected.
I moved
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
into Global.h and I added #include "Global.h" in my main file.
Main File :
#include "stdafx.h"
#include<Windows.h>
#include "Global.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);
HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;
void draw(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hWinDC);
}
int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) {
memset(screenBuffer, 0, sizeof(screenBuffer));
MSG msg = { 0 };
WNDCLASS wnd = { 0 };
wnd.lpfnWndProc = WndProc;
wnd.hInstance = hInstace;
wnd.lpszClassName = L"Window";
if (!RegisterClass(&wnd)) {
return 0;
}
HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);
if (!hwnd) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
while (true) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
draw(hwnd);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_CREATE:
initBackBuffer(hwnd);
break;
case WM_DESTROY:
DeleteDC(hBackDC);
DeleteObject(hBackBitmap);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void initBackBuffer(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
hBackDC = CreateCompatibleDC(hWinDC);
hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
SelectObject(hBackDC, hBackBitmap);
ReleaseDC(hwnd, hWinDC);
}
Global.h
#pragma once
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
I get a erroneous white window.
I don't understand why this is happening because the compiler will anyway copy the contents of Global.h in to main file, so both variants should produce same results.
What is the cause of this problem ?
There is a bug here:
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
void foo()
{
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
}
This should be should be i < 512. Otherwise it overwrites a random memory location, this can result in an error in a different location, or no error if you are lucky. Debugger may report a nonsensical error, or no error at all. If screenBuffer was created on stack, debugger may give "heap corruption" error.
Consider using std::vector to avoid this problem in future.
vector<int> vec;
vec[vec.size()] = 0;//<- debug error
Side note: SetDIBitsToDevice or StretchDIBits will set bits directly:
void draw(HWND hwnd)
{
BITMAPINFO bi;
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biWidth = WIDTH;
bi.bmiHeader.biHeight = HEIGHT;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(hwnd);
SetDIBitsToDevice(hdc, 0, 0, WIDTH, HEIGHT, 0, 0, 0, HEIGHT, screenBuffer,
&bi, DIB_RGB_COLORS);
ReleaseDC(hwnd, hdc);
}

C++ Callbacks - Unresolved external symbol

I'm trying to write a error handling system in c++, which calls a callback function whenever an error rises. However, the linker always complains about an unresolved external symbol whenever I try to run the project.
GEWindow.h:
#include <Windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <functional>
#ifndef GE_WINDOW
#define GE_WINDOW
//class GEWindow;
//typedef void(*GE_ERROR_CALLBACK)(HWND, UINT, LPCWSTR);
#define GE_WINDOW_REGISTERATION_FAILED 1
#define GE_WINDOW_CREATION_FAILED 2
class GEWindow {
public:
typedef std::function<void(GEWindow *, UINT, LPCWSTR)> GE_ERROR_CALLBACK;
static int geCreateWindow(GEWindow *window);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static inline void setErrorCallback(GE_ERROR_CALLBACK fun) { errCallback = fun; };
inline HWND getHWnd() { return hWnd; };
inline int getWidth() { return _width; };
inline int getHeight() { return _height; };
inline LPCWSTR getTitle() { return _title; };
inline void setWidth(int width) { _width = width; };
inline void setHeight(int height) { _height = height; };
inline void setTitle(LPCWSTR title) { _title = title; };
private:
static GE_ERROR_CALLBACK errCallback;
HWND hWnd;
int _width = 400, _height = 400;
LPCWSTR _title;
};
#endif
GEWindow.cpp:
#include "GEWindow.h"
int GEWindow::geCreateWindow(GEWindow *window) {
HINSTANCE hInstance = GetModuleHandle(NULL);
TCHAR szWindowClass[] = _T("GEWindow");
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = GEWindow::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
//if (!errCallback)
// errCallback(window, GE_WINDOW_REGISTERATION_FAILED, _T("Failed to register the GE window"));
MessageBox(NULL, _T("Failed to register"), _T("Error"), NULL);
return NULL;
}
window->hWnd = CreateWindow(
szWindowClass,
window->_title,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
window->_width, window->_height,
NULL,
NULL,
hInstance,
NULL);
if (!window->hWnd) {
//if (!errCallback)
// errCallback(window, GE_WINDOW_CREATION_FAILED, _T("Failed to create the GE window"));
MessageBox(NULL, _T("Failed to create"), _T("Error"), NULL);
return NULL;
}
ShowWindow(window->hWnd, SW_SHOW);
UpdateWindow(window->hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
LRESULT CALLBACK GEWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_PAINT:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
main.cpp:
#include "GEWindow.h"
static void error_callback(GEWindow *window, UINT errorCode, LPCWSTR message) {
MessageBox(window->getHWnd(), message, _T("Error"), NULL);
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
GEWindow window;
window.setWidth(500);
window.setHeight(500);
window.setTitle(_T("lala lulu"));
/*
error LNK2001: unresolved external symbol "private: static class std::function<void __cdecl(class GEWindow *, unsigned int, wchar_t const *)> GEWindow::errCallback" (?errCallback#GEWindow##0V?$function#$$A^AXPAVGEWindow##IPB_W#Z#std##A)
*/
window.setErrorCallback(error_callback);
GEWindow::geCreateWindow(&window);
}
When using static member variables, you only declare them in the class, you need to define them as well. That definition has to be done in a source file, like this:
GEWindow::GE_ERROR_CALLBACK GEWindow::errCallback;
static void GeWindows::error_callback() will fix this problem..I found it after days by days by the help of "Tony The Lion" (credit...)