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).
Related
My goal is to create a custom maximize button for my app and I also want to trigger snap layouts menu on the button on Windows 11. For that, I am following the guide from here, but it doesn't work.
Here is the sample code, For testing purpose, I take a rectangle {point(0,0), size(200,200)} and when the mouse is over it and I receive WM_NCHITTEST message I return the HTMAXBUTTON as instructed in the guide but the snap layouts menu doesn't show up.
#include <stdio.h>
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <windowsx.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Test Window", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
case WM_NCHITTEST:
{
// Get the point in screen coordinates.
// GET_X_LPARAM and GET_Y_LPARAM are defined in windowsx.h
POINT point = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// Map the point to client coordinates.
::MapWindowPoints(nullptr, hwnd, &point, 1);
RECT maximizeRect {0, 0, 200, 200};
// If the point is in your maximize button then return HTMAXBUTTON
if (::PtInRect(&maximizeRect, point))
{
printf("maxmize button rect %d\n", rand());
fflush(stdout);
return HTMAXBUTTON;
}
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
At the beginning, custom title bar before supporting the snap layouts menu
maximizeRect is client coordinates and not the maximize button rectangle.
The WM_NCHITTEST return value is wrong. If the point is in your maximize button then return HTMAXBUTTON.
There is a HitTestNCA example which calculates in the screen coordinate instead of the client coordinate and works fine. Another win32-window-custom-titlebar sample.
Is it possible to paint colored text to what I've already done?
I've tried WM_CTLCOLORSTATIC, CreateSolidBrush(), and several other functions.
//-----------------------------------
// Learning the win32 API for C++
//
// Created by: Cosmic Cruizer
//-----------------------------------
#include <windows.h>
#include <tchar.h>
// Function declarations
bool SetUpWindowClass (char*, int, int, int); // Remove window structure from WinMain and put into function
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) // Pre-declare Windows procedure/function
// Global variables
const char CLASS_NAME[] = "My Window Class Array"; // declared for window class; Can be static or const
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow){
//Step 1: Registering the Window Class
HWND hwnd{}; // This is the handle for our window
MSG msg{}; // Handle for messages to the application
SetUpWindowClass (NULL, NULL, NULL, NULL); // The Window structure - removed from main and made into a function
// Step 2: Creating the Window
hwnd = CreateWindowEx( // returns a handle to the new window, or zero if the function fails
WS_EX_CLIENTEDGE, // Optional window styles. Can be set to 0
CLASS_NAME, // Name of window class, see set 1b. Also set as the constant
"My First C++ Windows App", // Window title text
WS_OVERLAPPEDWINDOW, // Window style, title bar, a border, a system menu, and Minimize and Maximize buttons.
200, 200, 500, 400, // Size and position
NULL, NULL, hInstance, NULL); // Parent window, Menu, Instance handle, Additional app data
// Add an exit button
CreateWindow(
"BUTTON", // Predefined class; Unicode assumed
"EXIT", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
200, 200, 60, 25, // x position, y position, Button width, Button height
hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL); // Pointer not needed.
ShowWindow(hwnd, nCmdShow); // Make the window visible on the screen
// Step 3: The Message Loop
while(GetMessage(&msg, NULL, 0, 0) != 0) { // Run the message loop. It will run until GetMessage() returns 0
TranslateMessage(&msg); // Translate virtual-key messages into character messages
DispatchMessage(&msg); // Send message to WindowProcedure
}
return msg.wParam;
}
//---------- Functions ----------//
// Setup the window structure
bool SetUpWindowClass (char *cpTitle, int iR, int iG, int iB) {
//Step 1a: The Window structure
WNDCLASSEX wc{}; // Data structure for the windowclass
wc.cbSize = sizeof(WNDCLASSEX); // Sets the size of the Windows API
wc.style = 0; // define additional elements of the window class
wc.lpfnWndProc = WndProc; // defines most of the behavior of the window. See setp 4 "LRESULT CALLBACK WndProc" function
wc.cbClsExtra = 0; // No extra bytes after the window class
wc.cbWndExtra = 0; // structure for the window instance
wc.hInstance = GetModuleHandle (NULL); // handle to the application instance.
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // handle to icon class, if NULL, system provides a default icon.
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // handle to cursor class
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+18); // Add color as the background of the window
wc.lpszMenuName = NULL; // No menu
wc.lpszClassName = CLASS_NAME; // string that identifies the window class
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
//Step 1b: Register the window class, and if it fails quit the program
if (RegisterClassEx (&wc)) return true;
else return false;
}
// Step 4: the Window Procedure in this function
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch(uMsg){
case WM_CLOSE:{
DestroyWindow(hwnd);
break;
}
case WM_COMMAND:{ // Close the window when exit is pressed
if (MessageBox(hwnd, "Really quit?", "Exit Warning", MB_OKCANCEL) == IDOK){ // what the hell, just wanted this.
PostQuitMessage(0);
}
break;
}
case WM_DESTROY:{
PostQuitMessage(0);
break;
}
//--- trying to create colored text ---//
case WM_CTLCOLORSTATIC:{
HDC hdcStatic = (HDC) wParam; // handle to display context
hwnd = (HWND) lParam; // handle to control window
SetTextColor(hdcStatic, RGB(100,255,255));
SetBkColor(hdcStatic, RGB(250,250,6));
return (INT_PTR)CreateSolidBrush(RGB(250,250,100));
}
case WM_CTLCOLOREDIT:{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,255));
SetBkColor(hdcStatic, RGB(0,230,0));
return (INT_PTR)CreateSolidBrush(RGB(0,230,0));
}
case WM_PAINT:{ // All painting (text) occurs here, between BeginPaint and EndPaint.
PAINTSTRUCT ps; // Holds info about current painting session.
HDC hdc = BeginPaint(hwnd, &ps); // Create the device context (DC)
// Each character is added to the cpaText array. Then the for loop goes through and paints each character
int iY = 7; // Vertical spaces and number of lines for the array
const char *cpaText [iY] = { // changed from char to const char to get rid of warning. and added 1 for each line and a return
"Hello Peoples",
"",
"This is my first attempt to program using the Win32 API.",
"I can only hope it gets better with time.",
"",
"Created by \"The\" Cosmic Cruizer"
};
for (int iLoopCounter = 0; cpaText [iLoopCounter] != 0; iLoopCounter++, iY += 20) {
TextOut (hdc, 5, iY, cpaText [iLoopCounter], strlen (cpaText [iLoopCounter]));
}
EndPaint(hwnd, &ps); // Free up HDC created with BeginPaint
break;
}
default:{
return DefWindowProc(hwnd, uMsg, wParam, lParam); // Return is needed either here or at the end
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam); // Return is needed either here or in the default case
}
WM_CTLCOLORSTATIC and WM_CTLCOLOREDIT are notification messages used by STATIC/EDIT controls, neither of which you have on your window, so you will never receive those messages and should remove those handlers from your code.
You are trying to draw colored text directly onto your window using TextOutA() in a WM_PAINT handler, which is fine. But per the TextOutA() documentation:
The TextOut function writes a character string at the specified location, using the currently selected font, background color, and text color.
Your WM_PAINT handler is not selecting anything into the HDC that BeginPaint() returns, before trying to draw on it. It simply needs to configure the desired font/color values as desired, eg:
HFONT hFont;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch(uMsg){
...
case WM_CREATE:{
hFont = CreateFont(...); // or CreateFontIndirect()
break;
}
case WM_DESTROY:{
DeleteObject(hFont);
PostQuitMessage(0);
break;
}
case WM_SETTINGCHANGE:
case WM_FONTCHANGE:{
DeleteObject(hFont);
hFont = CreateFont(...); // or CreateFontIndirect()
InvalidateRect(hwnd, NULL, TRUE);
break;
}
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HFONT hOldFont = (HFONT) SelectObject(hdc, hFont);
SetTextColor(hdc, ...);
SetBkColor(hdc, ...);
int iY = 7;
const char *cpaText [iY] = {
"Hello Peoples",
"",
"This is my first attempt to program using the Win32 API.",
"I can only hope it gets better with time.",
"",
"Created by \"The\" Cosmic Cruizer"
};
for (int iLoopCounter = 0; cpaText [iLoopCounter] != 0; iLoopCounter++, iY += 20) {
TextOutA (hdc, 5, iY, cpaText [iLoopCounter], strlen (cpaText [iLoopCounter]));
}
SelectObject(hdc, hOldFont);
EndPaint(hwnd, &ps);
return 0;
}
...
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
I'm having trouble with a WINAPI project. There are two problems, when I launch a window with the below code, the height parameter behaves strangely. It seems to cap off at 1092, 18 pixels below where I need it on my computer. The second problem is that the window has no edges nor the top menu bar, until I use the Windows+Up/Down key combination to minimize and maximize it, then it behaves normally. I'm using the below code to initialize the window (the only code that runs before this initializes the options.pxXRes and other variables used below):
//Set up the window class
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = &WndHandleInput;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = GetModuleHandle(nullptr);
wndClass.hIcon = nullptr;
wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = "ToastCatClass";
wndClass.hIconSm = nullptr;
RegisterClassEx(&wndClass);
RECT wndRect;
if (options.fullscreen) {
wndRect.left = 0;
wndRect.right = options.pxXRes;
wndRect.top = 0;
wndRect.bottom = options.pxYRes;
AdjustWindowRect(&wndRect, WS_OVERLAPPEDWINDOW, false);
} else {
wndRect.left = (GetPXXRes() - options.pxXRes) / 2;
wndRect.right = options.pxXRes;
wndRect.top = (GetPXYRes() - options.pxYRes) / 2;
wndRect.bottom = options.pxYRes;
AdjustWindowRect(&wndRect, WS_OVERLAPPEDWINDOW, false);
}
hWnd = CreateWindowEx(
0,
wndClass.lpszClassName,
"ToastCat",
WS_OVERLAPPEDWINDOW,
wndRect.left,
wndRect.top,
wndRect.right - wndRect.left,
wndRect.bottom - wndRect.top,
nullptr,
nullptr,
wndClass.hInstance,
nullptr
);
assert(hWnd != nullptr, "Failure to launch window.");
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
The Window procedure is as follows:
LRESULT __stdcall WndHandleInput(HWND hWndParam, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_NCCREATE:
return true;
case WM_KEYDOWN:
switch (wParam) {
//TODO: Update controls
}
break;
case WM_KEYUP:
switch (wParam) {
//TODO: Update controls
}
break;
case WM_CLOSE:
case WM_QUIT:
Cleanup();
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
Windows have two different rectangles:
the client rectangle which is the area of the window to which the application can draw or create child windows within and
the window rectangle which is the outer rectangle of the window (including borders, menu, ...).
By calling AdjustWindowRect you're converting a client rectangle to a window rectangle. Therefore the borders and the menu cannot be visible if you're setting up a client rectangle as big as the entire screen, converting it to a window rectangle and creating a window with such size. If you look at wndRect after the call to AdjustWindowRect(..) using the debugger you will see that top and left are negative.
That the window height is off by some pixels is the default windows behavior. Windows by default does not allow windows to have a height bigger than the screen height because this will move the caption out of the area the mouse can get to. To change this you have to process message WM_GETMINMAXINFO:
case WM_GETMINMAXINFO:
DefWindowProc(hWnd, msg, wParam, lParam);
MINMAXINFO *pmmi = (MINMAXINFO*)lParam;
pmmi->ptMaxTrackSize.x *= 2; // just make it bigger...
pmmi->ptMaxTrackSize.y *= 2; //
return 0;
If you just want to start your window either maximized (with all controls and border visible) or at some other defined location just do the following: Create your window normally with the wndRect initialized within the else-part of if (options.fullscreen) and change the call of ShowWindow(..) as follows:
if (options.fullscreen)
{
ShowWindow(hWnd, SW_MAXIMIZE);
}
else
{
ShowWindow(hWnd, SW_SHOWDEFAULT);
}
UpdateWindow(hWnd);
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.
I would like to add a child window to Metatrader4's chart window which always stays on top, without blinking, just statically there all the time upon eveything (in parent window). I am doing this from a DLL (C++).
I call this method from the mql side:
MT4_EXPFUNC int __stdcall testWindow(HWND hwnd) {
prnt_hWnd = hwnd;
CreateThread(0, NULL, ThreadProc, (LPVOID)L"Window Title", NULL, NULL);
return 0;
}
The parent window's (chart) handle is given as a parameter.
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
MSG messages;
/*
... in createWindowClass:
WNDCLASSEX wc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = (LPCWSTR)L"MyClass";
wc.lpszClassName = (LPCWSTR)szClassName;
wc.lpfnWndProc = DLLWindowProc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
...
*/
CreateWindowClass(L"MyClass");
HWND hwnd = CreateWindowEx (0, L"MyClass", NULL, WS_VISIBLE | WS_CHILD , CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, prnt_hWnd, NULL, GetModuleHandle(NULL), NULL );
ShowWindow (hwnd, SW_SHOWNORMAL);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return 1;
}
I handle window's messages this way:
LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint( hwnd, &ps );
EndPaint( hwnd, &ps );
return 0;
}
case WM_COMMAND:
/* */
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
My child window at the begining appears, then after (I guess) the parent window is getting being redrawn, suddenly it disappears, then it is just blinking (fastly appears-disappear).
My goal is to have a child window on that chart statically, so always topmost, without blinking. I could achieve this only without the WS_CHILD property. But then my child window is not ON the parent.
Try adding the WS_CLIPCHILDREN style to the chart window. I would pass the handle on the MQL4 side in init() via some MT4 export function. For example, SetChartWnd( HWND hChartWnd ) and passing WindowHandle( Symbol(), Period() ) as the parameter.
Then inside that function, I would try doing something like the following:
if ( ::IsWindow( hChartWnd ) ) {
DWORD style = GetWindowLong( hChartWnd, GWL_STYLE );
style |= WS_CLIPCHILDREN;
SetWindowLong( hChartWnd, GWL_STYLE, style );
}
}