WIN32 - Create a WebView2 synchronously does not work - I don't get a white rectangle for my integrated web browser - c++

In brief, I explain you my problem. In my application, I've a button to load the browser. When I click on the button, it opens and creates well the webBrowser (white rectangle).
BUT when I try to create the web Browser when I open a window (who contains different components : buttons, edittext,...) of my application through the Event WM_SHOWWINDOW, I don't see my web browser EXCEPTED IF I put in comment all the loop "while" in the constructor EdgeBrowser.
Why ? Can you give me a solution please ? It would be friendly.
I created a class EdgeBrowser with a constructor who receives the handle of my WIN32 component (HWND component). In my constructor, I do something like that :
EdgeBrowser::EdgeBrowser(HWND hwnd)
{
_hwnd = hwnd;
EdgeBrowser::_beginAsyncOperation = true;
this->CreateWebView();
while (EdgeBrowser::_beginAsyncOperation)
{
Sleep(10);
MSG msg;
for (int nmsg = 0; nmsg < 50; ++nmsg)
{
int rc = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (rc == 0)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
And in a part of my "CreateWebView" function with the different listeners :
void EdgeBrowser::CreateWebView
{
// other code
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(nullptr, userDataDir.c_str(),nullptr,Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(this,&EdgeBrowser::OnCreateCoreWebView2EnvironmentCompletedHandler).Get());
}
HRESULT EdgeBrowser::OnEnvironmentReadyCompletedHandler(HRESULT result, ICoreWebView2Environment* env)
{
HRESULT createBrowserControlsResult = m_uiEnv->CreateCoreWebView2Controller(this->_thisHandler, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(this,&EdgeBrowser::OnCreateCoreWebView2ControllerCompletedHandler).Get());
HRESULT createBrowserOptionsResult = m_uiEnv->CreateCoreWebView2Controller(this->_thisHandler,Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( this,&EdgeBrowser::OnCreateCoreWebView2ControllerOptionsCompletedHandler).Get());
}
HRESULT EdgeBrowser::OnCreateCoreWebView2ControllerOptionsCompletedHandler(HRESULT result, ICoreWebView2Controller* host)
{
m_optionsController = host;
HRESULT getOptWebViewResult = m_optionsController->get_CoreWebView2(&m_optionsWebView);
//other code
EdgeBrowser::_beginAsyncOperation = false;
return S_OK;
}

The WebView2 control requires a message loop to run on the UI thread on which it is created (see this page for more info on WebView2 threading). You can see the WebView2APISample sample app as a sample C++ Win32 HWND based application.

Related

Disabling "Script Error" popup IWebBrowser2 c++ WinApi

Having a HWND with IWebBrowser2 on it. IWebBrowser2 is new CLSID_WEBBROWSER.
When I navigating to youtube,google and etc, sometimes it shows me Script Error. And I want to disable it. How can I do it?
if (MoneyHWND == NULL) {
if (SUCCEEDED(OleInitialize(NULL)))
{
vector<wchar_t> fn(1000);
GetModuleFileName(0, fn.data(), 1000);
PathStripPath(fn.data());
RKEY k(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION");
k[fn.data()] = 11001UL; // Use IE 11
MoneyHWND = CreateDialog(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FORMVIEW1), hWnd, MoneyProc);
pBrowser2 = new WebBrowser(MoneyHWND);
RECT rc;
GetClientRect(MoneyHWND, &rc);
pBrowser2->SetRect(rc);
pBrowser2->Navigate(site);
OleUninitialize();
}
}
IWebBrowser2::Silent:
Sets or gets a value that indicates whether the object can display dialog boxes.
Note, that the property is exposed to C and C++ programs using the following signatures:
HRESULT IWebBrowser2::get_Silent(VARIANT_BOOL *pbSilent);
HRESULT IWebBrowser2::put_Silent(VARIANT_BOOL bSilent);
In other words:
// ...
auto hr{ pBrowser2->put_Silent(VARIANT_TRUE) };
if FAILED(hr)
{
// Handle error
// ...
}

How to wait for IWebBrowser2::ExecWB() to finish when printing from a WebBrowser control?

I'm using the WebBrowser control in my CDialog-based MFC window. The window allows printing, using code similar to this:
CComPtr<IWebBrowser2> pWebBrowser = this->GetIWebBrowser2();
if(pWebBrowser)
{
HRESULT hr;
COleVariant varNull;
if(SUCCEEDED(hr = pWebBrowser->ExecWB(
bDoPreview ? OLECMDID_PRINTPREVIEW : OLECMDID_PRINT,
OLECMDEXECOPT_PROMPTUSER, varNull, varNull)))
{
//All good
bRes = TRUE;
}
}
IWebBrowser2* GetIWebBrowser2()
{
IWebBrowser2* pBrowser = NULL;
LPUNKNOWN unknown = m_browser.GetControlUnknown();
if(unknown)
{
unknown->QueryInterface(IID_IWebBrowser2,(void **)&pBrowser);
if(unknown)
{
unknown->Release();
}
}
return pBrowser;
}
It works, except that if the document is large enough, pWebBrowser->ExecWB() seems to return right away and all the printing is done asynchronously. So in that case if the user closes my window (that houses this WebBrowser control) printing is aborted midway.
Thus my question, how do I wait for the printing to finish before I can allow closing of the host window?

Sending a click event to a Paint application

I am writing an application that automatically draws something on a canvas, depending on the user's preferences.
For starters, how can I send a click event to the MS Paint application?
First, you need to find the Paint application:
static HWND findMSPaintDrawWindow(void)
{
HWND target;
target = FindWindow(TARGET_PAINT_WINDOW, NULL);
if (NULL != target)
{
target = FindWindowEx(target, NULL, TARGET_PAINT_INPUT_SUBWINDOW, NULL);
if (NULL != target)
{
target = FindWindowEx(target, NULL, NULL, NULL);
}
}
if (/*ENABLE_DEBUG_CONSOLE*/ 0)
{
char name[256];
GetClassName(target, name, 255);
printf("Detected ms paint Draw area with name [%s]\n", name);
}
return target;
}
Second, you interact with it:
static void sendMouseButton(int buttonState)
{
HWND target;
UINT buttonMode = WM_LBUTTONUP;
target = findMSPaintDrawWindow();
if (target)
{
if (buttonState)
{
buttonMode = WM_LBUTTONDOWN;
g_MouseDown = 0;
}
printf("INFO: Mouse [%d] msg %d\n", buttonState, buttonMode);
PostMessage(target, buttonMode, MK_LBUTTON, X, Y));
}
}
You can always browse the MSDN documentation for more details.
You have to search google for how to send windows message to different application. The mouse click is composed of 2 different messages: WM_LBUTTONDOWN and WM_LBUTTONUP.
http://stefanstools.sourceforge.net/SendMessage.html
http://www.codeproject.com/Articles/137/Sending-a-message-to-the-Main-Frame-Window-of-Anot

How to get HWND of an embedded web browser control in MFC

I'm using the embedded web browser control in my dialog-based MFC window and I need to know the HWND of the web browser control in it. I was able to find the following code that claims to retrieve it:
HWND hWndWebBrowser = NULL;
LPUNKNOWN unknown = m_browser.GetControlUnknown();
IWebBrowser2* pWB = NULL;
if(SUCCEEDED(unknown->QueryInterface(IID_IWebBrowser2,(void **)&pWB)))
{
CComPtr<IServiceProvider> pServiceProvider;
if (SUCCEEDED(pWB->QueryInterface(IID_IServiceProvider, (void**)&pServiceProvider)))
{
CComPtr<IOleWindow> pWindow;
if (SUCCEEDED(pServiceProvider->QueryService(SID_SShellBrowser, IID_IOleWindow, (void**)&pWindow)))
{
SHANDLE_PTR hBrowser = 0;
if (SUCCEEDED(pWindow->GetWindow(&hBrowser)))
{
hWndWebBrowser = (HWND)hBrowser;
}
}
}
}
if(unknown)
{
unknown->Release();
}
but the problem is that when it runs, it returns a handle, but not the one I would expect. The best way to illustrate it is with this Spy++ screenshot:
I understand that I can use EnumChildWindows and look for a window with the Internet Explorer_Server class, but I'm somewhat concerned about using this undocumented class name.
Does anyone have a better way to retrieve that (web browser) window handle?
Per Obtaining the HWND for the WebBrowser control, you can use following function to retrieve HWND.
IOleWindow *pOWin;
HWND hBWnd;
HRESULT hRes = m_pBrowserApp->QueryInterface(IID_IOleWindow, (void **)&pOWin);
if (SUCCEEDED(hRes)) {
hRes = pOWin->GetWindow(&hBWnd);
if (SUCCEEDED(hRes)) {
// Place hBWnd-manipulating code here
}
pOWin->Release(); // Missing from the MS example
}
Because the class names (Shell DocObject View and Internet Explorer_Server) could change, the above code should be preferred, although it is unlikely given the fact that Internet Explorer is now discontinued.
The lexical of the question is a little tricky.
The HWND of the (Web Browser) is indeed the answer
that you posted and the answer posted by Santosh Dhanawade.
When a document is loaded, the web browser control creates a
new window or iframe, see the DWebBrowserEvents2::DocumentComplete event.
Event handler parameters:
" pDisp [in] "
A pointer to the IDispatch interface of the window or frame in which the document is loaded. This IDispatch interface can be queried for the IWebBrowser2 interface.
so, changing the question:
"Does anyone have a better way to retrieve that (web browser) window handle?"
to:
"Does anyone have a better way to retrieve that (window or iframe) window handle?"
we have that the window or iframe HWND that you are locking for,
will be abailable after that the document has been completed loaded.
Which means that we can do the follow:
Implement a DocumentComplete event handler throw a raw c or c++ implementation of
IDispatch or an ATL DispEventImpl or ATL DispEventSimpleImpl.
See Understanding COM Event Handling.
Sink our event handler into the the web browser control to get the events report.
And get the window or iframe HWND from the DocumentComplete event:
assuming a raw c++ IDispatch implementation:
IFACEMETHODIMP DWebBrowserEvents2Impl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)
{
if (dispIdMember == DISPID_DOCUMENTCOMPLETE) {
VARIANT variantDispatch;
VariantInit(&variantDispatch);
HRESULT hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &variantDispatch, NULL);
if (SUCCEEDED(hr)) {
IOleWindow* iOleWindow;
hr = variantDispatch.pdispVal->QueryInterface(IID_IOleWindow, (LPVOID*) &iOleWindow);
if (SUCCEEDED(hr)) {
HWND hwnd;
hr = iOleWindow->GetWindow(&hwnd);
iOleWindow->Release();
if (SUCCEEED(hr)){
//now the hwnd correponds to the Internet Explorer_Server window.
//Do what ever you want with the HWND handler.
}
}
}
return S_OK;
}
return E_NOTIMPL;
}
From my experience, the window we're looking for is a direct descendance of the CHtmlView derived CWnd, so I use this hack to get the window and set the focus to it:
static CWnd* findChildWebbrowser(CWnd* pWnd) {
if(pWnd == NULL) { return NULL; }
CWnd* pC = pWnd->GetWindow(GW_CHILD);
if(pC == NULL) { return NULL; };
CString buf;
::GetClassName(pC->GetSafeHwnd(), buf.GetBuffer(2048), 2047);
buf.ReleaseBuffer();
if(buf == _T("Internet Explorer_Server")) {
return pC;
}
return findChildWebbrowser(pC);
}
void CMyWebView::OnSetFocus(CWnd* pOldWnd) {
// CHtmlView::OnSetFocus(pOldWnd);
CWnd* pIE = findChildWebbrowser(this);
if(pIE!=NULL) {
// this makes cursor/page keys work
pIE->SetFocus();
// this makes the TAB key work
pIE->SendMessage(WM_LBUTTONDOWN);
pIE->SendMessage(WM_LBUTTONUP);
}
}

Browse For Folder dialog window handle C++

How to get the handle HWND of the dialog which user open when clicking on a button.
I'm using Spy++ to find the window class and tittle, but it says that no such window is found. And how then to get the handle of that dialog in C++ using Win API ?
I hope that I will be able to do that using simple functions as FindWindow, GetParent, any WIN APi function. I do not like to inject something or load DLL. Thanks
UPDATE:
the folder browser dialog is opened by other program. I want to get it's handle from different program , my program. Thanks.
The closest to want i need is for now the function WindowFromPoint
Accessibility will let you capture window creation events from other processes without DLL injection. You can modify the example to accommodate for the browsing window specifically. Here's an example I made previously to test that is based on the one from the article. Modify it however you wish:
#include <iostream>
#include <windows.h>
void CALLBACK proc(HWINEVENTHOOK hook, DWORD event, HWND hwnd, LONG obj, LONG child, DWORD thr, DWORD time) {
if (hwnd && obj == OBJID_WINDOW && child == CHILDID_SELF) {
switch (event) {
case EVENT_OBJECT_CREATE: {
std::cout << "Window created!\n";
break;
}
case EVENT_OBJECT_DESTROY: {
std::cout << "Window destroyed!\n";
break;
}
}
}
}
int main() {
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, nullptr, proc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (hook) {
UnhookWinEvent(hook);
}
}