How to Cleanly Destroy WebBrowser Control - c++

I am using ATL in VisualC++10 to host browser control.
My code is similar to this example: http://msdn.microsoft.com/en-us/library/9d0714y1(v=vs.80).aspx
Difference is I have main window and then child window hosts the browser control.
After 2 minutes i have to close the browser completely kill the browser activeX but this child window should be alive and do something else. But somehow this browser control still stays there, i can either see scrollbars or something..
I have also tried by creating child window to an existing child window, and at the time of closing browser I then destroy this child of a child - but still it does not work!
This is how I am closing:
CLOSE()
{
m_spIWebBrowser2->Navigate(bstrURL, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
m_spIWebBrowser2->Stop();
m_spIWebBrowser2->put_Visible(VARIANT_FALSE);
m_spIWebBrowser2->Quit();
DestroyWindow(m_wndChild.m_hWnd);
}
Thanks!

I had many problems with "access violation" when closing webbrowser control, these are the steps that worked for me:
Unadvise any previously advised events (DWebBrowserEvents2 in my case).
If you've attached click events unattach them like this: _variant_t v; v.vt = VT_DISPATCH; v.pdispVal = 0; IHTMLDocument2->put_onclick(v);
IWebBrowser2->Stop()
IWebBrowser2->ExecWB(OLECMDID_CLOSE, OLECMDEXECOPT_DONTPROMPTUSER, 0, 0) - when closing browser window through window.external.CloseWindow() I had unhandled exceptions and OLECMDID_CLOSE fixed it.
IWebBrowser2->put_Visible(VARIANT_FALSE)
IWebBrowser2->Release()
IOleInPlaceObject->InPlaceDeactivate()
IOleInPlaceObject->Release()
IOleObject->DoVerb(OLEIVERB_HIDE, NULL, IOleClientSite, 0, windowHandle_, NULL)
IOleObject->Close(OLECLOSE_NOSAVE)
OleSetContainedObject(IOleObject, FALSE)
IOleObject->SetClientSite(NULL)
CoDisconnectObject(IOleObject, 0)
IOleObject->Release()
IWebBrowser2->Quit() should not be called for WebBrowser control (CLSID_WebBrowser), it is intended only for Internet Explorer object (CLSID_InternetExplorer).
Why must it be so hard?

My experience is that some calls might need message processing to function properly. Try to pump some messages between your calls to Navigate, Stop etc. When working with the web browser interfaces I PostMessage myself often to trigger the next step to make sure the previous step had time to complete.
The problem might be related to your child thread. You cannot access web browser interfaces between threads without some additional work. COM needs to be initialized as single-threaded apartment (STA). And you need to follow the rules of STAs:
Every object should live on only one thread (within a single-threaded apartment).
Initialize the COM library for each thread.
Marshal all pointers to objects when passing them between apartments.
Each single-threaded apartment must have a message loop to handle calls from other processes and apartments within the same process. Single-threaded apartments without objects (client only) also need a message loop to dispatch the broadcast messages that some applications use.
...

If I use DialogBox and drop a IEControl on it as a resource and DialogBox is derived from CAxDialogImpl<> - then while I call DestroyWindow() of dialogBox then it is automatically doing the cleanup() - which is what I required.
But originally I wanted to get rid of DialogBox itself and use IEControl directly on my Window, it seems not..

Related

How to run an ActiveX component method without blocking the possessing control?

BSTR CCtrl::mosMsgFromHost(BSTR mosMsg)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(mosMsg);
mainDialog.GetWebView()->PostWebMessageAsString(mosMsg);
std::unique_lock<std::mutex> ul(m);
cv.wait(ul);
AfxMessageBox(mainDialog.receivedMessage);
return mainDialog.receivedMessage.AllocSysString();
}
I have laid out a WebView2 component in ActiveX Control. When its following method is called, it forwards the message it gets to WebView2 to be processed and returned by means of an event that notifies cv, which is the condition variable so that it continues to the rest below cv.wait(ul).
Everything is fine and working like a charm but the issue here is blocking the entire ActiveX control while it is waiting. I cannot tweak at the client side that uses the ActiveX control we're designing, so I cannot make it Asynchronous ActiveX control. So is there any recommendation to solve this issue?
If the control is apartment threaded I doubt you are going to make this work. Calls to apartment objects either need to be made on the apartment thread (which generally needs to be the main application thread) or use a proxy (even if from within the same process) that transfers the call to the apartment thread.
I do not know whether your object is in fact apartment threaded or not, although that is extremely common for ActiveX UI controls.
Are you sure the object doesn't offer a connection point that could be used instead?
If you are interested in still, you can use the following workaround that allows us to achieve what we want to do:
while (some_condition)
{
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
If your some_condition becomes false through an event, however, it will cease the loop. That way, you don't have to freeze the whole application that is the owner of the thread we're currently operating on. It can be deemed as tricky, it serves our purpose well.

Is sending message to a non-existent window Ok?

I have a thread that send update messages to a window, I use ::SendMessage() and ::PostMessage() APIs.
I go in and out of multiple dialogs and register the dialog that I am currently in with the thread via the window handle (m_hWnd). If I exit all the way out, the main application window doesn't handle these messages. For that reason I don't register that window. At this point the thread will have the handle of an older window which now doesn't exist.
Is it okay if it sends messages to that non-existent window? I am assuming it should not do any harm but wanted to double check.
No, it is not ok to post a message to a deleted window.
There is no guarantee noone will set up shop at that address just after the previous tennant is gone.
If you use a NULL window handle, you'll post a thread message to the current threads message queue. SendMessage as far as I could google shoul be a no-op.
Might be harmless enough.
Now, we get tricky:
Under specific cicumstances it does not matter, pre-supposing well-behaved applications.
A message like WM_NULL should not make anything happen.
A window-message you globally registered in your application using RegisterWindowMessage, if you can guarantee none of your applications windows created in the meantime will choke on it.

Run custom code when MFC app is terminated: d'tor or WM_CLOSE?

I have a dialog-based MFC application which needs to stop Windows Wifi service in order to run correctly, but I want to enable it again when my application exits.
So I thought I'd put the code that restarts the service in the destructor of the main dialog class.
Now it has come to my attention that others put their code which should be run during program termination into a WM_CLOSE message handler.
Both ways seem to work, but I would like to know if there are downsides to either way.
For MFC dialog based application you can place finalization code to application class InitInstance method, immediately after main dialog DoModal call. For other MFC application types (MDI, SDI) finalization code is usually placed to ExitInstance method.
The difference between dialog based application and SDI/MDI applications, is that InitInstance in dialog based applications returns FALSE, and application exits - all action is done in the main dialog DoModal call.
You can prefer to use ExitInstance for all application types, it should work as well.
Edit. If you want to make cleanup code inside of the dialog class, WM_DESTROY (already mentioned by Roger Rowland) is better place than WM_CLOSE. Sometimes we can handle WM_CLOSE message and prevent a dialog to be closed, for example, by asking "Exit the program? Yes - No". In the case you want to use some child windows, they exist in WM_CLOSE and WM_DESTROY message handlers, and don't exist in a dialog destructor. Also, message queue doesn't exist when main dialog destructor is called, so you should not use Windows messaging in this case.
Aim to maintain symmetry: if you are stopping the wifi service in a constructor, then restart it in the same class's destructor. If instead you stop the service in InitInstance, you would restart it in ExitInstance; if as a response to WM_CREATE or some other message, then restart it in WM_CLOSE, etc.
Constructors and destructors have no way of returning an error status, so normally they are better suited for simple tasks such as initialization and memory allocation/deallocation.
InitInstance and ExitInstance, as well as windows messages such as WM_CLOSE, happen at a good spot in the application's lifetime to display error messages if necessary, or to abort in response to error conditions.

WM_TIMER stops suddenly in ATL ActiveX control

I originally had an ActiveX control that registered a Windows timer (with SetTimer()) that fires every few seconds. That worked fine so far. Now in order to implement a full screen mode, I added a child window to my control that is supposed to show the content while the control itself manages all the ActiveX stuff.
The problem that I have with this approach is that my WM_TIMER suddenly stops firing at some time. I have traced it back to UIDeactivate() being called on my control but I don't know why this method is called (I believe it has something to do with losing focus) when it wasn't called before.
I would also like to know why my WM_TIMER events suddenly stop while everything else still seems to work fine. And what could it have to do with showing the content in a child window instead of on the ActiveX control itself?
Timers stops for a reason. Which might be:
You do stop timer by KillTimer call
Your window is re-created and timer is not re-enabled
Your control is windowless and you actually don't have a HWND handle
There is a collision in timer identifiers, there is something else (e.g. internal subclassed window) out there to use the same identifier, it sets, kill the timer and you no longer see WM_TIMER messages you enabled earlier
The window thread is busy (frozen) with some activity which does not include message dispatching, so timer itself exists, is healthy and alive, just no messages sent
The things to do - without yet additional information on the issue on hands:
Check threads of your window, and your Set/KillTimer calls to make sure they all make sense together
Use Spy++ tool to check messages posted for your window and/or in the thread of the interest, to find out if you really have WM_TIMERs missing, or they just don't reach your code; also you might see other interesting messages around
Here's an excerpt from ATL implementation of CComControlBase (I would guess that your control inherits from that). Check the part marked with <<<<<<<<<<<:
inline HRESULT CComControlBase::IOleInPlaceObject_InPlaceDeactivate(void)
{
if (!m_bInPlaceActive)
return S_OK;
if(m_bUIActive) {
CComPtr<IOleInPlaceObject> pIPO;
ControlQueryInterface(__uuidof(IOleInPlaceObject), (void**)&pIPO);
ATLENSURE(pIPO != NULL);
pIPO->UIDeactivate();
}
m_bInPlaceActive = FALSE;
// if we have a window, tell it to go away.
//
if (m_hWndCD)
{
ATLTRACE(atlTraceControls,2,_T("Destroying Window\n"));
if (::IsWindow(m_hWndCD))
DestroyWindow(m_hWndCD); <<<<<<<<<<<<<<<<<<<<<<<<<<<
m_hWndCD = NULL;
}
if (m_spInPlaceSite)
m_spInPlaceSite->OnInPlaceDeactivate();
return S_OK;
}
On deactivation, the control window gets destroyed. Therefore it can't process WM_TIMER anymore.

Application wide periodic tasks with Dialog Based MFC application

In Single Document Interface (SDI) or Multiple Document Interface (MDI) MFC application, I created an application wide timer in the View. The timer will tick as long as the application is running and trigger some periodic actions.
How can I do the same with Dialog Based MFC application?
Should I create Thread's Timer (SetTimer with NULL HWND) and pass a callback function to it?
Should I create worker threads? My experience with other projects was when I tried to display some feedback GUI from non-GUI/worker threads, I need to roll out my own "delegate"/command pattern and a "delegate invoker"/command invoker. The worker thread will send message (I think using message is safer than direct function call when dealing across thread-boundary, CMIIW) to the UI-thread. and the UI-thread will be the "delegate"/command invoker. Failing to do this and to make sure that the windows/dialogs have the correct parent will result in bizzare behaviors such as the Application suddenly disappears to the background; Window/Dialog that is shown behind the current window/dialog and causing the current window to be unresponsive/unclickable. Probably I was doing something wrong but there were so much problems when dealing with threads.
Are there best practices for this?
A timer works as well in a dialog-based application as an SDI or MDI app. OTOH, timers are (mostly) a leftover from 16-bit Windows. If you want to do things periodically, a worker thread is usually a better way to do it (and yes, Windows Mobile supports multiple threads).
Edit: in a dialog-based application, the main dialog exists for (essentially) the entire life of the application. Unless you really need the timer during the milliseconds between application startup and dialog creation or dialog destruction and application exit, just attach it to the dialog. Otherwise, you can attach it to the main window -- which MFC creates and destroys, even though it's never displayed.
If you use the MFC Wizard to create the Dialog based app, you probably have a hidden view window as well as a dialog window. The view window creates the dialog with DoModal(), which runs the dialog in the same thread, effectively suspending the view window.
While the dialog is open, the view window will not process any events. So, if the view window owns the timer, it will not process the timer events.
The simplest solution is to create the timer in the dialog and let the dialog handle the timer messages.
IMO, use the Timer if it solves the problem. As you've mentioned a Worker Thread interacting with the UI, in MFC, can be more trouble than its worth sometimes.
If the problem is simple enough for a timer to suffice, thats what i'd use (Remember KISS)
SetTimer does not have to be handed a window to work, it can call a callback method.
You can use that in your application - declare in your CWinApp (or anywhere really)
static void CALLBACK OnTimer(HWND, UINT, UINT, DWORD);
Then in the InitInstance call SetTimer(0, [eventid], [time period], OnTimer);
In OnTimer you can get back to the CWinApp instance via AfxGetApp() or theApp since there is only one.
Second attempt: my previous answer was dne in a hurry and was not correct.
Your basic vanilla MFC Dialog app only uses one thread. The main thread starts with a class derived from CWinApp. In the InitInstance() method it launches the dialog using CDialog::DoModal(). This function doesn't return until the dialog is closed.
While the dialog is running, the CWinApp class does not process any messages, so won't see a WM_TIMER.
There are many ways around this.
Let the first dialog own the timer and make all other dialogs children of it. This might be OK, depending on your dialog requirements, but it might be too restrictive.
Launch the first Dialog as modeless, i.e. use Create() instead of DoModal(). Create() returns straight away (putting the Dialog into a different thread). You can then create a message loop in the CWinApp class and process timers there. You'll have to use thread timers instead of window timers as the CWinApp class doesn't have a window. (or you could create a hidden window if that is more convenient).
You can hack the dialog's mesage loop and make it pass messages to the CWinApp class' message handler. That is quite complex and not for the faint hearted.
You can create a dedicated timer thread. You'd probably do that from the CWinApp class before it creates the dialog, but other strategies are possible.
Do any of those schemes sound like they fit your needs? If not, maybe you can explain your needs more fully and we might be able to come up with something appropriate.