Native Handle of QMdiSubWindow - c++

I'm trying to get the HWND of a QMdiSubWindow which I added to a QMdiArea.
I used the following code to get the handle:
Dialog* subWindow = new Dialog(this);
QMdiSubWindow* sw = ui->mdiArea->addSubWindow(subWindow);
(HWND) hwnd = id->winId();
To test if it worked I used the following code:
SetWindowText(hwnd, "Hello, World!");
Nothing happens. If I run the following code:
SetWindowText((HWND) this->winId(), "Hello, World!");
It works correctly for the main window. The code is placed in the constructor of the main window. The type of the subwindow class is QDialog.
Any ideas how to get it working?

You probably have à correct HWND but it may actually not be the one you are looking for (one visible window could actually be made of a few nested ones: you may believe you picked up the right one when you actually wish to pick up its child or parent).
I suggest that you use Microsoft spy tool, use the finder tool to select the mdi widget you are interested in (directly on your displayed GUI), spy tool will give you its HWND. Then you can check Qt reports you the same id.

Related

Getting top-level window by widget

I'm hooking the QPainter::drawText() function of a Qt5 application on Windows.
My goal is to identify the native handle of the top-level-window to which the text is painted. First, I'm getting the associated widget.
QWidget *widget = static_cast<QWidget *>(painter->device());
So it should be possible to find the corresponding top-level window/widget.
But it's harder than I thought. This is what I tried so far:
while (widget->parentWidget())
widget = widget->parentWidget();
HWND hwnd = (HWND) widget->winId();
No success. The top-parent is never the desired window.
QApplication::topLevelWidgets()
Showed me that one single window contains several top-level-widgets (including the one I'm looking for).
I also tried QApplication::topLevelAt(widget->mapToGlobal(QPoint()))
In some cases this actually works, but not reliably.
Depending on text and window position I'm getting a AccessViolationException,
so this is not an option.
By testing widget->testAttribute(Qt::WA_NativeWindow)
I found out that most of the widgets are non-native Alien Widgets.
This is how I get the (what I call) top-level window.
WinAPI.EnumChildWindows(
WinAPI.GetDesktopWindow(),
new EnumWindowsProc(this.EnumWindowsCallback), 0);
Then I check the window titles to find the handles I'm interested in.
I'm not able to find a relation from any (low-level) widget to the (top-level) widget that holds the window title.
For the QWidget that acts as a top level window, call QWidget::window().
For the nearest parent with a native handle, call QWidget::nativeParentWidget().
Calling winId() forces the widget to acquire a native window handle if it does not have one, which isn't your goal. A top level window will always have a native id, so (HWND)window()->winId() is fine. Note that this is usually the same as calling QWidget::effectiveWinId().
It's done! I found a solution for my problem.
Each windows has it's own thread.
int threadId = WinApi.GetWindowThreadProcessId(wndHandle, IntPtr.Zero)
With it I use EnumThreadWindows
to get a list of all window-handles created by this thread.
Finally I check wheather widget->effectiveWinId() is in the list.
So I can map each widget to its corresponding window!

Should a CDialog based app set AfxGetApp()->m_pMainWnd

EDIT:
I Need to Research some weird stuff first, is there some way to put the question "on hold"?
Original:
I am working on an existing codebase using a CDialog based GUI. The Application consists of a CDialog "MainWindow", which spawns other CDialog "SubWindow"s using CDialog.DoModal.
This does work, when showing the SubWindow, the MainWindow blocks, etc.
When we call AfxMessageBox from "SubWindow", the MainWindow gets reenabled and focused.
Debugging into AfxMessagebox shows, that the function gets the mainWindow and reenables it. This causes a lot of different bugs. Using ::MesageBox works correctly, but we have about 50 different SubWindows, and, if possible, i would like to make only small, localized changes.
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc\appui1.cpp
int CWinApp::ShowAppMessageBox(CWinApp *pApp, LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
...
HWND hWndTop;
HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
// re-enable the parent window, so that focus is restored
// correctly when the dialog is dismissed.
if (hWnd != hWndTop)
EnableWindow(hWnd, TRUE);
...
In our entry Point we do something like this:
::AfxGetApp()->m_pMainWnd = &mainDlg;
mainDlg.DoModal();
What is the prefered way? Should i comment the line so the member stays NULL?
or could that cause any side effects?
I guess (not yet tested) i could also set
AfxGetApp()->m_pMainWnd = &subDlg;
prior to subDlg.DoModal() and reset it afterwards, but that would also mean changing 50 different files, one for every SubWindow.
Does MFC depend on m_pMainWnd, or should i just let it stay at NULL?
Thanks.
EDIT:
I tried passing the MainWindow to the SubWindow's constructor, but to no avail.
This is, where MainWindow gets reenabled:
This is, where MFC finds the MainWindow:
Do i need to manually set m_pActiveWnd ?
(OFF Topic: I love that there is the source code for mfc available.)
EDIT 2:
The MFC App is actually a DLL, which can be invoked in two ways:
Either loaded by a simple loader.exe, or by anyther big application.
This other application may also use MFC, so there may be two different CWinApp objects.
If it is loaded by loader.exe, the error does not occur.
MFC depends on m_pMainWnd in a lot of cases. Leaving it NULL isn't a good approach and it doen't fix your problem.
The main problem seams to be more subtle. The question is why does AfxMessageBox find the main dialog as the last active and not you subdialog. And this can be only a problem if you don't define a pParent when you create a new subdialog based con CDialog.
Try to pass the dialog that is currently active to the sub dialog you are calling. CDialog find the parent "automatically". But sometimes it doen't worked for me.
I had the same problem that the wrond dialog was enabled again after a message box or DoModal.
I fixed it, in defining always the parent when I create the sub dialogs.

Listbox in main window

I'm creating a Win32 GUI application using Code::Blocks and MinGW. I am using this tutorial as a guide. Everything worked well until I decided I needed a listbox to display files in the current directory. I don't want the list box in a window by itself, I want it inside the main window. The tutorial I was following wasn't very helpful on this part, so I still don't know how to create a listbox. Using the resource editor somehow, I think. Could someone please help me?
I assume you have a window procedure somewhere (let's call it WndProc) for your main window (let's call it hWndMain).
Add a WM_CREATE handler in WndProc if it's not already there.
In the WM_CREATE handler, add a call to CreateWindowEx to create the listbox as a child window of hWndMain:
const HWND hWndList = CreateWindowEx(...);
The picky parameters of CreateWindowEx that are essential here are the class name "LISTBOX", that the style parameter must include the WS_CHILD flag (plus the other essential listbox flags, of course) and that the parent parameter must be hWndMain.
For the other parameters, use your brain and read the docs.

Creating multiple MFC dialogs through COM, strange behaviour

Updated: please see this other thread instead, all this COM stuff is not part of the problem.
One of our apps has a COM interface which will launch a dialog, e.g:
STDMETHODIMP CSomeClass::LaunchDialog(BSTR TextToDisplay)
{
CDialog *pDlg = new CSomeDialog(TextToDisplay);
pDlg->BringWindowToTop();
}
For some reason when the COM method is called several times at once by the server, we get odd behaviour:
We get multiple dialogs, but only one entry in the taskbar
Dialog Z-order is based on order created and can't be changed... the first dialog created is always shown under the 2nd one, 2nd under 3rd, etc, even when you drag them around
if N dialogs were created, closing one of them closes it and all the others created afterwards. e.g if 5 dialogsa re created and you close the 3rd one, #3,#4,#5 all get closed.
It's somehow like the dialogs are siblings but I don't see anything weird going on. Is it perhaps due to COM, or is this a weird MFC/Win32 issue?
EDIT: If the interface method is called several times separately, it works as expected. Only when the server component sends several through at once does it seem to mess up. Could threading/timings be to blame?
EDIT2:
I put this logging in:
std::stringstream ss;
HWND self = dlg->m_hWnd;
HWND parent = dlg->GetParent() ? dlg->GetParent()->m_hWnd : 0;
ss<<"Dlg created'. HWND = "<<self<<", Parent = "<<parent<<std::endl;
OutputDebugString(ss.str().c_str());
It gave:
Dlg created. HWND = 0013014A, Parent = 00000000
Dlg created. HWND = 001B0390, Parent = 0013014A
Dlg created. HWND = 000B03B0, Parent = 001B0390
So clearly the problem is the dialogs are being made children of each other. But the question is, WHY?! It seems Windows is doing this automatically...
This question seems to be slightly away from the main issue of parenting, so I have tried to separate out the main issue into a new question.
It sounds like the first dialog has been set as the owner of the second, and the second as the owner of the third. Can you change the dialog initialization to explicitly specify the owner window? Is there a window that makes sense to assign? Perhaps the Desktop window, if they're all intended to be top-level?
If you want to be able to access all three (or more), then they would need to be modeless. Try using Create(CSomeClass::IDD, CWnd::GetDesktopWindow()), and you ought to see sibling dialogs, all of which show up on the taskbar.

Not able to Show a Dialog Box in its class using SW_SHOW in MFC?

I am trying to create a wizard like structure using dialog boxes...So I replaced the code in CDialog1App as below
CDialog1Dlg* dlg = new CDialog1Dlg;
m_pMainWnd = dlg;
dlg->Create(IDD_DIALOG1);
dlg->ShowWindow(SW_SHOW);
the above worked fine...its displying the dialog box.but I have added another dialog box...
So in the first dialog box if the user clicks Next it has to hide the first dialog box and display the second dialog..
//CDialog1 class
void CDialog1Dlg::OnBnClickedNext()
{
// TODO: Add your control notification handler code here
CDialog2* dialog2 = new CDialog2();
dialog2->Create(IDD_DIALOG2);
dialog2->ShowWindow(SW_SHOW);
this->ShowWindow(SW_HIDE);
}
in the above code am creating an object for the Dialog2 class and trying to show that....
Now the problem is,when I click next its hiding both the windows..What can I do..I tried several types but its still its not workin..Please dont suggest me to do with PropertySheet..It will work with that, i know ...but I want this using Dialog Box for some reason
You're creating the dialog2 with the default parent window (NULL):
dialog2->Create(IDD_DIALOG2);
But the default parent seems to be dialog1 in your case. And since you hide dialog1 which is the parent of dialog2, dialog2 is also hidden.
Find the window (CWnd) of either your main app dialog (if you have one visible apart from your wizard), or use the desktop window as the parent.
For example:
dialog2->Create(IDD_DIALOG2, GetDesktopWindow());