how to use SetWindowLong to address a class member function? [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
To pass a pointer to a member function
When I move out of the class the WNDPROC DefEditProc; and EditKeyProc all works ok. But now as I pasted the code it fails compilation with error error: invalid use of member function (did you forget the '()' ?). So my question is how to squeeze this code into class so I do not pollute global namespace ?
#include <windows.h>
#include <richedit.h>
class richEdit {
HWND richeditWindow;
WNDPROC DefEditProc;
public:
LRESULT EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
}
richEdit() {
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
DefEditProc = (WNDPROC)SetWindowLong(richeditWindow, GWL_WNDPROC, (long)EditKeyProc);
}
~richEdit() {
MSG msg;
while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
}
}
};
int main() {
richEdit re;
}

You are supposed to use a free function instead of a member function, since member functions have an implicit this parameter. You will have to declare EditKeyProc as static CALLBACK, and find another way to pass around the this pointer if you need it.
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
:::
}
Additionally, you may be able to use SetWindowSubclass which will take care of proper subclassing and will keep around an extra pointer argument for you.

Try this:
#include <windows.h>
#include <richedit.h>
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&EditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
Or:
#include <windows.h>
#include <richedit.h>
namespace myNS
{
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&richEditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
}

You can't use a pointer to a member function as a window procedure because Windows expects a regular function pointer. Internally, it won't have enough storage for the member pointer in some cases (they can be up to 16 bytes in MSVC, 32-bit), and it won't know what "this" pointer to use in any case.
If your only interest is avoiding namespace pollution, then the easiest solution is to use a namespace instead of a class. Otherwise, you'll have to make EditKeyProc a static member of the class and use SetWindowLong to store a copy of the "this" pointer for EditKeyProc to access.

Related

CreateWindowEx does not create HWND with the instance of a class template

I'm following this tutorial and if I keep all the definitions in a single header file, it's all fine and works (I mean, if I directly copy this code). But if I try to move the definitions into separate files, it won't create HWND.
On CreateWindowEx called, it goes to the BaseWindow<DERIVED_TYPE>::WindowProc and sends the following messages, one after the other in a sequence:
WM_GETMINMAXINFO
WM_NCCREATE
WM_NCDESTROY
After it steps out, it results in HWND not being created. Thus, no window will show up.
The structure of the project:
main.cpp
Windows/
BaseWindow.h
BaseWindow.cpp
MainWindow.h
MainWindow.cpp
This is how my code looks.
main.cpp
#ifndef UNICODE
#define UNICODE
#endif // !UNICODE
#include <windows.h>
#include <new>
#include "Windows/MainWindow.h"
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
MainWindow win;
if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
{
return 0;
}
ShowWindow(win.Window(), nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Windows/BaseWindow.h
#pragma once
#include <windows.h>
template <typename DERIVED_TYPE>
class BaseWindow
{
public:
BaseWindow() : m_hwnd(NULL) { }
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
);
HWND Window() const { return m_hwnd; }
protected:
virtual PCWSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
HWND m_hwnd;
};
Windows/BaseWindow.cpp
#include "BaseWindow.h"
#include "MainWindow.h"
template <typename DERIVED_TYPE>
LRESULT CALLBACK BaseWindow<DERIVED_TYPE>::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE* pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
}
else
{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
template <typename DERIVED_TYPE>
BOOL BaseWindow<DERIVED_TYPE>::Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName();
RegisterClass(&wc);
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);
return (m_hwnd ? TRUE : FALSE);
}
// !The definition of the class template
template class BaseWindow<MainWindow>;
Windows/MainWindow.h
#pragma once
#include "BaseWindow.h"
class MainWindow : public BaseWindow<MainWindow>
{
public:
PCWSTR ClassName() const { return L"Sample Window Class"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
Windows/MainWindow.cpp
#include "MainWindow.h"
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(m_hwnd, &ps);
}
return 0;
default:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
I've tried to do it on mere classes without templating and it works.
I've also tried to remove this at the end of the argument list of CreateWindowEx and it worked too! I mean, if it looks like that, it will work:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);
So maybe I'm somehow misusing templates.
Before CreateWindowEx returns several messages will be pumped. All but one (WM_NCCREATE) will route through HandleMessage, which expects m_hwnd to be a valid handle. They're not getting that because you never save it until the final result of CreateWindowEx is reaped by return value. By then it is too late.
WM_NCCREATE is the first window message received by top-level windows (e.g. yours), and should be the one used to (a) save the hwnd parameter to m_hwnd, and (b) store the instance pointer int GWLP_USERDATA space. You're doing the latter, but you didn't do the former.
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
HandleMessage() uses m_hwnd, which is not initialized. That is why CreateWindowEx() returns NULL.
1. CreateWindowEx
2. sends WM_CREATE
3. WM_CREATE is processed by WindowProc
4. WindowProc calls HandleMessage, which uses m_hwnd,
which is not initialized, so HandleMessage returns
failure
5. Failure is returned as the result for WM_CREATE,
cancelling window creation
6. CreateWindowEx fails and returns NULL <--- and right now
m_hwnd is assigned
In short: you cannot use m_hwnd until CreateWindowEx() returns.
WM_CREATE reference
If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.

LNK2001 in "How can I make a WNDPROC or DLGPROC a member of my C++ class?"

VS10: MCBS: Hi there,
In the light of this discussion encountered a problem attempting a Hello World implementation of Raymond Chen's method in How can I make a WNDPROC or DLGPROC a member of my C++ class? using Pudeyev's code for "Hello World":
error LNK2001: unresolved external symbol "private: long __thiscall
BaseWnd::WndProc(struct HWND__ *,unsigned int,unsigned int,long)"
(?WndProc#BaseWnd##AAEJPAUHWND__##IIJ#Z)
The code is as follows:
// ...Written by Oleg Pudeyev, (c) 2003
#include <windows.h>
HINSTANCE appHinst;
class BaseWnd
{
public:
BaseWnd();
// This is the static callback that we register
static LRESULT CALLBACK s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// The static callback recovers the "this" pointer and then calls this member function.
LRESULT BaseWnd::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
BaseWnd::BaseWnd(void)
{
WNDCLASSW wc = {
// Request redraws as we're centering the string
CS_HREDRAW|CS_VREDRAW,
BaseWnd::s_WndProc,
// No per-class data
0,
// No per-window data
0,
appHinst,
LoadIcon(0, IDI_APPLICATION),
LoadCursor(0, IDC_ARROW),
HBRUSH(COLOR_BACKGROUND),
0,
L"BaseWnd"
};
RegisterClassW(&wc);
HWND hwnd = CreateWindowW(L"BaseWnd", L"Hello, World!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, appHinst, this);
ShowWindow(hwnd, SW_SHOW);
}
LRESULT BaseWnd::WndProc(HWND hwnd, UINT Umsg, WPARAM wParam, LPARAM lParam)
{
switch (Umsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
RECT r;
GetClientRect(hwnd, &r);
SetBkMode(ps.hdc, TRANSPARENT);
DrawTextW(ps.hdc, L"Hello, World!", -1, &r, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
default:
// Use default handling for messages we don't process
return DefWindowProc(hwnd, Umsg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK BaseWnd::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BaseWnd *pThis; // our "this" pointer will go here
if (uMsg == WM_NCCREATE) {
// Recover the "this" pointer which was passed as a parameter to CreateWindow(Ex).
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<BaseWnd*>(lpcs->lpCreateParams);
// Put the value in a safe place for future use
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
} else {
// Recover the "this" pointer from where our WM_NCCREATE handler stashed it.
pThis = reinterpret_cast<BaseWnd*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (pThis) {
// Now that we have recovered our "this" pointer, let the member function finish the job.
//Removal of this line removes the LNK2001
return pThis->WndProc(hwnd, uMsg, wParam, lParam);
}
// "this" pointer unknown, so just do the default thing. Hopefully, didn't need to customize behavior yet.
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPTSTR cmdline, int showcmd)
{
appHinst = hinst;
BaseWnd p;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Is there something wrong with either how the BaseWnd constructor is invoked or the class setup - or perhaps, viz the date on Raymond's article, a revision of c++11 that cannot cope?
Edit: Replaced WndProc with absolute qualifier BaseWnd::WndProc and made the offending static callback a callback. Great, the other issues with the code we can work with.
Edit2: The final piece of the puzzle was returning Pudeyev's original CS_HREDRAW|CS_VREDRAW to the WNDCLASS structure.
Apparently I think a definition of scope is none.
LRESULT CALLBACK WndProc
{
 ↓
LRESULT CALLBACK BaseWnd::WndProc
{
The error should be self-explanatory. BaseWnd::WndProc is declared, but not defined. See #nariuji
Also BaseWnd needs a public constructor because you are going to call it later:
class BaseWnd
{
//******* make this public:
public:
BaseWnd();
...
};
WNDCLASSW wc has to be initialized to zero:
WNDCLASSW wc = { 0 };
When you create the window it is invisible. You have to make it visible with ShowWindow or change the style to WS_VISIBLE | WS_OVERLAPPEDWINDOW
HWND hwnd = CreateWindowW(L"BaseWnd", L"Hello, World!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, appHinst, this);
ShowWindow(hwnd, SW_SHOW); //*** add this
Declaring a pointer doesn't do anything. You also need a message loop:
WinMain(...)
{
//BaseWnd *pthis; //***remove this line
BaseWnd wnd;//***add this line
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
}

Creating a window in OO

Here is my header
#pragma once
#ifndef BASE_H
#define BASE_H
#include <Windows.h>
#include <windowsx.h>
class Base
{
HWND hWnd;
WNDCLASSEX WndCls;
HRESULT Hr;
public:
Base();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RegisterWnd(HINSTANCE hInstance);
void ShowWnd(int nCmdShow);
~Base();
};
#endif
Here is my base.cpp
#include "Base.h"
Base::Base()
{
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
void Base::RegisterWnd(HINSTANCE hInstance)
{
ZeroMemory(&WndCls, sizeof(WNDCLASSEX));
WndCls.cbSize = sizeof(WNDCLASSEX);
WndCls.hbrBackground = (HBRUSH)COLOR_WINDOW;
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hIcon = LoadIcon(hInstance, NULL);
WndCls.hIconSm = LoadIcon(hInstance, NULL);
WndCls.hInstance = hInstance;
WndCls.lpfnWndProc = WndProc;
WndCls.lpszClassName = "ClsName";
WndCls.style = CS_HREDRAW | CS_VREDRAW;
Hr = RegisterClassEx(&WndCls);
if (FAILED(Hr))
MessageBox(NULL, "Window Class failed to register.", "ERROR", MB_OK);
hWnd = CreateWindowEx(
NULL,
"WndClassName",
"WndName",
WS_OVERLAPPEDWINDOW,
100, 100,
480, 640,
NULL,
NULL,
hInstance,
NULL);
if (FAILED(hWnd))
MessageBox(NULL, "Window Class failed to create", "ERROR", MB_OK);
}
void Base::ShowWnd(int nCmdShow)
{
Hr = ShowWindow(hWnd, nCmdShow);
if (FAILED(Hr))
MessageBox(NULL, "Failed to display Window", "ERROR", MB_OK);
}
Base::~Base()
{
}
And here is my main.cpp
#include "Base.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Base CreateWnd;
CreateWnd.RegisterWnd(hInstance);
CreateWnd.ShowWnd(nCmdShow);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&Msg);
// send the message to the WindowProc function
DispatchMessage(&Msg);
}
// return this part of the WM_QUIT message to Windows
return Msg.wParam;
}
The problem is, I keep getting this error message that I dont understand of. Sorry for the bad explanation..Still a student in programming...
UPDATED :
The error above has been corrected by replacing
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
with
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Thanks to ravi and IInspectable for the quick help.
Now I am having another error D: When i clicked on debug, everything run perfectly but nothing shows up. No window is showing. Visual studio is running perfectly as "Ready". (Sorry i do not want to make another new question because it's still related to creating window in oo
SECOND UPDATE :
My class name in CreateWindowEx is different from the RegisterWnd..My bad. Thanks to IInspectable again for the help.
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
You have to define this with class scope OR how compiler know if its global static OR static member of class. So it should be
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

C++ Win32 Control Procedure within a Class

What I am trying to do, is to create a edit control and procedure within a class. I have tried various things, and tried using parts of a similar question I asked: Win32 C++ Create a Window and Procedure Within a Class.
At the moment, I took them apart.
main_class.h
class MainClass {
private:
HWND hwndMain; // main windows handle
HINSTANCE hInstanceMain; // main windows instance
HWND hTextarea;
public:
bool init(HWND _hwnd, HINSTANCE _hInstance);
bool ShowInfoTextarea();
};
main_class.cpp
// Heres the question
bool MainClass::ShowInfoTextarea() {
if (hTextarea != NULL) return true; // if it is not null, the textarea is likely already displayed.
// Creating the EDIT textarea
hTextarea = CreateWindowEx(0, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
SCREEN_WIDTH+5, 0, WINDOW_WIDTH-SCREEN_WIDTH-10, WINDOW_HEIGHT-30, hwndMain, (HMENU)IDC_CTRL_EDIT, GetModuleHandle(NULL), NULL);
if (hTextarea == NULL) { MessageBox(NULL, L"Could not create the test text control. The program will now close.", NULL, MB_OK | MB_ICONEXCLAMATION); return false; }
// Dozens of attempts with something like:
// lpEditWndProc = (WNDPROC)SetWindowLongPtr(hTextarea, GWLP_WNDPROC, (LONG_PTR)EditControlProc);
// lpEditWndProc = (WNDPROC)SetWindowLongPtr(hTextarea, GWLP_WNDPROC, (LONG_PTR)MainClass::EditControlProc);
// tried static callback functions, etc.
//Every try the compiler said: Are there missing braces ( ) with EditControlProc
}
// To get working, I separated:
WNDPROC lpEditWndProc;
LRESULT CALLBACK EditControlProc(HWND hwndEdit, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
default:
return CallWindowProc(lpEditWndProc, hwndEdit, uMsg, wParam, lParam);
}
return 0; // DONE
}
With my (various) attempts the compiler said: Are there missing braces ( ) with EditControlProc, or said type mismatch when trying to define lpEditWndProc.
I am probably missing out on something simple?
Useage:
main.cpp
MainClass mainclass;
mainclass.ShowInfoTextarea();
Thanks. If missing any info let me know.
Jonathan Potter's comment was the solution.
For anyone who reads:
mainclass.h
class MainClass {
private:
HWND hwndMain;
HINSTANCE hInstanceMain;
HWND hTextarea;
static LRESULT CALLBACK EditControlProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
public:
bool init(HWND _hwnd, HINSTANCE _hInstance);
bool ShowInfoTextarea();
};
mainclass.cpp
// To Show the Textarea
bool MainClass::ShowInfoTextarea() {
if (hTextarea != NULL) return true; // if it is not null, the textarea is likely already displayed.
// Creating the EDIT textarea
hTextarea = CreateWindowEx(0, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
SCREEN_WIDTH+5, 0, WINDOW_WIDTH-SCREEN_WIDTH-10, WINDOW_HEIGHT-30, hwndMain, (HMENU)IDC_CTRL_EDIT, GetModuleHandle(NULL), NULL);
if (hTextarea == NULL) { MessageBox(NULL, L"Could not create the test text control. The program will now close.", NULL, MB_OK | MB_ICONEXCLAMATION); return false; }
SetWindowSubclass(hTextarea, EditControlProc, 0, 0);
}
// Now Works
LRESULT CALLBACK MainClass::EditControlProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch (msg) {
case WM_KEYDOWN:
MessageBox(0,0,0,0);
break;
default:
return DefSubclassProc(hWnd, msg, wParam, lParam);
break;
}
return 0; // DONE
}

C++, WIN32, WndProc to Member Function Crash (Access violation at 0x88)

I've been writing a win32 wrapper class, and I've come across a problem: Because each instance of the class has a window, I've enclosed the this pointer in the user info space using SetWindowLongPtrW(), allowing me to call a message handler from the static WndProc function. This works fine: I can call the function. However, when I try to call another member function from the message handler, I get an access violation at 0x00000088
It does compile.
I posted quite a lot, because to be honest I'm not too sure where the problem originates from...
Please feel free to comment/criticize my code in general. Thanks for the help!
Here is the header:
#pragma once
#include <Windows.h>
#include "GlobalDefines.h"
#include "GraphicsWrapper.h"
#include "Keyboard.h"
namespace Startup
{
class GraphicsWrapper;
class WindowsWrapper
{
public:
WindowsWrapper(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow);
~WindowsWrapper();
void EnterMsgLoop(GraphicsWrapper* Gfx);
static LRESULT _stdcall WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
LRESULT _stdcall MessageHandler(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
WNDCLASSEX WndClass;
MSG Message;
RECT Desktop;
RECT Taskbar;
RECT WindowCoordinates;
LPSTR CommandLineArgs;
INT CmdShow;
HINSTANCE TheInstance;
HWND WindowHandle;
void InitializeWndClassEx();
void InitializeWindowHandleHWND();
void ShowWindowOnScreen();
bool GetScreenRect(RECT & Desktop);
bool GetTaskbarRect(RECT& rectTaskbar);
bool GetWindowCoords(RECT& WindowCoordinates);
int GetTaskbarSide();
enum TaskbarSides
{
Top,
Right,
Bottom,
Left
};
void SetFullScreen(bool Enable);
};
static IO::Keyboard * kbd;
}
And this is the relevant part of the implementation. I'll mark where the crash occurs.
void Startup::WindowsWrapper::InitializeWndClassEx()
{
WndClass.hIcon = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.hIconSm = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WindowProc;
WndClass.hInstance = TheInstance;
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.lpszClassName = L"WindowClassName";
RegisterClassEx(&WndClass);
SetWindowLongPtrW(WindowHandle, GWLP_USERDATA, (long)this);
}
void Startup::WindowsWrapper::SetFullScreen(bool Enable)
{
long style = Enable ? WS_POPUP : WS_OVERLAPPED | WS_SYSMENU;
static RECT windowRect = {};
static bool needRect = true;
if (needRect)
{
GetWindowRect(WindowHandle, &windowRect);
needRect = false;
}
SetWindowLong(WindowHandle, GWL_STYLE, style);
if (Enable)
{
SetWindowPos(WindowHandle, HWND_TOPMOST,
0,0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SWP_SHOWWINDOW);
}
else
{
SetWindowPos(WindowHandle, 0,
windowRect.left,windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_SHOWWINDOW);
}
}
and
LRESULT CALLBACK Startup::WindowsWrapper::WindowProc
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
WindowsWrapper* ourObjectPtr = NULL;
long thisObject = GetWindowLongW(hWnd, GWLP_USERDATA);
ourObjectPtr = (WindowsWrapper *)( (void*)thisObject);
long Result = ourObjectPtr->MessageHandler(hWnd, message, wParam, lParam);
RET(Result);
}
LRESULT _stdcall Startup::WindowsWrapper::MessageHandler
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0); //Works fine here, but...
break;
case VK_SPACE:
this->SetFullScreen(false); //Crashes here w/ access violation
break;
case VK_SHIFT:
this->SetFullScreen(true); //Or here, w/ the same error.
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
This is the createWindowEx call. Thanks for your help, again.
void Startup::WindowsWrapper::InitializeWindowHandleHWND()
{
WindowHandle = CreateWindowEx(NULL,
L"WindowClassName",
L"WindowTitle"
WS_OVERLAPPED | WS_SYSMENU,
WindowCoordinates.left, WindowCoordinates.top,
WindowCoordinates.right, WindowCoordinates.bottom,
NULL, NULL, TheInstance,
CommandLineArgs);
}
I have some code from a custom dialog handler I wrote quite a while back, which might be of use to you.
Same principle applies for a window but switch the WM_INITDIALOG for WM_CREATE and also replace DWLP_USER with GWLP_USERDATA. The format of the callback is subtley different too. You should be able to salvage almost all of this function though.
LRESULT CALLBACK CDialog::DlgProc( HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
CDialog* pWindow;
if( msg == WM_INITDIALOG ) {
SetWindowLongPtr( hWndDlg, DWLP_USER, (LONG_PTR)lParam );
pWindow = reinterpret_cast<CDialog*>( lParam );
pWindow->m_hWnd = hWndDlg;
} else {
pWindow = reinterpret_cast<CDialog*>( (LPARAM)GetWindowLongPtr( hWndDlg, DWLP_USER ) );
}
if( pWindow != NULL ) {
LRESULT ret = pWindow->OnMessage( msg, wParam, lParam );
if( msg == WM_NCDESTROY ) pWindow->m_hWnd = NULL;
}
return FALSE;
}