this works
mcs.szTitle = L"untitled";
mcs.szClass = childClassName;
mcs.hOwner = GetModuleHandle(NULL);
mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;
mcs.style = WS_HSCROLL;
hChild = (HWND)SendMessage(hMDIClient, WM_MDICREATE, 0, (LONG)&mcs);
but I can't send it a pointer to the object that has the WndProc I'd like to use (see here for wrapping up WndProc in classes) like this
hChild = CreateWindow(childClassName, L"", WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 250, hMDIClient, NULL, GetModuleHandle(NULL), this);
However doing it like that will cause an error with the windo, there will be no zlose, no minimise, no maximise, I can't resize it, and if I create another mdi child window, the previous one will become deselectable.
Is it possible to use either SendMessage or CreateWindow, pass a this pointer and still create a working MDI Child window?
I solved it by doing this
hChild = CreateMDIWindow(childClassName, L"", WS_EX_WINDOWEDGE, 49, 50, 51, 52, hMDIClient, GetModuleHandle(NULL), (LPARAM)this);
and then in the static WndProc
LRESULT CALLBACK CWindowHandler::MsgRouter(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
{
CREATESTRUCT* wnd = 0;
MDICREATESTRUCT* mdiStruct = 0;
CMDIChildWindowBase* wndBase;
if(message == WM_NCCREATE)
{
wnd = (CREATESTRUCT*)(lparam);
mdiStruct = (MDICREATESTRUCT*)wnd->lpCreateParams;
wndBase = (CMDIChildWindowBase*)mdiStruct->lParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(wndBase));
// save window handle
wndBase->SetHWND(hwnd);
}
else
wndBase = reinterpret_cast<CMDIChildWindowBase *>(::GetWindowLong(hwnd, GWL_USERDATA));
// call the windows message handler
if(wndBase)
return wndBase->WndProcs(message, wparam, lparam);
return DefWindowProc(hwnd, message, wparam, lparam);
}
so when creating an MDIChild, lparam in WndProc will be a pointer to a CREATESTRUCT, whose lpCreateParams will be a MIDICREATESTRUCT, whose lParam will be the pointer to your object.
phew.
When you pass the MDICREATESTRUCT to WM_MDICREATE, you can provide the object pointer in MDICREATESTRUCT.lParam field:
mcs.szTitle = L"untitled";
mcs.szClass = childClassName;
mcs.hOwner = GetModuleHandle(NULL);
mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;
mcs.style = WS_HSCROLL;
mcs.lParam = (LPARAM) this; // <-- here
hChild = (HWND) SendMessage(hMDIClient, WM_MDICREATE, 0, (LPARAM)&mcs);
Related
LRESULT WINAPI TextViewWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
TextView* ptv = (TextView*)GetWindowLong(hwnd, 0);
switch (msg)
{
case WM_NCCREATE:
if ((ptv = new TextView(hwnd)) == nullptr)
return FALSE;
SetWindowLong(hwnd, 0, (LONG)ptv);
return TRUE;
}
Code from: http://www.catch22.net/tuts/neatpad/neatpad-overview
The TextView here is Owner-Drawn Controls.
My question here is:
GetWindowLong(hwnd, 0)
SetWindowLong(hwnd, 0, (LONG)ptv);
what does that mean? I have searched MSDN, it says:
The following values are also available when the hWnd parameter identifies a dialog box.
...
DWL_MSGRESULT
0
Retrieves the return value of a message processed in the dialog box procedure.
If the function succeeds, the return value is the requested value.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
If SetWindowLong has not been called previously, GetWindowLong returns zero for values in the extra window or class memory.
Per the GetWindowLong() documentation:
nIndex
Type: int
The zero-based offset to the value to be retrieved. Valid values are in the range zero through the number of bytes of extra window memory, minus four; for example, if you specified 12 or more bytes of extra memory, a value of 8 would be an index to the third 32-bit integer.
...
The following values are also available when the hWnd parameter identifies a dialog box.
DWL_MSGRESULT
0
Retrieves the return value of a message processed in the dialog box procedure.
The code in question is for a window that wraps a TextView object. The window is NOT a dialog box, and there is an InitTextView() function beint called that registers a WNDCLASSEX whose cbWndExtra field is set to sizeof(TextView*) when calling RegisterClassEx():
BOOL InitTextView()
{
WNDCLASSEX wcx;
//Window class for the main application parent window
wcx.cbSize = sizeof(wcx);
wcx.style = 0;
wcx.lpfnWndProc = TextViewWndProc; // <--
wcx.cbClsExtra = 0;
wcx.cbWndExtra = sizeof(TextView *); // <--
wcx.hInstance = GetModuleHandle(0);
wcx.hIcon = 0;
wcx.hCursor = LoadCursor (NULL, IDC_IBEAM);
wcx.hbrBackground = (HBRUSH)0; //NO FLICKERING FOR US!!
wcx.lpszMenuName = 0;
wcx.lpszClassName = TEXTVIEW_CLASS; // <--
wcx.hIconSm = 0;
return RegisterClassEx(&wcx) ? TRUE : FALSE;
}
And then there is a CreateTextView() function that creates a new window, and thus a new TextView object, using that registered class:
HWND CreateTextView(HWND hwndParent)
{
return CreateWindowEx(WS_EX_CLIENTEDGE,
TEXTVIEW_CLASS, _T(""), // <--
WS_VSCROLL |WS_HSCROLL | WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwndParent,
0,
GetModuleHandle(0),
0);
}
In the window's WM_NCCREATE handler, a new TextView object is created, and a pointer to that object is stored directly in the HWND itself, at offset 0 of the window's extra memory. Other message handlers for the same window can then retrieve that TextView* pointer and use the object as needed. In this case, WM_NCDESTROY and WM_PAINT specifically:
LRESULT WINAPI TextViewWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
TextView *ptv = (TextView *)GetWindowLong(hwnd, 0);
switch(msg)
{
// First message received by any window - make a new TextView object
// and store pointer to it in our extra-window-bytes
case WM_NCCREATE:
if((ptv = new TextView(hwnd)) == 0)
return FALSE;
SetWindowLong(hwnd, 0, (LONG)ptv);
return TRUE;
// Last message received by any window - delete the TextView object
case WM_NCDESTROY:
delete ptv;
return 0;
// Draw contents of TextView whenever window needs updating
case WM_PAINT:
return ptv->OnPaint();
default:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
The RegisterClassEx function is returning a non zero value, however the CreateWindowEx function return a null.
I have read multiple answers and I seem to be following everything correctly. Could someone please review my code and tell me what is going wrong? My guess is that there is something wrong with my WndProc, but I am unable to figure out what is going wrong. I am trying to create a class that can paint windows and hence the WndProc function is a class member.
void windowPainter::registerWindow()
{
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = windowPainter::WndProc;
wcex.hInstance = m_hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = windowClassName;
ATOM returnValue = RegisterClassEx(&wcex);
int lastErrorValue = GetLastError();
}
void windowPainter::createWindow()
{
DWORD dwStyle1 = WS_EX_TOPMOST | WS_EX_LAYERED;
if (m_isClickThrough)
dwStyle1 = dwStyle1 | WS_EX_TRANSPARENT;
DWORD dwStyle2 = WS_POPUP;
RECT rc = { m_windowOriginX, m_windowOriginY, m_windowSizeX, m_windowSizeY };
AdjustWindowRect(&rc, dwStyle1, FALSE);
BYTE alpha_value = 255;
m_hWnd = CreateWindowEx(dwStyle1,windowClassName, windowTitle, dwStyle2, rc.left,
rc.top, rc.right,
rc.bottom,
NULL, NULL, m_hInstance, NULL);
}
LRESULT CALLBACK windowPainter::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
UNREFERENCED_PARAMETER(lParam);
windowPainter* pThis = NULL;
if (WM_CREATE == message)
{
pThis = (windowPainter*)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
return DefWindowProc(hWnd, message, wParam, lParam);
}
else
{
pThis = (windowPainter*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if (pThis)
switch (message)
{
case WM_PAINT:
if (pThis)
{
//do painting using members of pThis
}break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
pThis->onLeftButtonClick();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Look at these statements from your code:
m_hWnd = CreateWindowEx(..., NULL);
Here you pass NULL for the lpParam argument.
pThis = (windowPainter*)((LPCREATESTRUCT)lParam)->lpCreateParams;
This line of code is where you read that same value. Because you passed NULL to lpParam when calling CreateWindowEx, you will be setting pThis to NULL.
Instead of passing NULL to lpParam when calling CreateWindowEx you must pass the address of the instance.
m_hWnd = CreateWindowEx(..., (LPVOID)this);
Note that these changes should not influence whether or not the call to CreateWindowEx succeeds or fails. For the code in the question, pThis being NULL just means that you always call the default window procedure. Which will not lead to the window creation failing. You appear to have some other problem in your code, presumably in the code that we cannot see.
I'm creating a win32 application in C++ and trying (unsuccessfully) to keep things somewhat object-orientated while still respecting the winapi message loop etc.
The problem I'm having is that my window seems to be drawing inconsistently and/or in a weird way that I have not encountered before. Eg:
This occurs when the window is resized. If I minimize the window and open it again, everything is black, apart from the buttons. Also, when moving my mouse from outside the window into the window, whatever my cursor was before getting inside the window is retained. For example, if there is a textbox behind the window, and I move my cursor from the textbox and into this window, the cursor remains the caret cursor.
I'm using this code to handle messages inside my WindowMain class:
std::map<HWND, WindowMain*> WindowMain::HWNDMap;
LRESULT CALLBACK WindowMain::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (HWNDMap.find(hWnd) == HWNDMap.end()) { return DefWindowProc(hWnd, msg, wParam, lParam); }
return HWNDMap[hWnd]->InstWndProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK WindowMain::InstWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
DeleteObject(_hFont);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
The WindowMain::WndProc function is a static member of WindowMain. I use a std::map to map HWND to WindowMain* so that I can pass messages from the static method to the relevant WindowMain::InstWndProc. I feel like the problem might be here but I can't spot it.
In case it's relevant, this is the code I'm using to create the window:
WindowMain::WindowMain(HINSTANCE hInst) {
_isReady = false;
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = WndProc;
wc.lpszClassName = ClassName.c_str();
wc.hInstance = hInst;
wc.hbrBackground = (HBRUSH)(0);
if (!RegisterClass(&wc)) { return; }
_hWnd = CreateWindow(ClassName.c_str(), "AlchemyAC", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInst, NULL);
if (!_hWnd) { return; }
_hBtnStart = CreateWindow("BUTTON", "Start", WS_TABSTOP | WS_VISIBLE | WS_CHILD, 10, 10, 100, 24, _hWnd, NULL, hInst, NULL);
_hBtnStop = CreateWindow("BUTTON", "Stop", WS_TABSTOP | WS_VISIBLE | WS_CHILD, 10, 39, 100, 24, _hWnd, NULL, hInst, NULL);
if (!_hBtnStart || !_hBtnStop) { return; }
GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &_lf);
_hFont = CreateFont(_lf.lfHeight, _lf.lfWidth,
_lf.lfEscapement, _lf.lfOrientation, _lf.lfWeight,
_lf.lfItalic, _lf.lfUnderline, _lf.lfStrikeOut, _lf.lfCharSet,
_lf.lfOutPrecision, _lf.lfClipPrecision, _lf.lfQuality,
_lf.lfPitchAndFamily, _lf.lfFaceName);
SendMessage(_hBtnStart, WM_SETFONT, (WPARAM)_hFont, true);
SendMessage(_hBtnStop, WM_SETFONT, (WPARAM)_hFont, true);
EnableWindow(_hBtnStop, false);
HWNDMap[_hWnd] = this;
_isReady = true;
}
My question is, why is this strange painting issue occurring? I'm mainly interested in the odd thing that is happening with the cursor.
EDIT: I am using a while loop to translate and dispatch messages.
Pre-Text/ Question
I am trying to make a fairly simple tool to help debug variable values. For it to be completely self contained within the class is what I am aiming for. The end product I can use a function in the class like ShowThisValue(whatever).
The problem I am having is that I can't figure out, if possible, to have the procedure within the class. Here is the short version, with the problem.
-Code updated again 11/29/13-
-I have put this in its own project now.
[main.cpp]
viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);
[viewvars.h] The entire updated
class viewvars {
private:
HWND hWindow; // the window, a pointer to
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
viewvars(); // blank constructor
int CreateTestWindow(HINSTANCE hInst);
};
// blank constructor
viewvars::viewvars() {}
// create the window
int viewvars::CreateTestWindow(HINSTANCE hInst) {
// variables
char thisClassName[] = "viewVars";
MSG msg;
WNDCLASS wincl;
// check for class info and modify the info
if (!GetClassInfo(hInst, thisClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return -1;
}
}
// create window
hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this);
if (hWindow == NULL) {
MessageBox(NULL,"Problem creating the window.","Error",0);
return -1;
}
// show window
ShowWindow(hWindow, TRUE);
// message loop
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// then quit window?
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
// window proc
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
MessageBox(NULL,"Has it gone this far?","Bench",0);
// variable
viewvars *view;
// ????
if (message == WM_NCCREATE) {
CREATESTRUCT *cs = (CREATESTRUCT*)lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) {
if (GetLastError() != 0) {
MessageBox(NULL,"There has been an error near here.","Error",0);
return FALSE;
}
}
}
else {
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view) return view->WindowProc(message, wParam, lParam);
MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
// you can access non-static members in here...
MessageBox(NULL,"Made it to window proc.","Error",0);
switch (message)
{
case WM_PAINT:
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
break;
}
}
The message boxes appear in this order:
Has it made it this far?
Made it to window proc
DefWindowProc returned
Has it made it this far? // repeated?
Made it to window proc
DefWindowProc returned
Problem Creating the Window
Thanks for the help so far. Do you know where the problem might be?
To use a non-static class method as a window procedure requires a dynamically-allocated thunk, which is an advanced technique that I will not get into it here.
The alternative is to declare the class method as static, then it will work as a window procedure. Of course, being a static method, it can no longer access non-static class members without an instance pointer. To get that pointer, you can have the class pass its this pointer to the lpParam parameter of CreateWindow/Ex(), then the window procedure can extract that pointer from the WM_NCCREATE message and store it in the window using SetWindowLong/Ptr(GWL_USERDATA). After that, subsequent messages can retrieve that pointer using GetWindowLong/Ptr(GWL_USERDATA) and thus be able to access non-static members of that object. For example:
class viewvars
{
private:
HWND hWindow;
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
int CreateTestWindow(HINSTANCE hInst);
};
int viewvars::CreateTestWindow(HINSTANCE hInst)
{
WNDCLASS wincl;
if (!GetClassInfo(hInst, thisClassName, &wincl))
{
...
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
if (RegisterClass(&wincl) == 0)
return -1;
}
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
...
MSG msg;
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
viewvars *view;
if (message == WM_NCCREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0)
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view)
return view->WindowProc(message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// you can access non-static members in here...
switch (message)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWindow, message, wParam, lParam);
}
}
The main message loop must not be in your class, and especially not in a "CreateTestWindow" function, as you will not return from that function until your thread receive the WM_QUIT message that makes GetMessage returns 0.
Here is simple implementation of your viewvars class. Key points:
The Window Proc is a static member.
The link between the Window Proc and the object is made through the
use of GWLP_USERDATA. See SetWindowLongPtr.
The class DTOR destroys the window if it still exists. The WM_DESTROY
message set the HWND member to 0.
Adding OnMsgXXX methods to the class is simple: declare/define then
and just call them from the WindowProc using the 'this' pointer
stored in GWLP_USERDATA.
EDIT:
As per Mr Chen suggestion, earlier binding of the HWND to the Object (in WM_NCCREATE) to allow message handler as methods during the Window Creation.
I changed the creation styles, to show the window and to be able to move it.
// VIEWVARS.H
class viewvars {
public:
static viewvars* CreateTestWindow( HINSTANCE hInstance );
viewvars() : m_hWnd( 0 ) {}
~viewvars();
private:
static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static const char * m_pszClassName;
HWND m_hWnd;
};
// VIEWVARS.CPP
#include "viewvars.h"
const char * viewvars::m_pszClassName = "viewvars";
viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {
WNDCLASS wincl;
if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = m_pszClassName;
wincl.lpfnWndProc = WindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return 0;
}
}
viewvars * pviewvars = new viewvars;
HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
if ( hWnd == NULL ) {
delete pviewvars;
MessageBox(NULL,"Problem creating the window.","Error",0);
return 0;
}
return pviewvars;
}
LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uMsg ) {
case WM_NCCREATE: {
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
pviewvars->m_hWnd = hwnd;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
return TRUE;
}
case WM_DESTROY: {
viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
if ( pviewvars ) pviewvars->m_hWnd = 0;
break;
}
default:
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
return 0;
}
viewvars::~viewvars() {
if ( m_hWnd ) DestroyWindow( m_hWnd );
}
Finally, a "main" sample, but beware that there is here no way to end the process. That should be taken care by regular code (another windows).
// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
if ( pviewvars == 0 ) return 0;
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete pviewvars;
return 0;
}
Unfortunately using an instance method as a C-style callback function for the WndProc won't work. At least not in any straight-forward way.
The reason it doesn't work like that is that an instance method requires the this pointer to be passed in (to point to an instance) and that won't be correctly set by the code calling the WndProc. The Win32 API was originally designed with C in mind so this is one area where you have to use some work-arounds.
One way to work around this would be to create a static method to serve as the window proc and dispatch messages to your class instances. The class instances would have to be registered (read added to a static collection) so the static method would know to dispatch WndProc messages to the instances. Instances would register themselves with the static dispatcher in the constructor and remove themselves in the destructor.
Of course all the registration and unregistration and dispatching overhead is only necessary if your WndProc handler needs to invoke other instance member functions, or access member variables. Otherwise you can just make it static and you're done.
Your window procedure is called during CreateWindow. You pass hWindow to DefWindowProc, but hWindow is only set after CreateWindow returns - so you pass DefWindowProc a garbage window handle.
I don't see a nice way to do it. You could set hWindow inside the window procedure, by changing WindowProc to:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(added the hwnd parameter), changing the call to:
return view->WindowProc(hwnd, message, wParam, lParam);
creating the window like this:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(the first assignment is to make sure hWindow is initialized; the second one is in case CreateWindow fails after calling the window procedure), and adding this at the start of WindowProc:
if(!this->hWindow)
this->hWindow = hwnd;
Step through the code in the debugger. When you get to the line
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
You will see something wrong: hWindow is garbage. You are using an uninitialized variable.
UPDATE: As requested I have added all of the code I am using to create the Window and its RichEdit control.
I'm trying to handle windows messages for a RichEdit control used as a child of another window.
Now I did have the RichEdit control working with the exception of my own WndProc. The issue is that, when I set wc.lpszClassName = MSFTEDIT_CLASS; so that it matches lpClassName used in CreateWindowEx(), the content of the RichEdit control no longer appears to draw (ie text, etc), however, its WndProc function can then handle messages.
The creation of the window:
First the constructor:
SubWindow::SubWindow(const wchar_t *szAppNameImport)
{
szAppName = szAppNameImport;
cfmt = CHARFORMATW();
hwnd = HWND();
windowRect = RECT();
editControlHwnd = HWND();
wc = WNDCLASSEX();
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
}
Then the Create() function:
VOID SubWindow::Create(unsigned int window_startX, unsigned int window_startY, unsigned int windowWidthInput, unsigned int windowHeightInput, HWND parent)
{
windowRect.left = window_startX;
windowRect.top = window_startY;
windowRect.right = windowWidthInput;
windowRect.bottom = windowHeightInput;
if(!RegisterClassEx(&wc))
{
throw std::exception();
}
if((hwnd = CreateWindowEx
(
WS_EX_CLIENTEDGE,
szAppName,
TEXT("Our classy sub window!"),
WS_OVERLAPPEDWINDOW| WS_VISIBLE,
windowRect.left, windowRect.top,
windowRect.right, windowRect.bottom,
parent,
NULL,
wc.hInstance,
NULL))==NULL)
{
throw std::exception();
}
SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)this);
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
}
WndProc:
LRESULT CALLBACK SubWindow::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SubWindow *childWindowPointer = (SubWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(childWindowPointer != NULL)
{
if(childWindowPointer->GetEditControl() == hwnd)
OutputDebugString(L"I SHOULD NOT BE CALLED");
return childWindowPointer->MsgProc(hwnd, uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
MsgProc:
LRESULT SubWindow::MsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch(uMsg)
{
case WM_WINDOWPOSCHANGED:
{
GetClientRect(hwnd, &windowRect);
SetWindowPos(editControlHwnd, NULL, windowRect.left, windowRect.top, windowRect.right, windowRect.bottom, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_DESTROY:
{
OutputDebugString(TEXT("DESTROYING A SUB WINDOW!\n"));
return 0;
}
case WM_PAINT:
{
InvalidateRect (hwnd, NULL, FALSE);
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}
case EM_EXSETSEL:
{
if(hwnd == editControlHwnd)
{
OutputDebugString(L"Text selection changed");
return 0;
}
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
The RichEdit control draws and functions perfectly, apparently without issue, with the exception of it not using the WndProc I have defined.
I'm not sure what I'm doing wrong here or how I can correctly resolve this.
EDIT:
Based on the answers and comments, I have restored my code to use only a Window class which contains a RichEdit control, created thusly:
void SubWindow::CreateEditControl()
{
std::wstring initialText = TEXT("TestWindow\r\n");
LoadLibrary(L"Msftedit.dll");
GetClientRect(hwnd, &windowRect);
editControlHwnd = CreateWindowEx(0, MSFTEDIT_CLASS, initialText.data(),
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY | WS_VSCROLL | ES_NOHIDESEL,
windowRect.left, windowRect.top,windowRect.right,windowRect.bottom,
hwnd,
NULL, NULL, NULL);
cfmt.cbSize = sizeof(CHARFORMAT);
cfmt.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE;
cfmt.dwEffects = 0;
cfmt.yHeight = 160;
cfmt.crTextColor = RGB(0,0,0);
wcscpy_s(cfmt.szFaceName, TEXT("Tahoma"));
SendMessage(editControlHwnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cfmt);
}
How do I process the messages from this control in the Window's MsgProc?
When you create a rich edit control window using the default class name (MSFTEDIT_CLASS), all messages are going to be sent to its parent window. Since you are not that parent window, you are not able to handle those messages.
So you will need to subclass the control, substituting your own window procedure that will be called directly, instead of allowing the messages to be passed on to the parent. That is simple to do; I've discussed it before in this answer for a regular edit control. The altered example code looks like this:
// Stores the old original window procedure for the rich edit control.
WNDPROC wpOldRichEditProc;
// The new custom window procedure for the rich edit control.
LRESULT CALLBACK CustomRichEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
...
}
// Pass the messages you don't process on to the original window procedure.
CallWindowProc(wpOldRichEditProc, hWnd, msg, wParam, lParam);
}
And when you create the control:
// Create the rich edit control
HWND hWnd = CreateWindowEx(...)
// Subclass it.
wpOldRichEditProc= (WNDPROC)SetWindowLongPtr(hWnd,
GWLP_WNDPROC,
(WNDPROC)CustomRichEditProc);
You will also need to make sure to unsubclass the control whenever it is destroyed. The other example demonstrates doing that in response to messages received by the parent window, but that won't work in your case, since you're not getting messages for the parent window. Instead, you'll need to remove the subclass from the control in response to its own WM_NCDESTROY message:
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)wpOldRichEditProc);
Or, version 6 of the common controls library introduced a new, less error-prone method of subclassing using a set of utility functions. (The critical functionality was actually there in earlier versions, but it was undocumented.) Considering that you do not have control over the process that actually owns the window, this is arguably the preferred approach.
There is a demo of both approaches here on MSDN.
And of course, you don't have to subclass only individual controls. You can also register a custom window class that behaves the same way as the built-in rich edit control, but still gives you first crack at the messages received by windows of that class. I can't tell from the question whether that's necessary or not; it sounds like you only have a single control you care about.
You say that the original problem was that your parent window was not getting the notification messages from the RichEdit control. Did you send a EM_SETEVENTMASK message to the RichEdit control? If you don't, the RichEdit control will not send certain notification messages to its parent window. See EM_SETEVENTMASK message.
Can you show your code involving the wc structure and the creation of the window? I'm fairly sure you don't want the main window to have the same class as the rich edit control - and that's what I'm reading so far.
I don't even know why you have a WNDCLASSEX apply to a rich edit control.
My suggestion is that you simplify things and "subclass" the rich edit control after it has been created, using SetWindowLong() with GWL_WNDPROC to your EditControl::WndProc.