VB6 Host with MFC DLL Showing Modal Dialog Incorrectly - mfc

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

Related

Intercepting Windows Messages from Webview2 (Edge/Chromium)

I have recently migrated a project of mine to WebView2 and the last part I can't figure out is how to intercept the Windows Messages for the webview. My code is very similar to webview/webview but I was unable to find help on their GitHub.
Previously, I was able to find the hWnd for the webview and use SetWindowSubclass to add my own wndproc to the webview. However, I've used Spy++ and tried SetWindowSubclass on all the windows that showed up there (see below) but none of them had any windows messages in my wndproc other than some window management ones I did not think were useful - The best I got was WM_PARENTYNOTIFY, but I am interested in WM_MOUSEMOVE and WM_NCHITTEST - neither of which I could find.
My goal is to create a borderless, draggable, resizeable WebView2 based app.
The problem is, that the real window that controls and gets all this input is in another process. You just see a window that shows the output in your process.
Look into Spy++. Everything below Chrome_WidgetWin_0 belongs to a new process (MSEDGEWEBVIEW2) and is not part of your process. So you can't subclass such a window with the normal techniques.
So if you want to subclass this window. You need to inject a new DLL into this new process. This DLL might subclass the real window. And this DLL might communicate with you hosting program via any IPC.

Asserts when using an extension dll in MFC

I am trying to create a new frame window with toolbars inside a dll.
I was able to create the frame and the toolbars but however the messages do not work properly in the CToolbar. Particularly the ON_UPDATE_COMMAND_UI messages are never called in the DLL.
After some research I came to know that this is because
PreTranslateMessage(MSG* pMsg)
and
OnIdle(LONG lCount)
need to be called.
But my calling application is Delphi based and this cannot be done.
After research I came to know that this is best possible from an Extension dll.
Since MFC extension dlls can only be called from an MFC application. I thought of the following solution.
Delphi calls an regular MFC dll
The MFC dll calls the Extension dll.
But I have run into problems because of asserts in in MFC AfxGetResourceHandle() and AfxGetInstanceHandle().
But I am also aware that AFX_MANAGE_STATE(AfxGetStaticModuleState()); cannot be called from an extension dll.
Does anybody have a solution for this problem?
The ON_UPDATE_COMMAND_UI messages are created by the MFC message loop. You don't have one. You will have to build your own ON_UPDATE_COMMAND_UI translator or something equivalent. It all starts with this in your frame window message map:
ON_WM_INITMENUPOPUP()
Your OnInitMenuPopup handler will be called when the user selects a menu, before the menu is displayed.

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.

MFC modal dialog close error

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.

Can't display modal QProgressDialog in MFC application

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.