I'm a newbie in winapi handling and opengl rendering. I try to use GLFW window to render my graphics and handling messages from WinApi windows. For handle messages from winapi i am create a subclass of GLFW window and replace wndproc function like this:
window = glfwCreateWindow(WIDTH, HEIGHT, "Window", nullptr, nullptr);
GLWINDOW = window;
HWND hWnd = glfwGetWin32Window(window);
SetParent(hWnd, mainWindow);
LONG nNewStyle = GetWindowLong(hWnd, GWL_STYLE) & ~WS_POPUP | WS_CHILDWINDOW;
SetWindowLong(hWnd, GWL_STYLE, nNewStyle);
ULONG_PTR cNewStyle = GetClassLongPtr(hWnd, GCL_STYLE) | CS_DBLCLKS;
SetClassLongPtr(hWnd, GCL_STYLE, cNewStyle);
SetWindowSubclass(hWnd, &msgProc, 0, 0);
glfwShowWindow(window);
Then i create my buttons and wndproc function. That work well, i can process messages from this buttons and click on it. But when i create "edit control", it doesn't react on my mouse clicks and key inputs. What do I need to do to make it work?
My wndproc:
LRESULT CALLBACK msgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
BeginPaint(hWnd, &PS);
EndPaint(hWnd, &PS);
break;
case WM_COMMAND:
if(LOWORD(wParam)==1)
glEnabled = true;
if(LOWORD(wParam)==2)
{
printf("system: application terminated \n");
terminate = true;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The problem is your WM_PAINT routine. You don't let the WM_PAINT to be handled by the edit control.
You gave the edit control no chance to display anything. Subclassing means: Just do that what is neccesary. Let the rest be handled by the control. Also calling DefWindowProc is wrong for subclassing. You have to use
return CallWindowProc(wndprocOrig, hwnd, wm, wParam, lParam);
If this windowproc is used for the edit control it wonders me, that you handle WM_COMMAND message in it. WM_COMMAND messages are not sent to the control. They are sent to the parent window. Also there is a break missing.
Please read the MSDN for further details.
Advice for the future: As you are new to SO, please offer as much information and code as possible when you ask a question.
Related
Whole sample project can be found here: Sample project
Normal MDI child:
MDI child is detached out of MDI client area:
Problem is after MDI child is detached, I am not able to click on menu/controls anymore.
I think one approach is to subclass winproc of MDI app, and then catching the messages and redirect them (like this one). But I dont know where to begin.
Any idea/ other approaches are welcome!
The code I used to detach MDI child:
HWND MDIHwnd = pMainFrame->m_hWndMDIClient;
HWND mdiChildHwnd = GetWindow(MDIHwnd, GW_CHILD);
unsigned int style = GetWindowLongPtr(mdiChildHwnd, GWL_STYLE);
style = (style & (~WS_CHILD) | WS_POPUP);
SetWindowLongPtr(mdiChildHwnd, GWL_STYLE, style);
WaitForInputIdle(mdiChildHwnd, INFINITE);
SetParent(mdiChildHwnd, NULL);
WaitForInputIdle(mdiChildHwnd, INFINITE);
SetWindowLongPtr(mdiChildHwnd, GWLP_HWNDPARENT, (long)MDIHwnd);
Some experts here said that it's impossible and I found out the solution.
Lesson learnt: when someone said it's impossible which mean that's only impossible to them not you.
Whole sample project can be found here: Sample Project Answer
#Experts: if you are really an good expert then be helpful and objective rather than trying to telling people that you are an expert and you know things that other people don't know. Moreover, giving out some advice that's not really helping and being subjective are really frustrating to people who asking.
For reference
For the record: I don't care about the downvote, what I care is someone willing to help and knowledge that I get.
WndProc Code:
LRESULT CALLBACK MDIAppWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
WNDPROC wpOrigMDIAppWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWL_USERDATA);
if (wpOrigMDIAppWndProc == NULL)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
switch (uMsg)
{
case WM_ACTIVATE:
case WM_SETFOCUS:
return 0;
case WM_CLOSE:
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)wpOrigMDIAppWndProc);
PostMessage(hwnd, WM_CLOSE, 0, 0);
return 0;
default:
return CallWindowProc(wpOrigMDIAppWndProc, hwnd, uMsg, wParam, lParam);
}
return 0;
}
Detaching code:
HWND MDIHwnd = pMainFrame->m_hWndMDIClient;
HWND mdiChildHwnd = GetWindow(MDIHwnd, GW_CHILD);
unsigned int style = GetWindowLongPtr(mdiChildHwnd, GWL_STYLE);
style = (style & (~WS_CHILD) | WS_POPUP);
SetWindowLongPtr(mdiChildHwnd, GWL_STYLE, style);
WaitForInputIdle(mdiChildHwnd, INFINITE);
SetParent(mdiChildHwnd, NULL);
WaitForInputIdle(mdiChildHwnd, INFINITE);
SetWindowLongPtr(mdiChildHwnd, GWLP_HWNDPARENT, (long)MDIHwnd);
HWND MDIAppHwnd = GetAncestor(MDIHwnd, GA_ROOT);
WNDPROC wpOrigMDIAppWndProc = (WNDPROC)SetWindowLong(MDIAppHwnd, GWL_WNDPROC, (LONG)MDIAppWndProc);
SetWindowLongPtr(MDIAppHwnd, GWL_USERDATA, (LONG)wpOrigMDIAppWndProc);
I've got an edit control where I'd like to disable any rendering, but leave it as interactive (similar to an owner-drawn control). Right now, I have subclassed the Window and captured WM_PAINT, such as
SetWindowSubclass(box, [](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR, DWORD_PTR) -> LRESULT {
if (msg != WM_PAINT)
return DefSubclassProc(hwnd, msg, wparam, lparam);
PAINTSTRUCT paint;
BeginPaint(hwnd, &paint);
EndPaint(hwnd, &paint);
return 0;
}, 0, 0);
This has worked fine to disable almost all of the edit box's rendering. However, when interacting with it, it still occasionally clears the area where it would render, causing airspace issues when I am rendering in that space with Direct3D.
How can I complete the subclass such that the edit box never renders anything?
A friend of mine is about to release an application and asked me to create a launcher for it. I found it a good excuse to finally study WinAPI and thought that a simple launcher would be easily doable even in a relatively small time window.
I was wrong.
I'm trying to create a launcher window with 5 buttons that start different things. The goal is to have transparent buttons (not done yet) that have a smaller image inside them. The image should only be displayed when the user hovers over the larger button area.
The images are in .png format. I'm using GDI+ and loading PNG files from resources with http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI.
I'm using MouseTrackEvents to keep track of the mouse and I've also subclassed a button. The problem is that I don't know how I should handle the WM_MOUSELEAVE message. I don't know how to erase the image I've drawn. If I have to save the ht_img as a variable and refer to it later, I don't know how.
Here's what I have so far. This example loads the .png from resource IDB_Website2. Displaying the image works (although it keeps being rendered over and over again currently):
WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
GDI gdi;
switch (Msg)
{
case WM_CREATE:
{
HWND hwndButton = CreateWindow(TEXT("button"), NULL,
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
80, 10, 100, 50,
hWnd, (HMENU) HT_BUTTON1, NULL, NULL);
HTButton = (WNDPROC) SetWindowLong(hwndButton, GWL_WNDPROC, (LONG) ButtonProc);
}
...
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
gdi.InitList(hInst, hdc);
EndPaint(hWnd, &ps);
break;
Buttonproc (subclassed button):
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MouseTrackEvents MouseTrack;
GDI gdi;
HDC odc = GetDC(GetParent(hWnd));
switch(Msg)
{
case WM_MOUSEMOVE:
MouseTrack.OnMouseMove(hWnd);
break;
case WM_MOUSEHOVER:
gdi.Create(IDB_Website2, _T("PNG"), hInst, odc, 62, 347, 200, 40, true);
MouseTrack.Reset(hWnd);
break;
case WM_MOUSELEAVE:
MouseTrack.Reset(hWnd);
break;
}
return CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
}
class GDI's Create graphic method:
void Create(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
Graphics grpx(hdc);
ht_img = new CGdiPlusBitmapResource();
ht_img -> Load(menuid, pType, hInst);
grpx.DrawImage(*ht_img, x, y, w, h);
delete ht_img;
}
This has been quite a challenge so far! It's been fun although a bit tear-my-hair-out inducing at times. :-) I'd be grateful for any advice on how I should proceed.
EDIT: Answering Adrian
I tried changing my Buttonproc, but the image doesn't seem to be rendered. Here's what I did:
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MouseTrackEvents MouseTrack;
GDI gdi;
HDC odc = GetDC(GetParent(hWnd));
PAINTSTRUCT ps;
int result;
switch(Msg)
{
case WM_MOUSEMOVE:
MouseTrack.OnMouseMove(hWnd);
break;
case WM_MOUSEHOVER:
hovering = true;
break;
case WM_MOUSELEAVE:
hovering = false;
MouseTrack.Reset(hWnd);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
result = CallWindowProc(HTButton, hWnd, Msg, wParam, lParam);
if (hovering == true) {
gdi.Create(IDB_Play2, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
}
EndPaint(hWnd, &ps);
return result;
}
return CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
}
You probably don't want to do the painting directly in the handling of the mouse events. You probably want to handle WM_PAINT in the button proc by calling the underlying implementation and then augmenting it based on the hover state. Then your mouse handling corresponds to flipping a state variable and invalidating the button (which will cause it to repaint).
case WM_PAINT:
// start with the standard rendering
int result = CallWindowProc (HTButton, hWnd, Msg, wParam, lParam);
// then overdraw our embellishments
if (my_state_variable == hovering) {
DrawOverlayImage();
}
return result; // don't just break here, or you'll call CallWindowProc again
I'm working on a windows application where I'm implementing the whole event loop and everything like that myself (there's a reason for that). In one place, I need to execute some code AFTER a window is shown. Normally, when the window is created, I do some initialisation when WM_CREATE message is received. WM_SHOWWINDOW is sent just BEFORE the window is displayed. However I need to get some code executed right AFTER the window is displayed for the first time. I can't seem to find a notification message sent AFTER the window is shown. Could it be that there isn't one?
Of course, I can keep a boolean - FirstRun - indicating whether or not I had performed my logic, and then execute the code when WM_ACTIVATE is received, provided the boolean is TRUE, then set FirstRun to FALSE so that the code is not execute the next time I am receive WM_ACTIVATE, but this seems somewhat unnatural to me.
It's been ages since I did win32 programming on this level, so can't remember much of it. What is the best approach here?
There is no special notification, but in many cases you can use this trick:
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_USER + 100:
//window is just displayed, do some actions
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_CREATE:
PostMessage(hwnd, WM_USER + 100, 0, 0);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
There isn't one because that's what WM_SHOWWINDOW is for. Once that mesage is passed to the default message handling procedure, the window will be shown. The best thing you could do is poll with IsWindowVisible via some sort of timer.
Your program design seems flawed though to have to rely on something like this. What are you trying to do?
As long as you are implementing the whole event loop and everything like that yourself, you can handle this directly in WinMain() like this:
HWND hWnd = CreateWindow(...);
if (!hWnd) return 0;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
OnWindowJustDisplayed(); // here
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
I created a window with the following flags to overlay a d3d application:
WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_LAYERED
I proceeded with colorkeying the window for transperacy and all worked well.
However once I began drawing on it using GDI an unforeseen problem occurred:
For some reason the mouse events (especially movement) are not passed correctly through the window when WM_PAINT is in progress, and so it appears as though the mouse and the keyboard for that matter lag. the FPS is fine, this is some API problem, I suspect that for some reason the keyboard/mouse messages are not handled as they should while the WM_PAINT is in progress, because the slower the timer is set to the less jerking there is.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
{
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
break;
}
case WM_CREATE:
{
SetTimer(hwnd, ID_TIMER, 10, NULL);
break;
}
case WM_TIMER:
{
InvalidateRect(hwnd, 0, 1);
break;
}
case WM_PAINT:
{
paint(hwnd);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
and
void paint (HWND hwnd)
{
PAINTSTRUCT Ps;
HDC hdc = BeginPaint(hwnd, &Ps);
SetBkColor(hdc, RGB(0,0,0));
SetBkMode(hdc, TRANSPARENT);
LOGBRUSH log_brush;
log_brush.lbStyle = BS_NULL;
HBRUSH handle_brush = CreateBrushIndirect(&log_brush);
SelectObject(hdc, handle_brush);
..........................................
DeleteObject(font);
DeleteObject(pen);
DeleteObject(handle_brush);
EndPaint(hwnd, &Ps);
}
Thank you for any help you may be able to give.
WM_PAINT messages are never delivered to your window unless someone calls UpdateWindow or there are no keyboard or mouse messages in your input queue.
Once you begin processing WM_PAINT, if a keyboard or mouse message arrives, it just sits in your queue until you are done with WM_PAINT. So What you are describing isn't possible.
If your WM_PAINT code takes a long time to execute, that could cause jerkiness, but you say that's not a problem so perhaps it's your handling of WM_ERASEBKGND? I don't see that code, but I do see that when you InvalidateRect, you are passing TRUE as the last parameter which means that you want the background to be erased.
If you don't handle WM_ERASEBKGND, then DefWindowProc will do it for you erasing your entire window with the brush from from your window class. This could result in windows thinking that no part of your window is transparent.
If you want mouse messages to pass through your window, a more reliable way is to handle the WM_NCHITTEST message and return HTTRANSPARENT where you want the mouse to pass through.
This is basically what how the WS_EX_TRANSPARENT style works. like this
case WM_NCHITTEST:
{
lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (HTCLIENT == lRet)
lRet = HTTRANSPARENT;
}
If your window has no non-client area, then you can skip the call to DefWindowProc.
WndProc() is not always re-entrant. I believe with the main message pump, the mouse and keyboard messages are queued up and waiting for you to finish the prior WM_PAINT message. Conversely, if you were to call SendMessage() from WndProc(), then you are looking at re-entrace. The other case is PostMessage() which would add the message to the queue. Maybe look at using DirectInput for mouse and keyboard input if this is an issue. Otherwise, look for ways to speed up your drawing.