In my code I have a call to the GetRawInputDeviceInfo function, passing in the RIDI_DEVICENAME command. It is supposed to fill the 3rd parameter with a string containing the name of the buffer, and return the length of the string.
However, when the function returns, that parameter points to invalid memory (inside the debugger it says 0x234449485c3f5c5c, and can't view the contents of it).
This is the complete code:
#include <windows.h>
#include <stdint.h>
#define assert(x) do{if(!(x))__debugbreak();}while(0)
typedef size_t usize;
typedef uint8_t u8;
typedef unsigned int uint;
struct Raw_Input_Memory {
usize size;
usize used;
u8 *data;
};
#define raw_input_memory_push_array(memory, type, count) (type *)raw_input_memory_push_size(memory, sizeof(type) * count)
#define raw_input_memory_push_struct(memory, type) (type *)raw_input_memory_push_size(memory, sizeof(type))
void *raw_input_memory_push_size(Raw_Input_Memory *memory, usize size) {
assert(memory->used + size <= memory->size);
void *result = memory->data + memory->used;
memory->used += size;
return result;
}
int main() {
HINSTANCE module_instance = GetModuleHandle(NULL);
WNDCLASSA window_class = {};
window_class.lpfnWndProc = DefWindowProc;
window_class.hInstance = module_instance;
window_class.hCursor = LoadCursorA(NULL, IDC_ARROW);
window_class.lpszClassName = "Toplevel";
ATOM window_class_atom = RegisterClassA(&window_class);
if (window_class_atom) {
HWND window = CreateWindowA(window_class.lpszClassName, "Raw Input",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, module_instance, NULL);
if (window) {
Raw_Input_Memory raw_input_memory = {};
raw_input_memory.size = 1024 * 1024;
raw_input_memory.used = 0;
raw_input_memory.data = (u8 *)VirtualAlloc(NULL, raw_input_memory.size,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
RAWINPUTDEVICE raw_input_device;
raw_input_device.usUsagePage = 0x01;
raw_input_device.usUsage = 0x05;
raw_input_device.dwFlags = 0;
raw_input_device.hwndTarget = 0;
if (RegisterRawInputDevices(&raw_input_device, 1,
sizeof(RAWINPUTDEVICE))) {
MSG window_message = {};
while (GetMessageA(&window_message, NULL, 0, 0) > 0) {
UINT message_kind = window_message.message;
WPARAM wparam = window_message.wParam;
LPARAM lparam = window_message.lParam;
switch (message_kind) {
case WM_QUIT: {
break;
} break;
case WM_INPUT: {
if (raw_input_memory.data) {
raw_input_memory.used = 0;
UINT input_size;
if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &input_size,
sizeof(RAWINPUTHEADER)) != -1) {
RAWINPUT *input = (RAWINPUT *)raw_input_memory_push_size(&raw_input_memory,
input_size);
if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input,
&input_size, sizeof(RAWINPUTHEADER)) == input_size) {
assert(input->header.dwType == RIM_TYPEHID);
uint device_name_length = 128;
char *device_name = NULL;
device_name = raw_input_memory_push_array(&raw_input_memory, char,
device_name_length);
assert(device_name);
INT got_device_name = GetRawInputDeviceInfoA(input->header.hDevice,
RIDI_DEVICENAME,
&device_name,
&device_name_length);
if (got_device_name) {
/* ... */
}
}
}
}
} break;
default: {
TranslateMessage(&window_message);
DispatchMessageA(&window_message);
}
}
}
}
}
}
return 0;
}
Note that the call doesn't explicitly fail: it doesn't return -1 nor 0, but 83; and GetLastError returns 0.
I've tried both allocating the memory myself (as you can see in the code I posted) and passing in a NULL pointer, and both have the same result. I tried the wide version too, and I still get an 83 as a return value and a pointer to 0x005c003f005c005c, which is still invalid.
What am I doing wrong? Why does the function pass me invalid memory and how do I stop it from doing so? I've seen this answer, and this other answer, and read the docs. I don't know what else to do.
By the way, I'm trying it with a PS4 controller.
I am working on a C++ project based on COM, My Code has a function with an out parameter which takes an object as input and assigns a new instance of a class into it. But when I used CRT Debugging I found some memory leak in the function, Here is the code of the function.
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (compositionHost != nullptr)
{
*compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
if (compositionHost != nullptr)
{
return true;
}
}
else
{
return false;
}
return false;
}
This function takes an object of ICompositionHost and initialize it with CompositionHost object, Where should I need to free the memory to avoid memory leak.
I Call the function using ComPtr, but still there is memory leak
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd,host.GetAddressOf());
The Complete Code:
FluentCompositor.cpp
#include "pch.h"
#include "ICompositionHost.h"
#include "IFluentCompositor.h"
#include "CompositionHost.h"
#include "FluentCompositor.h"
FluentCompositor::FluentCompositor() :ref(1)
{
}
ulong __stdcall FluentCompositor::AddRef()
{
return (++ref);
}
ulong __stdcall FluentCompositor::Release()
{
if (--ref == 0)
{
delete this;
return 0;
}
return ref;
}
HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
if (iid == IID_IFluentCompositor || iid == IID_IUnknown)
{
*ppv = (void*)this;
AddRef();
}
else
{
*ppv = NULL;
}
return (*ppv == NULL) ? E_NOINTERFACE : S_OK;
}
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (compositor != nullptr)
{
*compositor = reinterpret_cast<void*>(new FluentCompositor());
if (compositor != nullptr)
{
return S_OK;
}
}
else
{
return E_INVALIDARG;
}
return E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (compositionHost != nullptr)
{
*compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
if (compositionHost != nullptr)
{
return true;
}
}
else
{
return false;
}
return false;
}
MainWindow.cpp
#include "pch.h"
#include "MainWindow.h"
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
const wchar_t className[] = L"Fluent Compositor";
WNDCLASS wc = {
.lpfnWndProc = WndProc,
.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase),
.hCursor = LoadCursor(nullptr, IDC_ARROW),
.lpszClassName = className,
};
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, className, L"Fluent Compositor Sample", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
if (hwnd == nullptr)
{
return 0;
}
CreateCompositionEffect(hwnd);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_CrtDumpMemoryLeaks();
return 0;
}
bool CreateCompositionEffect(HWND hwnd)
{
auto fluentCompositorLib = LoadLibrary(L"FluentCompositor.dll");
if (!fluentCompositorLib)
{
return false;
}
CreateCompositor = (CreateFluentCompositor)GetProcAddress(fluentCompositorLib, "CreateFluentCompositor");
if (!CreateCompositor)
{
return false;
}
CreateCompositor(&compositor);
if (compositor == nullptr)
{
return false;
}
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd,&host);
return true;
}
LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
MainWindow.h
#pragma once
#include "IFluentCompositor.h"
using namespace Microsoft::WRL;
extern "C" IMAGE_DOS_HEADER __ImageBase;
ComPtr<IFluentCompositor> compositor;
typedef BOOL(__stdcall* CreateFluentCompositor)(IFluentCompositor** compositor);
CreateFluentCompositor CreateCompositor;
bool CreateCompositionEffect(HWND hwnd);
LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
Your use of reinterpret_cast (and type-casts in general) is simply wrong. If your classes implement the correct interfaces, there is no need to cast them manually (except maybe in QueryInterface()), the compiler will perform the correct conversions implicitly for you.
Also, you are not checking the return value of new correctly. Or, for that matter, handling the possibility that new throws an exception on failure by default, not returns nullptr. If you want a nullptr on falilure, use the nothrow version of new instead.
Also, when using ComPtr, should should be using its overloaded operator& rather than its GetAddressOf() method. Especially if the ComPtr already holds an interface. GetAddressOf() will not release the interface (that is why there is a separate ReleaseAndGetAddressOf() method), but operator& will.
Try this instead:
#include <new>
HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
if (!ppv) return E_POINTER;
if (iid == IID_IFluentCompositor)
{
*ppv = static_cast<IFluentCompositor*>(this);
/* alternatively:
IFluentCompositor *comp = this;
*ppv = comp;
*/
}
else if (iid == IID_IUnknown)
{
*ppv = static_cast<IUnknown*>(static_cast<IFluentCompositor*>(this));
/* alternatively:
IFluentCompositor *comp = this;
IUnknown *unk = comp;
*ppv = unk;
*/
}
else
{
*ppv = nullptr;
}
if (!*ppv)
return E_NOINTERFACE;
AddRef();
return S_OK;
}
// similar for CompositionHost::QueryInterface() ...
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (!compositor) return E_POINTER; // not E_INVALIDARG
// make sure FluentCompositor has a refcount of 1 when created!
*compositor = static_cast<IFluentCompositor*>(new(std::nothrow) FluentCompositor);
return (*compositor) ? S_OK : E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (!compositionHost) return false;
// make sure CompositionHost has a refcount of 1 when created!
*compositionHost = new(std::nothrow) CompositionHost(hwnd);
return (*compositionHost);
}
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd, &host);
Alternatively, consider using ComPtr internally when creating your objects, eg:
#include <new>
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (!compositor) return E_POINTER; // not E_INVALIDARG
// make sure FluentCompositor has a refcount of 0 when created,
// as the ComPtr constructor will increment it!
ComPtr<IFluentCompositor> obj(new(std::nothrow) FluentCompositor);
return (obj) ? obj->QueryInterface(IID_IFluentCompositor, compositor) : E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
// make sure CompositionHost has a refcount of 0 when created,
// as the ComPtr constructor will increment it!
ComPtr<ICompositionHost> obj(new(std::nothrow) CompositionHost(hwnd));
return ((obj) && (obj->QueryInterface(IID_ICompositionHost, reinterpret_cast<void**>(compositionHost)) == S_OK));
}
It is a good idea to have your objects start with a reference count of 0 rather than 1, because they don't know if they are going to be used with interface pointers or object pointers (if they are even used with pointers at all). Don't increment an object's reference count unless it is actually assigned to an interface pointer that is AddRef()'ed and needs to be Release()'d.
I am trying to create a simple window but it does not work for some reason. I have actually tried this for the past few months. Even used some old code and it does not run at all for whatever reason.
Actually, the registerclass function does not return anything ever but this happened in the past too but I was still able to create a window. Nothing works this time
I tried:
Providing a valid value for all of the members of WNDCLASS
Running Debug/Release
Running as administrator
Using GetLastError (always returns 0)
Providing different class names, because I know some don't work for some reason, and also window names
This is my code:
WNDCLASSEXW lpClass = WNDCLASSEXW{ 0x00 };
lpClass.cbSize = sizeof(decltype(lpClass));
lpClass.style = (CS_HREDRAW | CS_VREDRAW);
lpClass.lpfnWndProc = ScreenProcess;
lpClass.hInstance = GetModuleHandleW(nullptr);
lpClass.lpszClassName = L"__TEST__";
lpClass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
RegisterClassExW(&lpClass);
if (HWND hwnd = CreateWindowExW(WS_EX_APPWINDOW, lpClass.lpszClassName, L"abc", WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, nullptr, nullptr, lpClass.hInstance, nullptr))
{
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
printf("%d", GetLastError());
for (;;) {};
Did you define your window procedure as something like this?
LRESULT CALLBACK ScreenProcess(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
And instead of the infinite loop, place this:
MSG msg{0};
while(GetMessage(&msg, nullptr, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Also, the cbSize of the WNDCLASSEXW should be sizeof(WNDCLASSEXW). Also change the CreateSolidBrush(RGB(0, 0, 0)); to GetStockObject(BLACK_BRUSH);
Hope this helps!
Here's a version of your code that's working in VS2017 (using C++17). I've added debug prints so that you can see ScreenProcess() receiving mouse movements etc. I've also added a class (WindowsClassRegistrator) for handling one of the resources you allocate to show how you can extend the existing C structs to handle releasing of resources automatically.
I added a mapping between common Windows messages and their macro names to make it easier to follow what you actually get into your WndProc. Unknown Windows messages are collected and displayed when you click close on the app (on the task bar) so you can extend the messages you'd like to handle/display as you go.
I also added assertion function templates for throwing exceptions with proper errors messages that you can use around all WinAPI functions that have an easy way of checking if they've succeeded or not.
#include "pch.h" // if you use precompiled headers
#include <Windows.h>
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <Olectl.h> // OCM_BASE
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <map>
#include <unordered_set>
// --- bug hunting support functions start ---
std::string GetLastErrorString() {
DWORD le = GetLastError();
LPSTR lpBuffer = nullptr;
if (FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, le, 0,
reinterpret_cast<LPSTR>(&lpBuffer),
0, NULL
))
{
std::string rv(lpBuffer);
LocalFree(lpBuffer);
return rv;
}
else return std::to_string(le);
}
struct win_error : public std::runtime_error {
win_error(const std::string& prefix) :
std::runtime_error(prefix + ": " + GetLastErrorString())
{}
};
// assert that a function does NOT return a specific value
template<typename T>
inline T AssertNEQ(T value, const char* funcname, T got_value) {
if (value == got_value) throw win_error(funcname);
return got_value;
}
// assert that a function DOES return a specific value
template<typename T>
inline T AssertEQ(T value, const char* funcname, T got_value) {
if (value != got_value) throw win_error(funcname);
return got_value;
}
// --- bug hunting support functions end ---
class WindowsClassRegistrator : public WNDCLASSEXW {
ATOM wca;
public:
WindowsClassRegistrator(WNDPROC lpfnWndProc) :
WNDCLASSEXW{ 0 }, wca{}
{
this->cbSize = sizeof(WNDCLASSEXW);
this->style = CS_SAVEBITS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
this->lpfnWndProc = lpfnWndProc;
this->hInstance =
AssertNEQ<HMODULE>(NULL, "GetModuleHandleW", GetModuleHandleW(nullptr));
this->lpszClassName = L"__TEST__";
this->hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
this->wca =
AssertNEQ<ATOM>(NULL, "RegisterClassExW", RegisterClassExW(this));
}
WindowsClassRegistrator(const WindowsClassRegistrator&) = delete;
WindowsClassRegistrator(WindowsClassRegistrator&&) = delete;
WindowsClassRegistrator& operator=(const WindowsClassRegistrator&) = delete;
WindowsClassRegistrator& operator=(WindowsClassRegistrator&&) = delete;
~WindowsClassRegistrator() {
AssertNEQ<BOOL>(FALSE,
"UnregisterClassW", UnregisterClassW(GetAtomAsStr(), this->hInstance));
}
inline LPCWSTR GetAtomAsStr() const noexcept {
return reinterpret_cast<LPCWSTR>(this->wca);
}
inline HINSTANCE GetInstance() const noexcept {
return this->hInstance;
}
inline LPCWSTR GetClassName() const noexcept {
return this->lpszClassName;
}
};
std::multimap<UINT, std::string> messages = {
{WM_NULL, "WM_NULL"},
{WM_CREATE, "WM_CREATE"},
{WM_DESTROY, "WM_DESTROY"},
{WM_MOVE, "WM_MOVE"},
{WM_SIZE, "WM_SIZE"},
{WM_ACTIVATE, "WM_ACTIVATE"},
{WM_SETFOCUS, "WM_SETFOCUS"},
{WM_KILLFOCUS, "WM_KILLFOCUS"},
{WM_PAINT, "WM_PAINT"},
{WM_CLOSE, "WM_CLOSE"},
{WM_QUIT, "WM_QUIT"},
{WM_ERASEBKGND, "WM_ERASEBKGND"},
{WM_SHOWWINDOW, "WM_SHOWWINDOW"},
{WM_ACTIVATEAPP, "WM_ACTIVATEAPP"},
{WM_CANCELMODE, "WM_CANCELMODE"},
{WM_SETCURSOR, "WM_SETCURSOR"},
{WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE"},
{WM_VKEYTOITEM, "WM_VKEYTOITEM"},
{WM_CHARTOITEM, "WM_CHARTOITEM"},
{WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING"},
{WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED"},
{SPI_SETDRAGHEIGHT, "SPI_SETDRAGHEIGHT"},
{WM_HELP, "WM_HELP"},
{WM_CONTEXTMENU, "WM_CONTEXTMENU"},
{WM_GETICON, "WM_GETICON"},
{WM_NCCREATE, "WM_NCCREATE"},
{WM_NCDESTROY, "WM_NCDESTROY"},
{WM_NCCALCSIZE, "WM_NCCALCSIZE"},
{WM_NCHITTEST, "WM_NCHITTEST"},
{WM_NCPAINT, "WM_NCPAINT"},
{WM_NCACTIVATE, "WM_NCACTIVATE"},
{SPI_GETDOCKMOVING, "SPI_GETDOCKMOVING"},
{WM_KEYDOWN, "WM_KEYDOWN"},
{WM_KEYUP, "WM_KEYUP"},
{WM_CHAR, "WM_CHAR"},
{WM_SYSKEYDOWN, "WM_SYSKEYDOWN"},
{WM_SYSKEYUP, "WM_SYSKEYUP"},
{WM_SYSCHAR, "WM_SYSCHAR"},
{WM_SYSCOMMAND, "WM_SYSCOMMAND"},
{WM_MOUSEMOVE, "WM_MOUSEMOVE"},
{WM_LBUTTONDOWN, "WM_LBUTTONDOWN"},
{WM_LBUTTONUP, "WM_LBUTTONUP"},
{WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK"},
{WM_RBUTTONDOWN, "WM_RBUTTONDOWN"},
{WM_RBUTTONUP, "WM_RBUTTONUP"},
{WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK"},
{WM_MBUTTONDOWN, "WM_MBUTTONDOWN"},
{WM_MBUTTONUP, "WM_MBUTTONUP"},
{WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK"},
{WM_MOUSEWHEEL, "WM_MOUSEWHEEL"},
{WM_XBUTTONDOWN, "WM_XBUTTONDOWN"},
{WM_XBUTTONUP, "WM_XBUTTONUP"},
{WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT"},
{WM_IME_NOTIFY, "WM_IME_NOTIFY"},
{WM_HOTKEY, "WM_HOTKEY"},
{0x0313, ": https://stackoverflow.com/questions/10430377/winapi-undocumented-windows-message-0x0313-stable"},
{WM_PRINT, "WM_PRINT"},
{WM_APPCOMMAND, "WM_APPCOMMAND"},
};
std::unordered_set<UINT> unmapped_messages;
std::map<WPARAM, std::string> mk_down = {
{MK_CONTROL, "MK_CONTROL"},
{MK_LBUTTON,"MK_LBUTTON"},
{MK_MBUTTON,"MK_MBUTTON"},
{MK_RBUTTON,"MK_RBUTTON"},
{MK_SHIFT,"MK_SHIFT"},
{MK_XBUTTON1,"MK_XBUTTON1"},
{MK_XBUTTON2,"MK_XBUTTON2"}
};
constexpr int colw = 40;
std::string message_maker(const char* macro, UINT uMsg, UINT offset) {
std::stringstream ss;
ss << macro << " + " << std::hex << (uMsg - offset) << " (" << uMsg << ")";
return ss.str();
}
inline void DisplayMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) {
std::string message;
if (uMsg < WM_USER) {
// there may be duplicate macros for some messages, so show all of them
auto[rangefirst, rangelast] = messages.equal_range(uMsg);
if (rangefirst == rangelast) {
// unmapped message found, store it
unmapped_messages.emplace(uMsg);
rangefirst = messages.emplace(uMsg, ": " + std::to_string(uMsg) + " -- UNMAPPED MESSAGE");
rangelast = rangefirst;
++rangelast;
}
message = rangefirst->second;
while (++rangefirst != rangelast) message += " " + rangefirst->second;
}
else {
// https://learn.microsoft.com/en-us/windows/desktop/winmsg/ocm--base
#define REGISTERED_WINDOWS_MESSAGE_BASE (0xC000)
#define SYSRESERVED_BASE (0x10000)
if (uMsg < OCM__BASE)
message = message_maker("WM_USER", uMsg, WM_USER);
else if (uMsg < WM_APP)
message = message_maker("(WM_USER) OCM__BASE", uMsg, OCM__BASE);
else if (uMsg < REGISTERED_WINDOWS_MESSAGE_BASE)
message = message_maker("WM_APP", uMsg, WM_APP);
else if (uMsg < SYSRESERVED_BASE)
message = message_maker("Registered Window Message", uMsg, REGISTERED_WINDOWS_MESSAGE_BASE);
else
message = message_maker("Reserved by the system", uMsg, SYSRESERVED_BASE);
}
std::cout << std::setw(colw) << std::hex << message << std::setw(18) << wParam
<< std::setw(12) << lParam << "\n";
}
LRESULT CALLBACK ScreenProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static std::string old_mouse_message;
switch (uMsg) {
case WM_MOUSEMOVE:
{
std::stringstream ss;
std::string new_mouse_message;
int xPos, yPos;
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
ss << std::setw(colw) << "WM_MOUSEMOVE" << std::dec
<< " x=" << std::setw(6) << xPos << " y=" << std::setw(6) << yPos;
for (auto&[wp, key] : mk_down)
if (wp&wParam) ss << " " << key;
new_mouse_message = ss.str();
if (new_mouse_message != old_mouse_message) {
std::cout << new_mouse_message << "\n";
old_mouse_message = std::move(new_mouse_message);
}
}
return 0;
case WM_NCHITTEST:
return HTCLIENT;
case WM_SETCURSOR:
return TRUE;
case WM_DESTROY:
std::cout << std::setw(colw) << "WM_DESTROY" << " ";
PostQuitMessage(0);
std::cout << "PostQuitMessage() done\n";
return 0;
default:
DisplayMsg(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int main() {
try {
WindowsClassRegistrator wcr(ScreenProcess);
// use WS_VISIBLE so that you don't have to call ShowWindow()
HWND hWnd =
AssertNEQ<HWND>(NULL, "CreateWindowExW",
CreateWindowExW(
WS_EX_APPWINDOW,
wcr.GetAtomAsStr(),
L"Title string",
WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
0, // width
0, // height
nullptr,
nullptr,
wcr.GetInstance(),
nullptr
)
);
MONITORINFO mi = { sizeof(mi) }; // mi.cbSize = sizeof(mi);
AssertNEQ<BOOL>(FALSE, "GetMonitorInfo",
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), &mi)
);
AssertNEQ<BOOL>(FALSE, "SetWindowPos",
SetWindowPos(hWnd, HWND_TOP,
mi.rcMonitor.left, mi.rcMonitor.top,
(mi.rcMonitor.right - mi.rcMonitor.left) / 4, // 1/4 of the screen width
(mi.rcMonitor.bottom - mi.rcMonitor.top), // height
SWP_NOOWNERZORDER | SWP_FRAMECHANGED)
);
// paint a rectangle in the window
AssertNEQ<BOOL>(FALSE, "Rectangle", Rectangle(
AssertNEQ<HDC>(NULL, "GetDC", GetDC(hWnd)),
10, 10, 100, 100)
);
MSG uMsg;
while (AssertNEQ<BOOL>(-1, "GetMessage", GetMessage(&uMsg, nullptr, 0, 0))) {
TranslateMessage(&uMsg); // assertion would depend on message type
DispatchMessage(&uMsg); // assertion would depend on message type
}
DisplayMsg(uMsg.message, uMsg.wParam, uMsg.lParam); // WM_QUIT
if (unmapped_messages.size()) {
std::cout << "\nYou have collected unmapped messages: \n";
for (const auto&[msg, text] : messages) {
std::cout << "/* 0x" << std::setw(4) << std::setfill('0') << msg << " */ ";
if (unmapped_messages.count(msg) || text[0] == ':') {
std::cout << "{0x" << std::setw(4) << std::setfill('0') << msg;
}
else {
std::cout << "{" << text;
}
std::cout << ", \"" << text << "\"},\n";
}
}
return static_cast<int>(uMsg.wParam);
}
catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << "\n";
}
return 1;
} // the registered windows class will be unregistered here, when wcr goes out of
// scope
Here's a modified, single source file version of the default Win32 project template that Visual Studio generates for you. I've just stripped out the stuff related to resources, accelerators, menus, and the About box thing.
#include <windows.h>
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[] = L"Test App"; // The title bar text
WCHAR szWindowClass[] = L"__TestAppWindowClass__"; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = szWindowClass;
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I'm writing code for what should be a tetris game. It's early on, and right now it only displays a single piece (the piece that would be "falling" at that point), which is all it should do. The up arrow allows you to cycle forward (specifically only) through a randomly generated sequence of pieces (using the "bag" method). And, using the left and right arrows, you can rotate the pieces. Anyways, it's coded on a win32 platform, and I've found that after a certain number of frames (running WM_PAINT) the main HDC turns null and everything stops. It takes roughly twice as many frames of holding down the right or left arrow keys to achieve this as it does to hold down the up arrow key. Something odd is that after about 1000 frames the area of the console that I'm actively painting over (a 600x600 px frame) will go black (the hdc has not been nullified yet), and it's only when the up arrow is pressed that the hdc is nullified. What I suspect to be the problem is that the methods called when the up arrow key is pressed pass the hdc as a parameter to the in-class methods (tell me if this is poor practice or there's something I should be doing instead). I think the hdc might be corrupting or something (honestly I have no idea). The left and right arrow keys don't directly call methods with HDC's as a parameter. Because of case labels and how the win32 template is designed, I have to store an HDC who's scope falls out of any case-label, so that I can access the window's handle outside of the paint case (I feel like this is poor practice, but I'd like to understand why before I go out of my way to find a new way). I'll post the code, the stored HDC is called mainHDC, and it's defined just before the main case statement. To clue you in, I'll give an overview of the code structure:
The main .cpp file, which contains the basic win32 program, calls the Tetris class constructor in WM_PAINT and stores it universally like the mainHDC is and, when prompted, the "next" method (which brings the next piece), the "turnPiece" method (which rotates the piece clockwise or counter-clockwise based on the paramter), and the "paint" method which updates the screen, repainting the current piece. In the tetris class (which is in it's own header file), there is a sub-class called "pieces" which contain information about its objects, that are defined by another level of sub-classes named with a single character depicted by the pieces shape. The shape, color, and size of the pieces are stored in a 2d array of pointers (using COLORREF). "Pieces" contains its own "DrawObject" method which draws the object that calls it (which like all of the drawing/painting methods have an HDC as an argument). There's also a method that rotates the shape called "turnTet" (the "turnPiece" method relays the call from the main .cpp file to "pieces"). The only other applicable methods are those found in the "tetris" class which are "paint" and "next" (they ultimately paint the object). The WM_KEY cases, excluding the VK_UP case, don't use the saved hdc but rather use InvalidateRect().
Here's the applicable part of the .cpp file
int tempt = 2;
int tempvar = 0;
tetris *mainOBJ;
HDC mainHDC;
HDC testHDC;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
mainOBJ->turnPiece(false);
InvalidateRect(hWnd, 0, FALSE);
break;
case VK_RIGHT:
mainOBJ->turnPiece(true);
InvalidateRect(hWnd, 0, FALSE);
break;
case VK_UP:
mainOBJ->next(mainHDC);
InvalidateRect(hWnd, 0, FALSE);
break;
}
break;
case WM_COMMAND:
//Non-applicable & has not been changed
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
mainHDC = hdc;
HPEN oldP;
HPEN newP;
COLORREF qLC;
qLC = RGB(0, 0, 0);
newP = CreatePen(PS_SOLID, 1, qLC);
oldP = (HPEN) SelectObject(hdc, newP);
tempt++;
//USED FOR COUNTING FRAMES WITH DEBUGGER
if (tempt % 1 == 0) {
if (tempvar == 0) {
//CONSTRUCTOR CALL
mainOBJ = new tetris(hdc);
tempvar++;
}
//PAINT METHOD CALL
mainOBJ->paint(hdc);
}
if (hdc == NULL) {
int x = 3;
int y = x + 3; //SET DEBUG_BREAK POINT HERE
}
testHDC = hdc;
SelectObject(hdc, oldP);
DeleteObject(newP);
EndPaint(hWnd, &ps);
}
break;
}
}
The header file, which contains the Tetris and piece classes
class tetris {
public:
class pieces {
private:
COLORREF **test;
int size;
public:
//Abbreviated for space
class O {};
class I {};
class S {};
class Z {};
class T {};
class L {};
class J {};
void setSize(int a) {
//Initializing the piece-array
test = new COLORREF*[a];
size = a;
int i = 0;
while (i < a) {
test[i] = new COLORREF[a];
i++;
}
}
void setShape(COLORREF **shape) {
test = shape;
}
void setColor(HDC hdc, COLORREF rgb) {
HPEN Penn = CreatePen(PS_SOLID, 1, rgb);
HPEN Peno = (HPEN)SelectObject(hdc, Penn);
}
static pieces getObject(char type) {
pieces Gen;
switch (type) {
case 'O':
{
//Identical (almost) to the other cases
O pcs = O();
Gen = *pcs.getObject();
return Gen;
}
break;
case 'I':
case 'S':
case 'Z':
case 'T':
case 'L':
case 'J':
return pieces();
}
void turnTet(bool clockwise) {
int i = 0;
int s;
COLORREF **shape;
int ter = size - 1;
shape = new COLORREF*[2];
while (i < size) {
shape[i] = new COLORREF[2];
s = 0;
while (s < size) {
shape[i][s] = def;
s++;
}
i++;
}
i = 0;
while (i < size) {
s = 0;
while (s < size) {
if (clockwise) {
shape[s][ter - i] = test[i][s];
}
else {
shape[ter - s][i] = test[i][s];
}
s++;
}
i++;
}
test = shape;
}
void drawObject(HDC hWnd) {
int i = 0;
int s;
while (i < size) {
s = 0;
while (s < size) {
setColor(hWnd, test[i][s]);
int scaleOfBox = 90;
DrawBox((s + 1) * scaleOfBox, (i + 1) * scaleOfBox, scaleOfBox - 1, scaleOfBox - 1, hWnd);
s++;
}
i++;
}
}
void DrawBox(int x, int y, int w, int h, HDC hdc) {
if (h < 0) {
h *= -1;
}
if (w < 0) {
w *= -1;
}
int i = 0;
while (i < h) {
MoveToEx(hdc, x, y + i, NULL);
LineTo(hdc, x + w, y + i);
i++;
}
}
};
tetris(HDC hdc) {
refresh();
bagp[cur].drawObject(hdc);
}
void next(HDC hdc) {
bagp[cur].DrawBox(0, 0, 600, 600, hdc);
bagp[cur].drawObject(hdc);
cur++;
if (cur > 6) {
refresh();
}
}
void turnPiece(bool clockwise) {
bagp[cur].turnTet(clockwise);
}
void refresh() {
srand(time(NULL));
bag[0] = rand() % 7;
int i = 1;
while (i < 7) {
bool open = false;
cur = i;
while (!open) {
bag[i] = rand() % 7;
int s = 1;
open = true;
while (s <= i) {
if (bag[i] == bag[i - s]) {
open = false;
}
s++;
}
}
i++;
}
cur = 0;
while (cur < 7) {
switch (bag[cur]) {
case 0:
bagp[cur] = pieces::getObject('O');
break;
case 2:
bagp[cur] = pieces::getObject('T');
break;
case 1:
bagp[cur] = pieces::getObject('I');
break;
case 3:
bagp[cur] = pieces::getObject('S');
break;
case 4:
bagp[cur] = pieces::getObject('Z');
break;
case 5:
bagp[cur] = pieces::getObject('L');
break;
case 6:
bagp[cur] = pieces::getObject('J');
break;
}
cur++;
}
cur = 0;
}
void paint(HDC hdc) {
COLORREF temp = def;
bagp[cur].setColor(hdc, temp);
bagp[cur].DrawBox(0, 0, 600, 600, hdc);
bagp[cur].drawObject(hdc);
}
private:
int bag[7];
int cur;
pieces bagp[7];
};
I don't understand why the HDC nullifies like it does. Once again I suspect it's related to how the hdc is passed as a parameter or maybe how I'm saving the hdc. Help...please (and thank you).
Ooh, old school windows programming...
This may not be the entirety of the problem, but in your setColor function you're creating a Pen with CreatePen but never calling DeleteObject on it. This will leak the resource and eventually cause problems when Windows runs out of resource handles for objects.
Also, the Device Context returned by BeginPaint is (generally) only valid until EndPaint is called (unless you specify otherwise in the CreateWindow call, if I remember rightly). This can be another source of handle leakage.
In the Process tab of Task Manager, you can add the "GDI objects" column which will have an increasing number as your program runs if you're leaking handles. I saw this once with something I wrote way back when.