Jump to content
  • Advertisement
Sign in to follow this  
Ryan_001

Window Z-Order problem

This topic is 2526 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on some Windows code (Win32 API) and I'm having issues with child windows and z order. I have a main (parent) window and 2 child windows (they don't have the WS_CHILD style, as I want them to be movable outside the parent window, I think the term MSDN used is 'owned' window). When the child windows are destroyed the parent window seems to lose or alter its z-order in relation to any windows beneath it. I made a video so its easy to see what's happening:

[media]
[/media]

As you can see, when the 2 child windows are closed, the parent window is placed below the Explorer window. Its not destroyed and is still there, and can be brought back to the foreground, but its still not something I want to happen. Another thing that's funny is that it doesn't happen with just a single child window (at least 2 were required, I didn't test 3 or more though), and it doesn't happen 100% of the time either (my guess would be around 75% of the time). To be honest I'm not really sure where to start looking for errors. I've seen nothing in the MSDN documents to explain it. I'm certain that I've done something stupid or forgotten something obvious, but at this point I'm pretty stumped.

Any thought, ideas, or questions would be appreciated.

Share this post


Link to post
Share on other sites
Advertisement
Window creation:

hWnd = CreateWindowA( (LPCSTR)atom,
"Default Window Name",
styles,
r.left, // x
r.top, // y
r.right - r.left, // width
r.bottom - r.top, // height
hParent, // parent
0, // menu
hInstance,
0 );


r is the window Rect, styles is a DWORD with WS_OVERLAPPEDWINDOW for the parent window and WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_BORDER for the child windows.


Message loop:

MSG msg;

while (runApp) {

// process windows messages
if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {

switch (msg.message) {

// WM_QUIT
case WM_QUIT:
return;

default:
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
}
}



Window proc:

// translate and dispatch message to appropriate Window
switch (message) {

case WM_CLOSE:
internalWindow->WMClose();
return 0;

case WM_DESTROY:
break;

case WM_ENTERSIZEMOVE:
internalWindow->WMEnterSizeMove();
break;

case WM_EXITSIZEMOVE:
internalWindow->WMExitSizeMove();
break;

case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE) internalWindow->WMActivate(false);
else internalWindow->WMActivate(true);
break;

case WM_SHOWWINDOW:
case WM_WINDOWPOSCHANGED:
internalWindow->WMSizeMoveChange();
break;

case WM_PAINT:
internalWindow->WMPaint();
break;
}

// perform default processing
return DefWindowProc(hWnd,message,wParam,lParam);

Share this post


Link to post
Share on other sites
Are you using the same wndproc for all three windows? If not, where is the other one (assuming there's one for the parent and one for both children. ALso can we see the whole thing i.e. where internalWindow is initialized.

Share this post


Link to post
Share on other sites

What does internalWindow->WMActivate do?

Note that you receive WM_ACTIVATE also for your child windows.

Maybe its WM_ACTIVATEAPP you want?


InternalWindow is just a simple Win32 API wrapper class. In the window proc I map the HWND to its InternalWindow. The WMActivate member function doesn't actually do anything, its just there to indicate when a window gets focus, or has lost focus. Its actually just an empty function, that hasn't been overloaded by either the parent or child window. I don't think WM_ACTIVATEAPP is what I'm looking for, I'd like each window to be able to track independently whether it has focus or not.



Are you using the same wndproc for all three windows? If not, where is the other one (assuming there's one for the parent and one for both children. ALso can we see the whole thing i.e. where internalWindow is initialized.


Yes I'm using the same windows proc for all three. When u say InternalWindow is initialized I'm assuming you mean the constructor?



InternalWindow::InternalWindow (InternalWindow* internalParent, HINSTANCE hInstance, ATOM atom, DWORD styles_) {

// intialize
hWnd = 0;
device = 0;
styles = styles_;
HWND hParent = (internalParent) ? internalParent->GetHWND() : 0;

// calculate window size (window width/height cannot be zero or smaller, as CreateDevice will fail)
RECT r;
r.left = 0;
r.top = 0;
r.right = 10;
r.bottom = 10;

if (AdjustWindowRect(&r,styles,FALSE) == FALSE) throw_detailed(Exception("Cannot calculate window size."));


// CW_USEDEFAULT does not work for width/height here, as WS_POPUP style will cause the CreateDevice function
// to fail (apparently it has no area or something, I dunno, seems stupid to me)
// the width/height also has to be large enough to have area after border size is taken into effect
hWnd = CreateWindowA( (LPCSTR)atom,
"Default Window Name",
styles,
r.left, // x
r.top, // y
r.right - r.left, // width
r.bottom - r.top, // height
hParent, // parent
0, // menu
hInstance,
0 );

if (hWnd == 0) throw_detailed(Exception("Cannot create window.")); // ensure hWnd has been created
}

InternalWindow::~InternalWindow () {
if (device) delete device;
if (hWnd) DestroyWindow(hWnd);
}


The window's are resized and shown elsewhere, I can post that code to if required.

Share this post


Link to post
Share on other sites
But in the window procedure, where is "internalWindow", the pointer to an instance of the class InternalWindow, coming from? Are you grabbing it with GetWindowLongPtr( ..., GWLP_USERDATA), for example? Also is anything happening inside of InternalWindow::WMClose()? Generally, if you comment out all calls to InternalWindow::WMxxx in the window procedure do you still see the same behavior?

Share this post


Link to post
Share on other sites
[quote name='jwezorek' timestamp='1326854970' post='4903867'] But in the window procedure, where is "internalWindow", the pointer to an instance of the class InternalWindow, coming from? Are you grabbing it with GetWindowLongPtr( ..., GWLP_USERDATA), for example? Also is anything happening inside of InternalWindow::WMClose()? Generally, if you comment out all calls to InternalWindow::WMxxx in the window procedure do you still see the same behavior? [/quote]

The mapping doesn't use GetWindowLongPtr, rather its a class (as it tracks a little more than just hWnd/internal window mappings). There's a WinMainHandler handler class that wraps the window proc, message queue, ect.. which is the 'self' below (since the windows proc is a static function.

InternalWindow* internalWindow = self->iWinMap.GetInternal(hWnd);

Internally its actually just a sorted vector, accessed like this:


const InternalWindow* InternalWindowMapping::GetInternal (HWND hWnd) const {
auto i = std::lower_bound(hWndData.begin(),hWndData.end(),HWndIPair(hWnd,nullptr));
if (i != hWndData.end() && i->first == hWnd) return i->second;
return 0;
}


InternalWindow::WMClose() simply calls delete on the internal window (causing the destructor to call DestroyWindow).

I will try commenting out the WM.... calls and see what happens.
edit: tried it, same thing/no change

Share this post


Link to post
Share on other sites

const InternalWindow* InternalWindowMapping::GetInternal (HWND hWnd) const {
auto i = std::lower_bound(hWndData.begin(),hWndData.end(),HWndIPair(hWnd,nullptr));
if (i != hWndData.end() && i->first == hWnd) return i->second;
return 0;
}

If you're going to do the mapping with a separate data structure, why not use an std::map?

Also, I meant to ask this before ... is the main window being minimized or is it just moving to the bottom of the Z-order as implied by the thread title?

But, anyway, what you're seeing doesn't look like normal Windows behavior, so something that you are doing is doing it. I'm afraid I can't be more help without seeing the whole codebase. I guess, if I were debugging this I'd approach this as follows:
1.) Figure out exactly what is happening with the main window. Use Spy++.
2.) Make sure the mapping of HWND's to custum class instances is working the way you think it is.
3.) Start commenting things out until you get normal windows behavior.

Share this post


Link to post
Share on other sites

[quote name='Ryan_001' timestamp='1326866169' post='4903894']
const InternalWindow* InternalWindowMapping::GetInternal (HWND hWnd) const {
auto i = std::lower_bound(hWndData.begin(),hWndData.end(),HWndIPair(hWnd,nullptr));
if (i != hWndData.end() && i->first == hWnd) return i->second;
return 0;
}

If you're going to do the mapping with a separate data structure, why not use an std::map?

Also, I meant to ask this before ... is the main window being minimized or is it just moving to the bottom of the Z-order as implied by the thread title?

But, anyway, what you're seeing doesn't look like normal Windows behavior, so something that you are doing is doing it. I'm afraid I can't be more help without seeing the whole codebase. I guess, if I were debugging this I'd approach this as follows:
1.) Figure out exactly what is happening with the main window. Use Spy++.
2.) Make sure the mapping of HWND's to custum class instances is working the way you think it is.
3.) Start commenting things out until you get normal windows behavior.
[/quote]

I'm not using just a std::map because it stores a little more information than just HWND -> InternalWindow data. And its not being minimized, just losing its z-order. I've never heard of Spy++ till now, but it seems like it could be an interesting tool. Thank-you for your time, I really do appreciate it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!