This question already has answers here:
Why callback functions needs to be static when declared in class
(6 answers)
Closed 6 years ago.
I am attempting to write a class to handle the creation of a button and it's messages internally. I wish to keep all of the code contained within the class itself. This may be simple or impossible as I am relatively new to Winapi and C++, having used vb.net much more extensively. I was unable to find an example that accomplished this seeming simple conversion. I did find examples suggesting i use an alternate API, or use SetWindowLongPtr, I am told that SetWindowSubclass is the better option. My attempted code is below:
#pragma once
#include <iostream>
#include <Windows.h>
#include <Commctrl.h>
using namespace std;
class button {
public:
bool initialize();
buttton(int x, int y, int length, int width, LPCWSTR text, HWND parent, HINSTANCE hInstance); // This is the constructor
private:
LPCWSTR text = L"Button";
int height = 25;
int width = 100;
int x = 0;
int y = 0;
HWND parent;
HWND thisHandle;
HINSTANCE thisInstance;
bool initialized = false;
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
button::button(int x, int y, int height, int width, LPCWSTR text, HWND parent, HINSTANCE hInstance) {
this->x = x;
this->y = y;
this->height = height;
this->width = width;
this->text = text;
this->parent = parent;
thisInstance = hInstance;
}
bool button::initialize()
{
thisHandle = CreateWindowW(
L"BUTTON", // Predefined class; Unicode assumed
text, // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_CLIPSIBLINGS | BS_NOTIFY, // Styles
x, // x position
y, // y position
width, // Button width
height, // Button height
parent, // Parent window
NULL, // No ID.
thisInstance,
NULL); // Pointer not needed.
if (!thisHandle)
{
return false;
}
initialized = true;
//Problem Code****
SetWindowSubclass(thisHandle, mySubClassProc, 1, 0);
//****
return true;
}
LRESULT CALLBACK button::mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
//Code handling messages here.
}
}
This throws the error :
argument of type "LRESULT (__stdcall button::*)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)" is incompatible with parameter of type "SUBCLASSPROC"
I am hoping that someone can explain a simple solution, or suggest an alternative in Winapi, as i wish to learn native windows and C++ and not rely on additional libraries.
You are trying to use a non-static class method as the subclass callback. That will not work, because a non-static class method has a hidden this parameter that the API will not be able to pass.
To do what you are attempting, you must remove the this parameter (you can use the dwRefData parameter of SetWindowSubclass() instead), by either:
using a static class method:
class button {
public:
bool initialize();
...
private:
...
static LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
bool button::initialize()
{
...
SetWindowSubclass(thisHandle, mySubClassProc, 1, (DWORD_PTR) this);
return true;
}
LRESULT CALLBACK button::mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
button *pThis = (button*) dwRefData;
//Code handling messages here.
}
using a non-member function:
class button {
public:
bool initialize();
...
};
LRESULT CALLBACK myButtonSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
button *pBtn = (button*) dwRefData;
//Code handling messages here.
}
bool button::initialize()
{
...
SetWindowSubclass(thisHandle, myButtonSubClassProc, 1, (DWORD_PTR) this);
return true;
}
Related
I wanted to create a class that I will be used to create a static control. The problem is when subclassing the control, the callback should be static which means I won't be able to access non-static variables and function inside the callback.
I've been pulling my hair to make my code works but still no success.
MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(){
non_static_variable = 0; //default is zero.
}
LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
char buffer[5];
sprintf_s(buffer, "The value of non_static_variable is: %d \n", pThis->non_static_variable);
OutputDebugStringA(buffer);
switch (uMsg) {
case WM_PAINT: {
// do nothing for now
return 0;
}
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
void MyClass::CreateStaticControl(HWND WindowHandle) {
StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this);
SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
}
void MyClass::SetValue(int value){
non_static_variable = value; //test if I can access this in callback
}
MyClass.h
#include "header.h"
public:
MyClass();
void CreateStaticControl(HWND window);
void SetValue(int value);
static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
private:
int non_static_variable;
HWND StaticControl;
};
Then call the class inside my WM_CREATE from my main window procedure, I used it like this.
...
case WM_CREATE:{
MyClass myClass;
myClass.CreateStaticControl(hwnd);
myClass.SetValue(888);
break;
}
You might think that this is a duplicate of others, I found a lot of them but since this is about subclassing and I don't have access to WM_NCCREATE I can't properly set the pointer of my class. Can anyone help me?
EDIT: I put the exact code I have.
You can use the dwRefData parameter of SetWindowSubclass() to pass the this pointer to the dwRefData parameter of the callback. You can then type-cast that parameter to access non-static members. No need to use GWLP_USERDATA at all (especially since you are not using the callback's lParam correctly when setting GWLP_USERDATA).
Try this instead:
MyClass.h
class MyClass {
public:
MyClass();
void CreateStaticControl(HWND window);
static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
private:
int non_static_variable;
HWND StaticControl;
};
MyClass.cpp:
void MyClass::CreateStaticControl(HWND WindowHandle) {
StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this);
SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
}
LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
switch (uMsg) {
case WM_PAINT: {
// use pThis->non_static_variable as needed...
return 0;
}
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
I have been working with C++ and Win32 (non MFC/ATL) I am playing around with writing my own class library to wrap certain Win32 objects (HWNDs in particular).
When it comes to creating windows, I find the "RegisterClassEx / CreateWindowEx" method very awkward. This design makes it hard to write simple class wrappers (one must resort to thunks, or TLS or some other complicated mechanism).
It seems to me it would have been simpler to just let the application specify the window procedure and a user-data pointer at window creation time.
Is there any obvious reason I'm missing for the design choice here? Is there a really simple and efficient way for this to work?
ATL's CWindow and CWindowImpl are your friends.
CWindowImpl makes takes care of the RegisterClass/CreateWindow awkwardness that you speak of.
CWindow is a basic "wrapper" class for an HWND with all the win32 functions abstracted out.
The reason I prefer ATL over MFC. ATL is a very lightweight set of classes with all the source code provided. It's a simple #include with no extra libraries or runtimes to deal with. After rolling my own WndProcs and window encapsulation classes for many years, I've found CWindowImpl a joy to work with. You have to declare a global AtlModuleExe instance in your code to use it, but besides that, ATL stays out of the way.
Links to documenation for these classes below:
CWindow:
http://msdn.microsoft.com/en-us/library/d19y607d.aspx
CWindowImpl:
http://msdn.microsoft.com/en-us/library/h4616bh2.aspx
Update: Here's some sample code I dug up for you:
class CMyApp : public CAtlExeModuleT<CMyApp>
{
public:
static HRESULT InitializeCom()
{
CoInitialize(NULL);
return S_OK;
}
};
CMyApp g_app;
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
CMyWindow();
~CMyWindow();
BEGIN_MSG_MAP(CMyWindow)
MESSAGE_HANDLER(WM_PAINT, OnPaint);
MESSAGE_HANDLER(WM_CLOSE, OnClose);
END_MSG_MAP();
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled);
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled);
};
LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
// your WM_PAINT code goes here
}
LRESULT CMyWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
PostQuitMessage();
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdline, int nCmdShow)
{
// 640x480 window
CMyWindow appwindow;
RECT rect = {0, 0, 640, 480};
RECT rectActual = {0};
appwindow.Create(NULL, rect, L"App Window", WS_OVERLAPPEDWINDOW);
appwindow.ShowWindow(SW_SHOW);
{
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// app shutdown
appwindow.DestroyWindow();
return 0;
}
Resort to thunks or tls? I dont know what you mean by a thunk in this case, but its quite easy - if just a little convoluted - to bootstrap a window into a c++ class wrapper.
class UserWindow
{
HWND _hwnd;
public:
operator HWND(){
return _hwnd;
}
UserWindow():_hwnd(0){}
~UserWindow(){
if(_hwnd){
SetWindowLongPtr(GWL_USERDATA,0);
DestroyWindow(_hwnd);
}
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
UserWindow* self = 0;
if(uMsg == WM_CREATE)
{
LPCREATESTRUCT crst = (LPCREATESTRUCT)lParam;
self = (Window*)crst->lpCreateParams;
SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)self);
self->_hwnd = hwnd;
}
else
self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA);
if(self){
LRESULT lr = self->WndProc(uMsg,wParam,lParam);
if(uMsg == WM_DESTROY){
if(self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA))
self->_hwnd = NULL;
}
return lr;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
HWND Create(int x, int y, int w, int h, LPCTSTR pszTitle,DWORD dwStyle,DWORD dwStyleEx,LPCTSTR pszMenu,HINSTANCE hInstance, HWND hwndParent){
WNDCLASSEX wcex = { sizeof (wcex),0};
if(!GetClassInfo(hInstance,ClassName(),&wcex)){
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WindowndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.lpszClassName = ClassName();
OnCreatingClass( wcex );
RegisterClassEx(&wcex);
}
return CreateWindowEx( dwStyleEx, ClassName(), pszTitle, dwStyle, x, y, w, h, hwndParent, pszMenu, hInstance, this);
}
// Functions to override
virtual LPCTSTR ClassName(){
return TEXT("USERWINDOW");
}
virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam,LPARAM lParam){
return DefWindowProc(uMsg,wParam,lParam);
}
virtual void Window::OnCreatingClass(WNDCLASSEX& wcex){
wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
}
};
It is all a bit convoluted, but it means that the window can be destroyed safely by deleting the class, OR by being destroyed. There are one or two sizing related messages sent during the call to CreateWindow before WM_CREATE sets GWL_USERDATA to "this" but practically they are of no consequence. The window class is automatically created the first time the window is instantiated.
One thing this style of automatic class registration on the first call to create does not support is the instantiation of this type of window as a control on a dialog - To support that case a whole slew of things would need to be changed... provide a static class registration function... a "new MyClass" in the static WM_CREATE handler... its not obvious to me how this could be done in a frameworkish type fashion.
So I'm writing my own little GUI framework, wrapping the Windows API in useful classes. I'm currently trying to have the user handle WndProc's messages in an object-oriented way, but I've hit a bit of a snag.
I have a Button class, that inherits from an abstract Control class, that is pretty much a wrapper around the Button's HWND handle.
There's also a Window class, that contains (what do you know) a handle to a window. This class also has a container of Controls, and is responsible for creating its own (static) WndProc method.
What I'm trying to do is have a big switch statement in the containing window's WndProc that, based on the function's wParam and lParam parameters, calls the appropriate sending control's handling function. The way I've seen most people do something like it is this:
#define MY_BUTTON 101 //Define button's ID
Button::Button()
{
hwndButton= CreateWindow(
"BUTTON", text,
WS_VISIBLE | WS_CHILD,
position.x, position.y,
size.x, size.y,
parent_handle,
(HMENU)MY_BUTTON,
GetModuleHandle(NULL),
NULL);
}
Button* button = new Button();
//Later on, in the Window class...
LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch((LOWORD)wParam)
{
case MY_BUTTON:
button->HandleMessage(&msg);
break;
}
}
However, I don't want the user to assign a unique integer for every object instance they create.
Rather, since I have a container of controls, I would rather do something like this:
//In the Window Class...
Button* button = new Button();
AddControl(button);
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(&msg)
{
ObtainControlFromParams(wParam, lParam)->HandleMessage(&msg);
//ObtainControlFromParams should return a pointer to a Control,
//and it should look into the window's Control collection.
}
}
For as great as this sounds, I can't find a way to implement the ObtainControlFromParams function.
The way I thought of to distinguish every control instance is to have a string, that I call the control's "name", that should be unique to every object instantiation. This would leave me with two options (that I can think of)
Convert the string to a hash, store it somewhere else (such as in a containing window) and use that as the Button's HMENU. I could then use a hash set to link each string hash to its owner object, and call the corrisponding method in that way.
Use the lParam method, that (to the best of my knowledge) stores the button's HWND handle, and do something with that instead.
I apologize if what I'm trying to obtain isn't really clear, it's kind of a complicated concept to me.
Any help is greatly appreciated!
The usual approach is to reflect messages like WM_COMMAND and WM_NOTIFY, which are sent to the control's parent window, back to the control that sent them. This is possible because these messages identify the sender (each in a unique way, so check the docs).
There are a number of ways to associate a C++ object pointer with a window:
Dynamically generated trampoline function as window-proc.
Most efficient but also most tricky. Probably needs to use VirtualAlloc.
Window properties.
The SetProp and GetProp functions.
Window object words.
SetWindowLongPtr. Needs to be sure there's allocated space.
Static map.
E.g. a singleton std::unordered_map, mapping handle → object.
Whatever's used by Windows standard subclassing.
I.e. using SetWindowSubclass.
Store the button's Control* object pointer in the button HWND itself where the message procedure can retrieve it. You can use (Set|Get)WindowLongPtr(GWL_USERDATA) or (Get|Set)SetProp() for that purpose.
Only certain messages, like WM_COMMAND and WM_NOTIFY, identify the control that sends them. These messages are sent to the control's parent window. These messages contain the child control's HWND. The parent can retrieve the child's Control* and forward the message to it.
Other messages are sent directly to the control's own window, not its parent window. These messages do not identify the control they are being sent to. As such, each Control needs its own individual WndProc procedure for its own HWND. Use SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubclass() to assign that WndProc to the HWND after CreateWindow() is called.
Try something like this (this is very rough, there is a lot more involved in wtiting a UI framework, but this should give you some ideas):
typedef std::basic_string<TCHAR> tstring;
class Control
{
private:
HWND fWnd;
Control *fParent;
POINT fPosition;
SIZE fSize;
tstring fText;
std::list<Control*> fControls;
...
void addControl(Control *c);
void removeControl(Control *c);
virtual void createWnd() = 0;
void internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle);
...
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
...
public:
Control();
virtual ~Control();
HWND getWnd();
void destroyWnd();
void wndNeeded();
Control* getParent();
void setParent(Control *value);
POINT getPosition();
void setPosition(POINT value);
SIZE getSize();
void setSize(SIZE value);
tstring getText();
void setText(const tstring &value);
...
};
static const LPTSTR szControlPropStr = TEXT("ControlPtr")
Control* ControlFromWnd(HWND hwnd)
{
return (Control*) GetProp(hwnd, szControlPropStr);
}
Control::Control()
: fWnd(0), fParent(0)
{
fPosition.x = fPosition.y = 0;
fSize.cx = fSize.cy = 0;
...
}
Control::~Control()
{
setParent(0);
destroyWnd();
}
void Control::addControl(Control *c)
{
fControls.push_back(c);
c->fParent = this;
if (fWnd)
c->wndNeeded();
}
void Control::removeControl(Control *c)
{
fControls.remove(c);
c->destroyWnd();
c->fParent = 0;
}
HWND Control::getWnd()
{
wndNeeded();
return fWnd;
}
void Control::internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle)
{
style |= WS_VISIBLE;
if (fParent)
style |= WS_CHILD;
fWnd = CreateWindowEx(exstyle,
ClassName, fText.c_cstr(), style,
fPosition.x, fPosition.y,
fSize.cx, fSize.cy,
(fParent) ? fParent->getWnd() : 0,
0,
GetModuleHandle(NULL),
NULL);
SetProp(fWnd, szControlPropStr, (HANDLE)this);
SetWindowLongPtr(fWnd, GWL_WNDPROC, (LONG_PTR)&Control::WndProc);
}
void Control::destroyWnd()
{
DestroyWindow(fWnd);
fWnd = 0;
}
void Control::wndNeeded()
{
if (!fWnd)
{
createWnd();
for (std::list<Control*>::iterator iter = fControls.begin(); iter != fControls.end(); ++iter)
iter->wndNeeded();
}
}
Control* Control::getParent()
{
return fParent;
}
void Control::setParent(Control *value)
{
if (fParent != value)
{
if (fParent)
fParent->removeControl(this);
fParent = value;
if (fParent)
fParent->addControl(this);
}
}
POINT Control::getPosition()
{
return fPosition;
}
void Control::setPosition(POINT value)
{
fPosition = value;
if (fWnd)
SetWindowPos(fWnd, 0, fPosition.x, fPosition.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
SIZE Control::getSize()
{
return fSize;
}
void Control::setSize(SIZE value)
{
fSize = value;
if (fWnd)
SetWindowPos(fWnd, 0, 0, 0, fSize.cx, fSize.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}
tstring Control::getText()
{
return fText;
}
void Control::setText(const tstring &value)
{
fText = value;
if (fWnd)
SetWindowText(fWnd, fText.c_str());
}
LRESULT CALLBACK Control::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Control *pThis = ControlFromWnd(hwnd);
if (pThis)
return pThis->HandleMessage(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT Control::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
WM_NCDESTROY:
{
RemoveProp(fWnd, szControlPropStr);
fWnd = 0;
break;
}
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if ((hwnd) && (hwnd != fWnd))
{
Control *c = ControlFromWnd(hwnd);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_NOTIFY:
{
NMHDR *hdr = (NMHDR*) lParam;
if ((hdr->hwndFrom) && (hdr->hwndFrom != fWnd))
{
Control *c = ControlFromWnd(hdr->hwndFrom);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS *p = (WINDOWPOS*) lParam;
if (!(p->flags & SWP_NOMOVE))
{
fPosition.x = p->x;
fPosition.y = p->y;
}
if (!(p->flags & SWP_NOSIZE))
{
fSize.cx = p->cx;
fSize.cy = p->cy;
}
...
return 0;
}
case WM_SETTEXT:
{
LRESULT ret = DefWindowProc(fWnd, uMsg, wParam, lParam);
if (ret == TRUE)
fText = (TCHAR*) lParam;
return ret;
}
...
}
return DefWindowProc(fWnd, uMsg, wParam, lParam);
}
class Button : public Control
{
protected:
virtual void createWnd();
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
Button();
};
Button::Button()
: Control()
{
}
void Button::createWnd()
{
Control::intetnalCreateWnd(TEXT("BUTTON"), BS_PUSHBUTTON | BS_TEXT, 0);
...
}
LRESULT Button::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if (hwnd != fWnd)
break;
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
...
return 0;
}
...
}
break;
}
}
return Control::HandleMessage(uMsg, wParam, lParam);
}
//In the Window Class...
Button* button = new Button();
button->setPosition(...);
button->setSize(...);
button->setText(...);
button->setParent(this);
I want to know if there is a way i could write this code shorter, if there is anyway to make a #define that can shorten the way im doing my if statements in the message switch.
I check if i have set up a function if there is i then call it
This is just apart of my wndproc it is alot bigger
LRESULT Base::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch (uMsg)
{
case WM_CREATE:
{
if (this->onCreate != NULL)
{
if (onCreate(hwnd, (LPCREATESTRUCT)lParam))
return 1;
}
}break;
case WM_DESTROY:
{
if (onDestroy != NULL)
{
if (onDestroy(hwnd))
return 1;
}
this->Destroy();
}break;
case WM_SIZE:
{
if (onSize != NULL)
{
if (onSize(hwnd, wParam, lParam))
return 1;
}
}break;
case WM_CLOSE:
{
if (onClose != NULL)
{
if (onClose(hwnd))
return 1;
}
}break;
default:
{
}break;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
pointers defined like this
LRESULT(*onCreate) (HWND, LPCREATESTRUCT);
I then add them like this
LRESULT onCreate(HWND, LPCREATESTRUCT)
{
return true;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR pCmdLine, int nCmdShow)
{
Window mainWindow;
mainWindow.onCreate = onCreate;
return 0;
}
Use the message-cracker macros defined in <WindowsX.h>. This won't actually make the code run any faster, and it doesn't really result in a net loss of lines of code, but it certainly makes the code easier to read, which is the important part. Nobody wants to look at a 1000-line switch statement. Plus, these macros extract the parameters for each message from WPARAM and LPARAM, enhancing readability, reducing mistakes and simplifying what you have to remember.
The macros are pretty simple and anything but high-tech (they originated way back in the 16-bit Windows days):
#define HANDLE_MSG(hwnd, message, fn) \
case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
But they are pre-defined for all of the most common messages (you can add your own for the two or three messages that they omitted if you find that you need them), so you don't have to deal with the ugliness. You just get nice, readable code:
#include <Windows.h>
#include <WindowsX.h> // for the message cracker macros
...
void Base::OnClose(HWND hWnd)
{
// ...
}
BOOL Base::OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
// ...
return TRUE;
}
void Base::OnDestroy(HWND hWnd)
{
// ...
}
void Base::OnSize(HWND hWnd, UINT state, int cx, int cy)
{
// ...
}
LRESULT Base::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
HANDLE_MSG(hWnd, WM_CLOSE, OnClose);
HANDLE_MSG(hWnd, WM_CREATE, OnCreate);
HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hWnd, WM_SIZE, OnSize);
// TODO: Add more message crackers here to handle additional messages.
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
The header file also contains comments showing the signature of the handler function. But check out Hernán Di Pietro's Message Cracker Wizard to make your life even easier. It lists all of the messages, allows you to filter them down to the ones you're looking for, and will automatically copy the template code to the clipboard!
To avoid the definition of all possible specific (virtual) message-handlers in the base class, you may have a map of handlers:
#include <unordered_map>
// Mokup windows.h
typedef intptr_t HWND;
typedef intptr_t LRESULT;
typedef intptr_t WPARAM;
typedef intptr_t LPARAM;
typedef unsigned UINT;
enum {
WM_CREATE
};
// Base
class WindowBase
{
public:
virtual ~WindowBase() {}
LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// The complexity of finding an element in an unordered map
// is amortized O(1).
auto kv = window_procedures.find(uMsg);
if(kv != window_procedures.end()) {
auto procedure = kv->second;
return (this->*procedure)(hwnd, uMsg, wParam, lParam);
}
return 0; //::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
protected:
typedef LRESULT (WindowBase::*window_procedure)(HWND, UINT msg, WPARAM, LPARAM);
template <typename Procedure>
void register_window_procedure(UINT msg, Procedure procedure) {
window_procedures[msg] = static_cast<window_procedure>(procedure);
}
private:
std::unordered_map<UINT, window_procedure> window_procedures;
};
// Test
#include <iostream>
class Window : public WindowBase
{
public:
Window() {
register_window_procedure(WM_CREATE, &Window::onCreate);
}
protected:
LRESULT onCreate(HWND, UINT msg, WPARAM, LPARAM) {
std::cout << "onCreate\n";
return 0;
}
};
int main() {
Window w;
WindowBase* p = &w;
p->WindowProc(0, WM_CREATE, 0, 0);
}
Note: This is not following your example using a freestanding message handler. If you want that, you may typedef LRESULT (*window_procedure)(HWND, UINT msg, WPARAM, LPARAM); and adjust the code accordingly. However, the function signature should include an extra argument (e.g.: WindowBase*) to keep the context (besides HWND), in which the message is invoked.
I am attempting to add a low level mouse hook to a class. I am able to do so by placing this function in my CPP file:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
Then, I set up the hook in the class constructor like so:
HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
This works fine for intercepting mouse events, however since the callback function is not defined in my class, it does not have access to any of my class variables.
Therefore, I tried defining the callback function in my header file like so:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
and in my CPP file like this (TMainForm being the class):
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
However, when I attempt to compile like this, I get the following errors:
[bcc32 Error] MainFormU.cpp(67): E2034 Cannot convert 'long (__stdcall * (_closure )(int,unsigned int,long))(int,unsigned int,long)' to 'long (__stdcall *)(int,unsigned int,long)'
[bcc32 Error] MainFormU.cpp(67): E2342 Type mismatch in parameter 'lpfn' (wanted 'long (__stdcall *)(int,unsigned int,long)', got 'void')
What exactly am I doing wrong here? How is the method now different since I have made it a part of my TMainForm class?
You cannot use a non-static class methods as the callback. Non-static methods have a hidden this parameter, thus the signature of the callback does not match the signature that SetWindowsHookEx() is expecting. Even if the compiler allowed it (which can only be done with casting), the API would not be able to account for the this parameter anyway.
If you want to make the callback be a member of the class (so it can access private fields and such), it has to be declared as static to remove the this parameter, but then you will have to use the form's global pointer to reach it when needed, eg:
class TMainForm : public TForm
{
private:
HHOOK hMouseHook;
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
void MouseHook(int nCode, WPARAM wParam, LPARAM lParam);
public:
__fastcall TMainForm(TComponent *Owner);
__fastcall ~TMainForm();
};
extern TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent *Owner)
: TForm(Owner)
{
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, &MouseHookProc, NULL, 0);
}
__fastcall TMainForm::~TMainForm()
{
if (hMouseHook)
UnhookWindowsHookEx(hMouseHook);
}
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MainForm->MouseHook(nCode, wParam, lParam);
return CallNextHookEx(0, nCode, wParam, lParam);
}
void TMainForm::MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
// my hook code here
}
With that said, you should consider using the Raw Input API instead of SetWindowsHookEx(). The LowLevelMouseProc documentation even says so:
Note Debug hooks cannot track this type of low level mouse hooks. If the application must use low level hooks, it should run the hooks on a dedicated thread that passes the work off to a worker thread and then immediately returns. In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can. For more information on raw input, see Raw Input.
Using Raw Input, the mouse will send WM_INPUT messages directly to your window.
If you are using VCL, you can override the virtual WndProc() method to handle the WM_INPUT message, no static method needed:
class TMainForm : public TForm
{
protected:
virtual void __fastcall CreateWnd();
virtual void __fastcall WndProc(TMessage &Message);
};
void __fastcall TMainForm::CreateWnd()
{
TForm::CreateWnd();
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = this->Handle;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
void __fastcall TMainForm::WndProc(TMessage &Message)
{
if (Message.Msg == WM_INPUT)
{
HRAWINPUT hRawInput = (HRAWINPUT) Message.LParam;
UINT size = 0;
if (GetRawInputData(hRawInput, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) == 0)
{
LPBYTE buf = new BYTE[size];
if (GetRawInputData(hRawInput, RID_INPUT, buf, &size, sizeof(RAWINPUTHEADER)) != 0)
{
RAWINPUT *input = (RAWINPUT*) buf;
// use input->data.mouse or input->data.hid as needed...
}
delete[] buf;
}
}
TForm::WndProc(Message);
}
If you are using FireMonkey, there is no WndProc() method for handling window messages (FireMonkey does not dispatch window messages to user code at all). However, you can subclass the window that FireMonkey creates internally so you can still receive the WM_INPUT message. A static method is needed, but you do not have to rely on a global pointer, the Form object can be passed as a parameter of the subclassing:
class TMainForm : public TForm
{
private:
static LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
protected:
virtual void __fastcall CreateHandle();
};
void __fastcall TMainForm::CreateHandle()
{
TForm::CreateHandle();
HWND hWnd = Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd;
SetWindowSubclass(hWnd, &SubclassProc, 1, (DWORD_PTR)this);
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = hWnd;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
LRESULT CALLBACK TMainForm::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
TMainForm *pThis = (TMainForm*) dwRefData;
switch (uMsg)
{
case WM_INPUT:
{
// ...
break;
}
case WM_NCDESTROY:
{
RemoveWindowSubclass(hWnd, &SubclassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
I came across the same issue and I found that the best method, for my particular case, was to create a static array of pointers to my class. Then inside the static hook method, I just iterate through my class pointers and call their hook functions.
kb_hook.h
typedef KBDLLHOOKSTRUCT khookstruct;
typedef LRESULT lresult;
typedef void (*h_func)(uint64_t key_message, khookstruct* kbdhook);
typedef std::vector<kb_hook*> h_array;
class kb_hook
{
public:
kb_hook();
virtual ~kb_hook();
h_func hook_func;
private:
static h_array hook_array;
static lresult static_hook(int code, uint64_t key_message, khookstruct* kbdhook);
};
kb_hook.cpp
kb_hook::kb_hook() : hook_func(NULL)
{
this->hook_array.push_back(this);
}
lresult kb_hook::static_hook(int code, uint64_t key_message, khookstruct* kbdhook)
{
if(code == HC_ACTION)
{
for(auto it=kb_hook::hook_array.begin();it!=kb_hook::hook_array.end();it++)
{
if((*it)->hook_func) std::thread((*it)->hook_func, key_message, kbdhook).detach();
}
}
return CallNextHookEx(NULL, code, key_message, reinterpret_cast<LPARAM>(kbdhook));
}
I know it's an old question but I just wanted to throw in my two cents. I hope this is helpful to someone.