What I'm trying to do is steal a window from the screen and make it a child of my own created window. When my program closes, the stolen windows goes away too, probably along with its process.
So here are my questions:
The created window is frozen, it won't let me operate its controls. Does the console prevent it from being operated? If so, how can I fix this?
(code below) only steals the window on its second run, it doesn't do it in the first run and the window is still left in the taskbar.
I tried doing the same except I've stolen a Chrome window into a notepad window. Same problems and when it did stole the window, everything looked completely torn apart, rendering the browser virtually inoperable.
Here's the code I used (Win32 Console Application):
#include <conio.h>
#include <stdio.h>
#include <Windows.h>
#include <winuser.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LPCWSTR sClassName = L"MyClass";
HWND CreateTheWindow(LPCWSTR WindowTitle) {
// Create & register the class
WNDCLASSEX WndClass;
WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.style = NULL; WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.lpszClassName = sClassName;
WndClass.hInstance = NULL; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WndClass.lpszMenuName = NULL; WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&WndClass);
// Create & show the window
HWND hwnd = CreateWindowEx(WS_EX_STATICEDGE, sClassName, WindowTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, NULL, NULL);
ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd);
return hwnd;
}
// No idea what's this for, back in JS we simply had to do window.open
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message) {
case WM_CLOSE: DestroyWindow(hwnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
// start
void main()
{
HWND chrome = FindWindow(L"Chrome_WidgetWin_1", NULL);
HWND mywin = CreateTheWindow(L"HELLO BOSS");
if(chrome!=0) printf("Got Chrome\r\n"); else printf("Chrome not found\r\n");
if(mywin!=0) printf("Got yours\r\n"); else printf("Your window not found\r\n");
SetParent(chrome, mywin);
SetWindowLong(chrome, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE );
UpdateWindow(chrome);
UpdateWindow(mywin);
_getch();
}
Oh BTW, please don't ask me what I'm trying to achieve. :D It's a surprise.
You don't seem to be running a message loop, which is necessary for your own window, and is probably necessary for pumping messages that go between the child and the parent. That seems the most likely reason why the stolen window seems to be locked up. (There may be other problems, but I'd start here.)
Try adding a basic message loop where you have the getch call:
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
There may be additional difficulties. Having a child window in another process is tricky because of the per-thread message queues. Contrary to myth, it can be made to work: (multi-process browsers do it).
You might be grabbing the wrong window from Chrome. Remember that Chrome also plays this game, creating child windows in separate processes. Are you grabbing one of the children or the main frame window?
I eventually stole the window into Notepad. All I had to do was get rid of Notepad's child editor window and the paint issue goes away along with it.
Also the good styles that need to be applied are WS_CHILD on Chrome and WS_POPUP on Notepad, followed by UIS_INITIALIZE WM_CHANGEUISTATE message on both.
I really hope Chrome Dev doesn't change this behavior.
Related
I'm loading an embedded browser within a parent application using the IWebBrowser2 interface. My code is compiled as a dll, i.e. the browser component is dynamically loaded at runtime via a plugin interface.
The problem I'm having is that applications that load my dll are trapping certain keydown messages, and they are therefore not reaching my IWebBrowser2 instance.
I am therefore capturing these messages using the SetWindowsHookEx() API in my dll.
How can I then forward the WM_KEYDOWN or WM_CHAR messages to my IWebBrowser2 instance such that they could e.g. be used to enter text in a focused text box within the browser?
I believe the problem sits in host application's message queue implementation, where some messages are handled instead of delivering, for example to implement hotkeys. Since you can't change their code, hooking the message queue sounds like a reasonable approach.
The following code snippet demonstrates both problem and solution:
#define WINDOW_CLASS _T("StackOverflow_41911104")
HINSTANCE g_Instance = 0;
HHOOK g_Hook = 0;
HWND g_TargetWindow = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
HWND CreateMainWindow()
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = g_Instance;
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = WINDOW_CLASS;
wcex.hIconSm = nullptr;
ATOM windowClass = RegisterClassExW(&wcex);
HWND mainWindow = CreateWindowW(WINDOW_CLASS, WINDOW_CLASS, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, nullptr, nullptr, g_Instance, nullptr);
g_TargetWindow = CreateWindow(_T("Edit"), nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE, 0, 0, 300, 300, mainWindow, (HMENU)1000, g_Instance, nullptr);
return mainWindow;
}
HACCEL CreateAccelerators()
{
ACCEL acceleratorsList[] =
{
{FVIRTKEY, 'R', 1000},
{FVIRTKEY, 'T', 1001},
};
return CreateAcceleratorTable(acceleratorsList, _countof(acceleratorsList));
}
void ProcessHookMessage(MSG* a_Message)
{
// Only affect our window and its children
if ((g_TargetWindow != a_Message->hwnd) && !IsChild(g_TargetWindow, a_Message->hwnd))
return;
// Deliver the message directly
TranslateMessage(a_Message);
DispatchMessage(a_Message);
// Do not allow to process this message the second time
a_Message->message = WM_NULL;
}
LRESULT CALLBACK Hook_GetMsgProc(int a_Code, WPARAM a_WParam, LPARAM a_LParam)
{
if ((HC_ACTION == a_Code) && (PM_REMOVE == a_WParam))
ProcessHookMessage((MSG*)a_LParam);
return CallNextHookEx(g_Hook, a_Code, a_WParam, a_LParam);
}
void InstallHook()
{
g_Hook = SetWindowsHookEx(WH_GETMESSAGE, Hook_GetMsgProc, g_Instance, GetCurrentThreadId());
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int)
{
g_Instance = hInstance;
HWND mainWindow = CreateMainWindow();
HACCEL hAccelTable = CreateAccelerators();
InstallHook();
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
// The problem lurks here: some messages are handled directly and never reach the target window
if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
In this code snippet, if you comment out InstallHook() call, you won't be able to print R and T, because these keys are used for accelerator table. However, with InstallHook(), the hook forces normal message queue behavior and everything works as normal.
The suggested hook code have the following points of interest:
It only affects your window and nothing else
It works the same way as the usual message queue would, instead of messing with SendMessage / PostMessage
It prevents double-effect of messages that were not intercepted by hosting application
It sounds like the root problem is that your window is on a different thread than the host application's window, which can confuse the focus state. You can easily get into situations where the host window and the hosted window both believe they have the focus.
The solution to that is to create your window on the same thread as the parent window, and, if that's not possible (e.g., because of the plugin model or because the plugin is run in a separate process), use AttachThreadInput.
I haven't used a web browser control in many years, but I recall one project long ago, where we had similar issues when we added the web browser control as a child of a window in another process. Using AttachThreadInput there solved a lot of bugs. The drawback was that a bug in either thread (like a hang) effectively hangs both threads. We also had to be careful to detach the threads during teardown.
It seems, this is a bit trickier than the usual sending a message:
Firstly, you will need to obtain the In place active object (https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx) of your web browser and then call the TranslateAccelerator (https://msdn.microsoft.com/en-us/library/windows/desktop/ms693360(v=vs.85).aspx) on it.
Some very high level pseudocode would look like:
HRESULT hr;
IOleInPlaceActiveObject* pIOIPAO;
hr = webBrowser2->QueryInterface(webBrowser2,
&IID_IOleInPlaceActiveObject, (LPVOID*)&pIOIPAO);
if (SUCCEEDED(hr))
{
result = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, msg);
}
where msg is the message (MSG) you should populate accordingly, and webBrowser2 is your IWebBrowser2.
PS: Didn't try this code, use at your own risk :)
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.
I'm trying to create a borderless window that overlaps the taskbar potentially in fullscreen (without using ChangeDisplaySettings(&settings, CDS_FULLSCREEN);) and I can't quite figure out how to do it. I have tried pretty much every possible CreateWindowEx style combination with no success.
The purpose of it is to be rendered on in a windowed borderless application using opengl/directx while allowing alt-tabbing out while continuing rendering in the background. This is done by many games such as wow and dota2 (windowed fullscreen option) as well as applications such as windows7 task manager (took two screenshots of this in a vm to prove the idea) (windows8 doesn't have this behaviour).
Task manager not focused:
Task manager focused:
This is some minimal code I'm using for easily testing some ideas I had, but none really quite did it. It only creates a borderless window with sizes of screenwidth-20 and screenheight-20.
#include <windows.h>
static bool quit = false;
static LRESULT CALLBACK message_handler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch(umsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CLOSE:
quit = true;
break;
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
int main()
{
WNDCLASSEX wc;
HMODULE hInstance;
HWND hwnd;
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
hInstance = GetModuleHandle(NULL);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = message_handler;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
wc.lpszMenuName = NULL;
wc.lpszClassName = "test";
wc.cbSize = (unsigned int)sizeof(WNDCLASSEX);
RegisterClassEx(&wc);
hwnd = CreateWindowEx(WS_EX_APPWINDOW, "test", "test", WS_OVERLAPPED|WS_POPUP, 10, 10, (int)GetSystemMetrics(SM_CXSCREEN)-20, (int)GetSystemMetrics(SM_CYSCREEN)-20, NULL, NULL, hInstance, NULL);
//hwnd = CreateWindowEx(WS_EX_APPWINDOW, "test", "test", WS_OVERLAPPED|WS_POPUP, 0, 0, (int)GetSystemMetrics(SM_CXSCREEN), (int)GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
SetFocus(hwnd);
while (!quit)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
Using
CreateWindowEx(WS_EX_APPWINDOW, "test", "test", WS_OVERLAPPED|WS_POPUP, 0, 0, (int)GetSystemMetrics(SM_CXSCREEN), (int)GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);
does overlap the taskbar but it has some weird behaviour when opengl renders on it (flashes/flickers the screen such as when the video mode changes. Same effects were encountered by someone on this forum of some sort of a library that makes cross platform windows for rendering and other stuff. Someone also posted in that thread a video (careful, loud music) which shows exactly what I'm trying to achieve).
I have downloaded that opensource library and tried to figure out what is doing but I couldn't find anything special about the way it creates the window.
Using g++ (Built by MinGW-builds project) 4.8.0 20121225 (experimental) on Windows8.
Raymond Chen explained how to switch in and out of full screen mode rather simply by changing the window style with SetWindowLong(Ptr), using SetWindowPlacement to resize the window, and SetWindowPos to make sure the frame redraws.
The Task Bar detects when a window is trying to be full screen and automatically gets out of the way.
Use the SetWindowPos function to set your window TOPMOST. When you get a message saying you lost the focus, make it non-topmost again so the other window you switched to can appear.
So i have this code that creates two windows:
WNDCLASS wc;
wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = StaticWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = nullptr;
wc.lpszClassName = _T("Move Engine");
RegisterClass(&wc);
m_hWnd = CreateWindow("Move Engine", "Move Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, usWidth, usHeight, nullptr, nullptr, wc.hInstance, this);
// Create the settings window
wc.lpszClassName = _T("Settings");
RegisterClass(&wc);
s_hWnd = CreateWindow("Settings", "Settings", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 20, nullptr, nullptr, wc.hInstance, this);
ShowWindow(m_hWnd, SW_SHOW);
ShowWindow(s_hWnd, SW_SHOW);
The problem is, application terminates each time i close one of the windows. What i need is two separate windows; main application window and the one that gives access to various settings of the application - therefore closing the settings window should not affect the main window. Is there any way to achieve this? Thank you.
P.S. My WinMain function is modified like this:
int WINAPI WinMain(HINSTANCE a_hInstance, HINSTANCE a_hPrevInstance, LPTSTR a_lpCmdLine, int a_iCmdShow)
{
int iReturnCode;
// Initialise the engine.
if (!MyEngine.InitInstance(a_hInstance, a_lpCmdLine, a_iCmdShow)) return 0;
// Begin the gameplay process and return when the application due to exit
iReturnCode = MyEngine.StartEngine();
// Return the correct exit code.
return iReturnCode;
}
and StaticWndProc looks like this:
LRESULT CALLBACK CMoveEngine::StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
// If this is a create message, trap the 'this' pointer passed in and store it within the window.
if (Message == WM_CREATE) SetWindowLong(hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
// Obtain the correct destination for this message
CMoveEngine *Destination = (CMoveEngine*)GetWindowLong(hWnd, GWL_USERDATA);
// If the hWnd has a related class, pass it through
if (Destination) return Destination->DisplayWndProc(hWnd, Message, wParam, lParam);
// No destination found, defer to system...
return DefWindowProc(hWnd, Message, wParam, lParam);
}
Message loop:
int CMoveEngine::StartEngine()
{
MSG msg;
// Start main loop
while (true)
{
// Did we recieve a message, or are we idling?
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage (&msg);
}
else
{
}
}
return 0;
}
You register both window classes with the same lpfnWndProc. Thus, all the messages (including mouse clicks and keyboard presses) will be directed to the same function, StaticWndProc.
Thus, when this function receives the WM_CLOSE message, it will respond by destroying the window, which stops the message pump and terminates the program. Since both windows have the same message handler function, they will both respond in the same way.
The solution is to define two different WndProc methods, one for each of your windows, or special-case the handling of the WM_CLOSE message, only calling DefWindowProc for the window that you want to allow to close the entire application.
Make your StaticWndProc handle either WM_CLOSE or WM_DESTROY: In that case it should decrement an openedWindows-counter. If that counter reaches zero call PostQuitMessage.
I've been playing around with the Windows api for uni, but I cant get the window messages to call WM_PAINT after the inital creation. It calls it when the window is created but not after.
All Other messages get called! Just cant get the WM_PAINT to be called.
This is part of a game lib we have to make using the Windows api, (the goal is to learn abit of the windows api, rather than the intricacies of making game libs).
From what I gather the UpdateWindow(handle) command calls inititates the WM_PAINT message, but no matter where I put this command (currently in the game loop) I cant get it to call the message.
There's a lot of code, so I've trimmed it a little.
I know I have multiple WM_PAINT cases, they are a result of trying to make sure its getting through.
Window Creation
HWND hwnd;
hwnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
L"FirstWindowClass",
L"Mootlib",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
m_screenWidth,
m_screenHeight,
NULL,
NULL,
WindowsUtilities::windowInstance(),
NULL);
Register the Window
WNDCLASSEX wcex;
wcex.cbSize = sizeof (wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = windowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = WindowsUtilities::windowInstance();
wcex.hIcon = 0;
wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"FirstWindowClass";
wcex.hIconSm = 0;
RegisterClassEx (&wcex);
Winproc
LRESULT CALLBACK windowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LONG_PTR lptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch(message)
{
case(WM_PAINT):
int f = 3;
}
if (lptr)
{
Window* obj = reinterpret_cast<Window*>(lptr);
return obj->handleWindowsMessages(message, wParam, lParam);
}
else
return DefWindowProc(hwnd, message, wParam, lParam);
}
Internal command
LRESULT Window::handleWindowsMessages(UINT message, WPARAM wParam, LPARAM lParam)
{
std::wstring szHello = L"Hello, world!";
PAINTSTRUCT ps;
HDC hdc;
switch(message)
{
case(WM_PAINT):
hdc = BeginPaint(m_handle, &ps);
TextOut(hdc, 0, 0, L"Hello, Windows!", 15);
EndPaint(m_handle, &ps);
return 0;
case(WM_CLOSE):
quitGame();
return 0;
// Move Me!!!
case(WM_KEYDOWN):
case(WM_LBUTTONDOWN):
case(WM_RBUTTONDOWN):
inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_DOWN);
return 0;
// Move Me!!!
case(WM_KEYUP):
case(WM_LBUTTONUP):
case(WM_RBUTTONUP):
inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_UP);
return 0;
}
return DefWindowProc(handle(), message, wParam, lParam);
}
Game Loop
while(IsWindowVisible(handle()))
{
UpdateWindow(handle());
WindowsUtilities::processMessages();
draw();
update();
inputManager().update();
inputManager().clearButtonUpEvents(); // this should be in the input update()
}
Thanks.
First, I really don't like your game loop as its not at all obvious where the windows message pump is.
Somewhere along the line you need to call PeekMessage() or GetMessage() and TranslateMessage() & DispatchMessage() on any messages retrieved. Lots of people unfamiliar with windows will filter the messages, either in a range, or for a window: don't - always pump all messages for all windows on the current thread.
Then you need to call InvalidateRect to mark the areas of the window you want repainted. Games will want to pass FALSE as getting the background painted is just a waste of time when youre painting the entire scene in WM_PAINT.
RedrawWindow is useful for a game if you just want to mark an entire part of the window dirty and repaint it in one step. The advantage of InvalidateRect is that it just adds the rectangle to a dirty region, so if your game consists of just a few small 'active' sprites, as each sprite animates you can mark just the corresponding rect dirty, and windows will collect the dirty rects and paint them in a batch when next you call UpdateWindow, or let the message loop generate a WM_PAINT message.
Here's a snippet from the canonical Windows code, auto-generated when you create a new Win32 project in Visual Studio:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
Note what's missing in yours: you forgot to call ShowWindow().
If you want WM_PAINT to be called, you can use RedrawWindow or InvalidateRect.
MSDN states:
The UpdateWindow function updates the
client area of the specified window by
sending a WM_PAINT message to the
window if the window's update region
is not empty.
Your issue is that you haven't set an update region.
You could just do:
SendMessage( handle(), WM_PAINT, 0, 0 );