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);
}
Related
Running my program it runs and due to me having a menu with EXIT to Destroy the window it runs and immediately exits the window. Unsure how to fix my issue here on compiling the program to have it not run the WindowProcedure function and passing the argument EXITMENU resulting in the Window being destroyed.
*.CPP
#include <windows.h>
#define HELPMENU 1
#define HIGHSCROREMENU 2
#define EXITMENU 3
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {
WNDCLASS wc = { 0 }; // WNDCLASSW is a structure
LPCWSTR title = L"Window"; // Long Pointer Constant Wide (UTF-16) String
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // Background
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_HAND); // Sets Cursor
wc.hInstance = hInst; // Instance of window
wc.lpszClassName = L"windowClass"; // Class name
wc.lpfnWndProc = WindowProcedure; // Pointer to the function // Controller of window handle
if (!RegisterClassW(&wc)) { // Registers the window class
return -1;
}
// | binary combination value, posX, posY, Width, Height
// Creates the window
CreateWindow(wc.lpszClassName, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_BORDER, 100, 100, 800, 600, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL) > 0) { // Keeps the window running
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/* Event Paths */
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_CREATE: // On window creation
AddControls(hWnd);
AddMenu(hWnd);
break;
case WM_LBUTTONDOWN: // Left Mouse button
break;
case WM_DESTROY: // Makes GetMessage Function return false, closing the window
PostQuitMessage(0);
return 0;
case EXITMENU:
DestroyWindow(hWnd); // This part of the code shouldn't run on creation
break;
default:
return DefWindowProc(hWnd, msg, wp, lp);
}
}
/* Creates menu */
void AddMenu(HWND hWnd) {
hMenu = CreateMenu(); // Creates menu object
// AppendMenu(Menu Instance, Usage Type, Argument, String info);
AppendMenu(hMenu, MF_STRING, HELPMENU, L"Help - F1");
AppendMenu(hMenu, MF_STRING, HIGHSCROREMENU, L"Highscores - F2"); // Menu Created
AppendMenu(hMenu, MF_STRING, EXITMENU, L"Exit - ESC");
// SetMenu(Window Handle , Menu Instance);
SetMenu(hWnd, hMenu); // Sets menu for window //
}
You are not handling the menu commands correctly in your WindowProcedure().
You have defined EXITMENU as 3, which is the same value as the WM_MOVE message. So, in your switch, you are destroying your window as soon as it receives a WM_MOVE message during window creation.
You need to instead handle the menu commands via the WM_COMMAND message, per the documentation:
About Menus: Messages Used With Menus
When the user chooses a command item from a menu, the system sends a WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
Try this instead:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_COMMAND:
switch (wp) {
case HELPMENU: {
...
return 0;
}
case HIGHSCROREMENU: {
...
return 0;
}
case EXITMENU: {
DestroyWindow(hWnd);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
UPDATE: That being said, consider having your EXITMENU handler use SendMessage(WM_CLOSE) instead of DestroyWindow(). If your app maintains data that should be saved when the app is closed by the user, you can add a WM_CLOSE handler to perform that action regardless of how the window is being closed (your exit menu, X close button, Alt-F4, etc). DefWindowProc() destroys a window when processing WM_CLOSE.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_CLOSE: {
if (data has been modified) {
prompt user to save data...
if (cancelled) {
return 0;
}
if (should save) {
save data ...
}
}
break;
}
case WM_COMMAND:
switch (wp) {
...
case EXITMENU: {
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
Currently, I'm trying to make a simple Win32 application that switches back and forth between fullscreen and windowed mode. I'm using the Chromium way of doing this as a reference. I created a class to handle windows to make things simpler.
I expected this to work by toggling fullscreen when the F4 key was pressed. Unfortunately, the styles appear to be applied correctly, but the window doesn't resize or move to the correct area. It also snaps to the top left corner of the screen for a moment before it returns to its original position. For some reason, input for the window passes to the one below it when I toggle fullscreen. Then I have to go to Task Manager to kill the program because I can't close the window or application.
I've tried storing the styles of the HWND in a class variable at creation (it starts out in windowed mode) and using the value to create the necessary style for the fullscreen window and restoring the windowed mode window. I have also tried immediately getting the window styles with GetWindowLongPtr when the ToggleFullscreen function is called. Both of these do not work.
Here is my code:
WindowHandler.h
#include <Windows.h> // Win32 API
#ifndef WINDOWHANDLER
#define WINDOWHANDLER
class WindowHandler // WindowHandler
{
public:
WindowHandler(); // Constructor
void Destroy() { DestroyWindow(hwnd); } // Destroy the handler
void ToggleFullscreen(); // Toggle fullscreen
protected:
static LRESULT CALLBACK WindowProc // Window procedure
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
HWND hwnd; // The window
// Everything involved in fullscreen
bool fullscreen = false; // Whether the window is fullscreen or not
RECT windowRect = {}; // The restored window size
long int windowStyles = 0; // The restored window styles
long int extendedWindowStyles = 0; // The restored window extended styles
};
#endif
WindowHandler.cpp
#include "WindowHandler.h" // Header file
WindowHandler::WindowHandler() // Constructor
{
WNDCLASS wndClass = {}; // The window information
wndClass.lpfnWndProc = WindowProc;
wndClass.hInstance = GetModuleHandle(nullptr);
wndClass.lpszClassName = L"FullscreenTest";
RegisterClass(&wndClass); // Register the window
hwnd = CreateWindowEx // Create the window and store a pointer to the handler for the procedure to use
(
0,
L"FullscreenTest",
L"Stack Overflow Repro",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr,
nullptr,
GetModuleHandle(nullptr),
this
);
if (hwnd == nullptr) Destroy(); // Destroy the handler if the window is invalid
else // Otherwise...
{
GetWindowRect(hwnd, &windowRect); // Store the window size
windowStyles = GetWindowLongPtr(hwnd, GWL_STYLE); // Store the window styles
extendedWindowStyles = GetWindowLongPtr(hwnd, GWL_EXSTYLE); // Store the extended window styles
ShowWindow(hwnd, SW_SHOW); // Show the window
}
}
void WindowHandler::ToggleFullscreen() // Toggle fullscreen
{
if (!fullscreen) // If fullscreen is not enabled
{
MONITORINFO monitorInfo; // Get the monitor info
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), &monitorInfo);
SetWindowLongPtr(hwnd, GWL_STYLE, windowStyles & ~(WS_CAPTION | WS_THICKFRAME)); // Set the window styles
SetWindowLongPtr // Set the extended window styles
(
hwnd,
GWL_EXSTYLE,
extendedWindowStyles & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)
);
SetWindowPos // Resize, move, and refresh the window
(
hwnd,
nullptr,
monitorInfo.rcMonitor.left,
monitorInfo.rcMonitor.top,
monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED
);
fullscreen = true; // Indicate that fullscreen is on
}
else // Otherwise...
{
SetWindowLongPtr(hwnd, GWL_STYLE, windowStyles); // Set the window styles
SetWindowLongPtr(hwnd, GWL_EXSTYLE, extendedWindowStyles); // Set the extended window styles
SetWindowPos // Resize, move, and refresh the window
(
hwnd,
nullptr,
windowRect.left,
windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED
);
fullscreen = false; // Indicate that fullscreen is off
}
}
LRESULT CALLBACK WindowHandler::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // Window procedure
{
WindowHandler* handlerPtr; // Pointer to the window handler
if (message == WM_CREATE) // If the window is being created...
{
CREATESTRUCT* createStruct = reinterpret_cast<CREATESTRUCT*>(lParam); // Get the pointer's container
handlerPtr = reinterpret_cast<WindowHandler*>(createStruct->lpCreateParams); // Get the pointer
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)handlerPtr); // Store the pointer
}
else handlerPtr = reinterpret_cast<WindowHandler*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); // Otherwise, get the pointer
if (handlerPtr) { // If the pointer is valid...
switch (message)
{
case WM_PAINT: // Paint the window
{
PAINTSTRUCT paintStruct;
HDC hdc = BeginPaint(hwnd, &paintStruct);
FillRect(hdc, &paintStruct.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &paintStruct);
}
return 0;
case WM_DESTROY: // Destroy the window
PostQuitMessage(0);
return 0;
case WM_KEYDOWN: // Process input
switch ((int)wParam)
{
case VK_ESCAPE: // Quit if the escape key is pressed
handlerPtr->Destroy();
break;
case VK_F4: // Toggle fullscreen if F4 is pressed
handlerPtr->ToggleFullscreen();
break;
}
return 0;
default: // Do the default action if the message was not processed
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
else return DefWindowProc(hwnd, message, wParam, lParam); // Do the default action if the pointer is not valid
}
main.cpp
#include "WindowHandler.h" // Window handler
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) // Win32 main function
{
WindowHandler repro; // Create a window handler
MSG msg = {}; // Message structure
while (GetMessage(&msg, nullptr, 0, 0)) // Message loop
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Replace:
windowStyles = GetWindowLongPtr(hwnd, GWL_STYLE);
extendedWindowStyles = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
ShowWindow(hwnd, SW_SHOW); // Show the window
With:
ShowWindow(hwnd, SW_SHOW); // Show the window
windowStyles = GetWindowLongPtr(hwnd, GWL_STYLE);
extendedWindowStyles = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
The WS_VISIBLE style bit does not get set until after that first ShowWindow(SW_SHOW).
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);
I am creating a simple window but when I see the window being created and closes it, not WM_QUIT message is ever gotten. Here's some code:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
cWindowApplication app(hInstance);
const long width = 1024L;
const long height = 768L;
if (app.CreateWindowApplication(width, height) == false)
{
MessageBox(NULL, "Unable to create OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
app.DestroyWindowApplication();
return 1;
}
return app.MainLoop();
}
Here's the CreateWindowApplication(int, int) function:
bool cWindowApplication::CreateWindowApplication(long width, long height, bool full_screen /*= false*/)
{
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
mWindowRect.left = 0L; // Set Left Value To 0
mWindowRect.right = width; // Set Right Value To Requested Width
mWindowRect.top = 0L; // Set Top Value To 0
mWindowRect.bottom = height; // Set Bottom Value To Requested Height
mFullScreen = full_screen;
// fill out the window class structure
const char* class_name = "MyClass";
mWindowClass.cbSize = sizeof(WNDCLASSEX);
mWindowClass.style = CS_HREDRAW | CS_VREDRAW;
mWindowClass.lpfnWndProc = cWindowApplication::StaticWindowsProcessCallback;
mWindowClass.cbClsExtra = 0;
mWindowClass.cbWndExtra = 0;
mWindowClass.hInstance = mhInstance;
mWindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon
mWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow
mWindowClass.hbrBackground = NULL; // don't need background
mWindowClass.lpszMenuName = NULL; // no menu
mWindowClass.lpszClassName = class_name;
mWindowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon
// register the windows class
if (!RegisterClassEx(&mWindowClass))
{
return false;
}
if (mFullScreen == true) //If we are Full Screen, we need to change the display mode
{
DEVMODE dmScreenSettings; // device mode
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width; // screen width
dmScreenSettings.dmPelsHeight = height; // screen height
dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL; // bits per pixel
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
// setting display mode failed, switch to windowed
MessageBox(NULL, "Display mode failed", NULL, MB_OK);
mFullScreen = false;
}
}
if (mFullScreen == true) // Are We Still In Full Screen Mode?
{
dwExStyle = WS_EX_APPWINDOW; // Window Extended Style
dwStyle = WS_POPUP; // Windows Style
//ShowCursor(false); // Hide Mouse Pointer
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&mWindowRect, dwStyle, false, dwExStyle); // Adjust Window To True Requested Size
// class registered, and create our window
mHWND = CreateWindowEx(NULL, // extended style
class_name, // class name
"My Windows", // application name
dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, // x,y coordinate
mWindowRect.right - mWindowRect.left,
mWindowRect.bottom - mWindowRect.top, // width, height
NULL, // handle to parent
NULL, // handle to menu
mhInstance, // application instance
this); // this pointer to call member functions
// check if window creation failed (hwnd would equal NULL)
if (mHWND == false)
{
return false;
}
mHDC = GetDC(mHWND);
ShowWindow(mHWND, SW_SHOW); // display the window
UpdateWindow(mHWND); // update the window
return true;
}
Basically after this function call, the CreateWindowEx() function will call StaticWindowProcessCallback() that looks like this:
LRESULT cWindowApplication::StaticWindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
cWindowApplication* win_app = NULL;
if (msg == WM_CREATE)
{
//Creation event
//Get the pointer we pass during CreateWindowApplication() call
win_app = (cWindowApplication*)((LPCREATESTRUCT)lParam)->lpCreateParams;
//Associate window pointer with the hwnd for the other events to access
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)win_app);
}
else
{
//Non-creation event
win_app = (cWindowApplication*)GetWindowLongPtr(wnd, GWLP_USERDATA);
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
}
//call member
return win_app->WindowsProcessCallback(wnd, msg, wParam, lParam);
}
Finally, the last line of this function calls the member function WindowProcessCallback() that looks like this:
LRESULT cWindowApplication::WindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
mHDC = GetDC(wnd);
SetupPixelFormat();
//Set the version that we want, in this case 3.0
int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 }; //zero indicates the end of the array
//Create temporary context so we can get a pointer to the function
HGLRC tmp_context = wglCreateContext(mHDC);
//Make it current
wglMakeCurrent(mHDC, tmp_context);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
if (wglCreateContextAttribsARB == NULL)
{
//No OpenGL 3.0, back to 2.0
mHGLRC = tmp_context;
}
else
{
//Create OpenGL 3.0
mHGLRC = wglCreateContextAttribsARB(mHDC, 0, attribs);
//Delete the temp context
wglDeleteContext(tmp_context);
}
//Make OpenGL 3.0
wglMakeCurrent(mHDC, mHGLRC);
mIsRunning = true;
}
break;
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
wglMakeCurrent(mHDC, NULL);
wglDeleteContext(mHGLRC);
mIsRunning = false;
PostQuitMessage(0); //Send a WM_QUIT message
return 0;
default:
break;
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
As you can see, there are some message processing code there ... but other than the WM_CREATE, no other cases are being hit. After the WM_CREATE message being sent, the function MainLoop() is being called that looks like this:
int cWindowApplication::MainLoop()
{
while (mIsRunning == true)
{
ProcessWindowsMessages();
}
DestroyWindowApplication();
return 0;
}
Basically the ProcessWindowsMessages() function does not get any message after the window closes ... I have to press stop VS from running in order to kill the process. The ProcessWindowsMessages() function looks like this:
void cWindowApplication::ProcessWindowsMessages()
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
This logic in StaticWindowsProcessCallback looks backwards:
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
If you don't have a pointer to the window wrapper object, you'll need to call DefWindowProc. So that should happen if (win_app == NULL). This is to handle a handful of messages that are sent before WM_CREATE. As a result of this, your code has undefined behavior on messages processed before WM_CREATE, and discards (by applying default processing) all messages after WM_CREATE.
It would be even better to use WM_NCCREATE for setting up the link, though. As well, win_app is not a very good name for this, maybe win_obj or something.
You also shouldn't handle WM_QUIT in your window procedure, since it isn't sent to a window. And the default behavior of WM_CLOSE should be fine, it will call DestroyWindow which will trigger WM_DESTROY.
But the first, failure to forward any messages after WM_CREATE to your window procedure, likely explains your lack of WM_QUIT in the main message loop.
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.