I'm having problems displaying a modal Qt dialog while starting up a Qt application from an MFC application. Specifically, a QProgressDialog instance won't display within the MFC application when I set its parent to a QWinWidget instance. Here's my problem in more detail...
My MFC application needs to transfer a lot of data over to the Qt application, which is a DLL. The Qt application includes a ProgressDlg class in its API that behind the scenes is implemented using a QProgressDialog. This dialog must be created and updated before the Qt application's event loop is initialised so that the MFC application can update its progress (The QApplication::exec() help says this is possible with modal widgets).
Without setting the underlying QProgressDialog's parent, the progress bar gets updated as I would expect and the dialog remains responsive during the transfer, but I can continue to interract with the MFC application.
So I tried installing the Qt/MFC Migration Framework and setting the QProgressDialog's parent to be a QWinWidget:
void ProgressDlg::SetParent(HWND hParentWnd)
{
QWinWidget* w = new QWinWidget(hParentWnd);
m_impl->setParent(w);
}
(where m_impl derives from QProgressDialog.)
And then adding the calling code on the MFC side to create the dialog:
HWND hWnd = FindWindow(NULL, "ABC");
if(hWnd)
{
ProgressDlg dlg;
dlg.SetParent(hWnd);
//...
dlg.SetValue(0);
//...
}
However in setting the parent, the QProgressDialog no longer appears. (I retrieved the handle using ::FindWindow, passing in the Window Name, as to complicate the scenario further, my MFC application is actually a plugin DLL to a 3rd party executable.)
All help appreciated. Thanks.
Gotcha! The problem was caused by the call to SetParent(). I needed to instead create the QWinWidget before the QProgressDialog and pass the QWinWidget instance in to the QProgressDialog's constructor as its parent.
As the help says, QWidget::setParent resets the window flags, so the dialog was no longer being recognised as a dialog.
Related
I'm using raw Win32 and C++ for a project. As I understand it, I am able to superclass Windows controls by retrieving the class information, replacing the procedure, then registering this as a new class and using it when creating a new window. Subclassing is done by replacing the window's procedure after the window is created. The advantage of superclassing is that you are able to process messages before CreateWindow() returns.
I'm looking to see if it's possible to superclass a dialog box created with CreateDialog() because I'd like to use a resource file for the dialog layout. The problem is that I don't know how I would provide my superclass when I create a dialog box. Is it even possible? Any idea how MFC handles this?
If you use an extended dialog box template to create your dialog, you can specify a custom window class as part of the DLGTEMPLATEEX definition.
The dialog manager will create and layout your dialog as normal, and call your window procedure for any dialog messages. You can use the DefDlgProc function to obtain default processing for any dialog messages you don't want to handle yourself.
I am in a need to create two different type of windows, one MDI and one SDI in one application. I have tried to create an MDI application with document/view and put some codes to create an SDI window, but it failed. It seems MDI and SDI are created with different approach and I have no idea how to find a way to resolve it. Does anybody know the best way to do it?
After some tries, I managed to successfully create an SDI and a MDI window, but I am not sure if this is the correct way to do. This is how I've done
Create an SDI application using Visual Studio's AppWizard, and I put the following code to create a MDI window when user clicks on SDI Frame's menu
CSDIFrame::OnClickCreateMDI()
{
CFrameWnd* pFrameMDI = new TestMDIFrameWnd;
CCreateContext Context;
Context.m_pNewViewClass = RUNTIME_CLASS(CTestMDIView);
if (!pFrameMDI->LoadFrame(IDR_TESTMDIFRAME, WS_OVERLAPPEDWINDOW, NULL, &Context)) {
AfxMessageBox("LoadFrame failed");
return FALSE;
}
pFrameMDI->InitialUpdateFrame(NULL, TRUE);
}
Is this correct way to do it? Can all the MFC programming methods be used on this newly created MDI window just like this MDI window is created using AppWizard? Will there be any limitation (such like some meesages can not be sent to this MDI window....)
Thanks.
After some tries and study of MFC books and MFC source code, I have successfully create a MDI window from an SDI window. I can add multiple documents multiple panes to MDI. And exiting the application also OK without any error. It is not easy, there are some tricky parts to make this happen.
My MFC application is a MDI application. and I have a MFC Extension dll, MFC Extension dll will launch the child dialog on top of the MFC MDI application. like as below
CMyDialog pDisplayGlobal = new CMyDialog(IDD_DISPLAY, NULL);
pDisplayGlobal->Create(IDD_DISPLAY, AfxGetApp()->m_pMainWnd);
pDisplayGlobal->ShowWindow(SW_NORMAL);
Note : Kindly let me know if I am doing anything wrong in above code.
Problem:
I have launched my MFC mdi application and child modeless dialog as well. Modeless dialog always on top of parent window only (as per the above code)
Step1) I have opened other four different applications (Which means my MFC application is behind these four applications)
Step2) I clicked on my MFC application from the Taskbar it’s not showing the main application window. which means it didnt come in front its still in step1 stage only
Step3) To see My MFC application I have to minimize all the four applications
That’s the problem, kindly somebody give me some code snippet as a solution.
Thanks in advance.
To run a dialog from another program you can do the following
in DLL:
CDialog *g_dlg;
void dll_foo()
{
g_dlg = new CDialog;
g_dlg->Create(IDD_DIALOG1);
g_dlg->ShowWindow(SW_SHOWNORMAL);
}
in main app:
BOOL CMyWinApp::InitInstance()
{
//...
frame->ShowWindow(SW_SHOWNORMAL);
frame->UpdateWindow();
dll_foo();
return TRUE;
}
I have a strange error and spend hours in the debugger without finding a solution.
(But it helped me to fixed another error that you should never call EndDialog from a WM_KICKIDLE task).
My problem is that i have a main window and a modeless dialog window wich raises a modal subdialog window. When the subdialog window is closed. The modeless dialog window turns itself into a modal window. My code really does leave the modal loop. And if i close the now modal window it behaves like an invisble modal window is active, meaning no interaction is possible anymore.
When i only run a modal dialog on top of the main window it is closed fine.
BTW: The main window is not the one available view CWinApp::m_pMainWnd but a new create FrameWindow. I hide the p_MainWnd and use it as an invisible message only window. From some comments and my debugging session i found that the pMainWnd has some special meaning but i could figure what exactly it has to do with modal windows (there is an undocumented "CWinApp::DoEnableModeless" for example).
EDIT: I'm posting a WM_CLOSE to the dialog and then use EndDialog(0) from the OnClose() handler to exit the modal state. I also tried to use EndDialog(0) directly. There is no difference between this two methods.
When MFC creates a modal dialog, it makes it modal by disabling the windows above it. The code that reenables those windows occurs when the dialog ends normally with a call to EndDialog. If anything prevents that code from running, the other windows will be locked out.
Modeless dialogs are a different beast, and there's a note specifically in the EndDialog documentation warning you to use DestroyWindow instead.
Maybe this is justifiable but I have a question:
why are you using hidden window? Was it created as message only window (passing HWND_MESSAGE as a parent handle and Message as a class) or you just call it message only?
OK, a little more info about MFC and dialogs.
MFC does not use Windows modal dialog. It always creates modeless dialog; either Create or DoModal call in turn ::CreateDlgIndirect windows API.
Modeless dialof rely on the main window message dispatch, while modal calls RunModalLoop that is similar to MFC window message pupmp (not a message loop).
It runs in the main thread of execussion without freezing because it allows for idle processing (calls OnIdle).
How do you dismiss the modeless dialog? As Mark pointed you should use DestroyWindow.
As for m_pMainWnd, MFC framework uses it extensively to determine may things that control main window behavior. By changing it you may have created the behavior you experience.
Did you set the value to a newly created frame you treat as a main window?
What kind of MFC application is it? SDI or MDI?
Would it be possible to create test app to duplicate this behavior and post it somewhere for download?
By the way, you do not have to be concern about DoEnableModeless, since it does not do anything but calls hook (COleFrameHook type) that is spasly used, unless you are trying to implement some functionality using OLE or ActiveX or you are trying to marry MFC and .NET Windows Forms.
In conclusion if your (or third party code uses this hook, I would suggest checking the code in the COleFrameHook class.
We have a VB6 application that we have provided extended functionality through an MFC DLL. However, there's a specific problem with CDialog-based classes in the DLL. We pass in Me.hWnd from the VB6 app's main form to give to the CDialog constructor so that DoModal() knows what its parent is. Although the CDialog-based classes are staying on top of the VB6 app on DoModal(), they do not block the VB6 app the way that's expected by a modal dialog. That is, while the DLL dialog remains in front of the EXE's window, I can still click the button that called the DLL, showing the dialog again (and again).
There's not much to show from the VB6 code. As I mentioned it just passes in Me.hWnd. The MFC code is simple enough:
HWND exeHwnd = pSessionContext->GetHWnd(); // our state container for the DLL
CWnd* exeWnd = CWnd::FromHandle(exeHwnd);
MyCDialog dlg(exeWnd);
INT_PTR result = dlg.DoModal();
// waits, stays in front, but does not "block" the window
switch (result) // ...
I traced the values along the way and confirmed window handles with Spy++. Everything seems to be fine. Any ideas what I'm missing or doing wrong?
ADDITIONAL INFO
That hwnd value is used elsewhere for MessageBox and works as expected.
A simplistic solution might be to disable the parent VB6 app in the VB6 code just before calling the DLL, and when the DLL code returns re-enable the VB6 app. Something like this (air code)
Me.Enabled = False
MagicDLL.ShowTheDialog(Me.hWnd)
Me.Enabled = True