|
That is great.
But why is
#include <wil/com.h>
still in the example?
|
|
|
|
|
The current version is not the updated one, I will update it very soon. (Today)
|
|
|
|
|
New version is live now, you can download the project.
|
|
|
|
|
Great work, thank you
My problem ist, that i call the post and have to wait until response ist completed.
Wait () // Until response completed ????
Hope you can help...
Best
Tom
code:
my_app->m_pWebBrowser->NavigatePost(m_server, m_request, headers, nullptr);
Wait ()
CString response = my_app->m_pWebBrowser->GetResponse();
CString CWebBrowser::GetResponse()
{
return m_response;
}
CHECK_FAILURE(m_pImpl->m_webView2->add_WebResourceResponseReceived(
Callback<ICoreWebView2WebResourceResponseReceivedEventHandler>(
[this](ICoreWebView2* sender,ICoreWebView2WebResourceResponseReceivedEventArgs* args)
{
wil::com_ptr<ICoreWebView2WebResourceRequest> request;
CHECK_FAILURE(args->get_Request(&request));
wil::com_ptr<ICoreWebView2WebResourceResponseView> webResourceResponse;
CHECK_FAILURE(args->get_Response(&webResourceResponse));
int status = 0;
webResourceResponse->get_StatusCode(&status);
if (status == 200)
{
webResourceResponse->GetContent(Callback<ICoreWebView2WebResourceResponseViewGetContentCompletedHandler>([this, request, webResourceResponse](HRESULT result, IStream* content)
{
wil::com_ptr<IStream> getDataStream;
getDataStream.attach(SHCreateMemStream(NULL, 0));
if (content && m_posted == true)
{
CString erg = L"";
::std::wstring wideWhat;
bool readAll = false;
while (readAll == false)
{
char buffer[2048] = "";
unsigned long read;
content->Read(&buffer[0], 2048, &read);
readAll = read < 2048;
int convertResult = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, buffer, read, NULL, 0);
if (convertResult <= 0)
{
wideWhat = L"Exception occurred: Failure to convert its message text using MultiByteToWideChar";
AfxMessageBox(wideWhat.c_str());
}
else
{
wideWhat.resize(convertResult + 10);
convertResult = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED,buffer, read, &wideWhat[0], (int)wideWhat.size());
if (convertResult <= 0)
{
AfxMessageBox(L"Exception occurred: ");
}
erg.Append(wideWhat.c_str(), convertResult);
}
}
m_response = erg;
}
return S_OK;
}).Get());
}
return S_OK;
}).Get(), &m_webResourceResponseReceivedToken));
}
|
|
|
|
|
Are you calling an API or what?
Please let me know exactly what you want to achieve.
|
|
|
|
|
Hello Ayush,
until now wir use the C++ CHtmlView class to post some request to a C# Server App (IIS).
The problem is that CHtmlView needs InternetExplorer which is not longer supported by MS.
So we have to change to webview2 (Edge).
(the classes CHttpConnection, CHttpFile do the real communication, but the CHtmlView makes the cookie (login) management - no problem, they use the same session.)
Using CHttpConnection, CHttpFile, CHtmlView no extra threads are created. The code waits until response is finished.
But not so using WebView2.
wil::com_ptr<ICoreWebView2Environment2> m_webViewEnvironment2;
wil::com_ptr<ICoreWebView2_2> m_webView2;
...
in the main function first i call
CHECK_FAILURE(m_webViewEnvironment2->CreateWebResourceRequest(
url,
L"POST",
postDataStream2.get(),
NULL,
&webResourceRequest));
CHECK_FAILURE(m_webView2->NavigateWithWebResourceRequest(webResourceRequest.get()));
Do something with response String (m_response) in the main C++ function.
How can i wait until response is finished?
in the callback i receive the content:
CHECK_FAILURE(m_webView2->add_WebResourceResponseReceived(
Callback<ICoreWebView2WebResourceResponseReceivedEventHandler>(
[this](ICoreWebView2* sender,ICoreWebView2WebResourceResponseReceivedEventArgs* args)
{
wil::com_ptr<ICoreWebView2WebResourceRequest> request;
CHECK_FAILURE(args->get_Request(&request));
wil::com_ptr<ICoreWebView2WebResourceResponseView> webResourceResponse;
CHECK_FAILURE(args->get_Response(&webResourceResponse));
int status = 0;
webResourceResponse->get_StatusCode(&status);
if (status == 200)
{
webResourceResponse->GetContent(Callback<ICoreWebView2WebResourceResponseViewGetContentCompletedHandler>([this, request, webResourceResponse](HRESULT result, IStream* content)
{
wil::com_ptr<IStream> getDataStream;
getDataStream.attach(SHCreateMemStream(NULL, 0));
if (content && m_posted == true)
{
CString erg = L"";
bool readAll = false;
while (readAll == false)
{
char buffer[2048] = "";
unsigned long read;
content->Read(&buffer[0], 2048, &read);
readAll = read < 2048;
m_response += erg;
}
return S_OK;
}).Get());
}
return S_OK;
}).Get(), &m_webResourceResponseReceivedToken));
}
My Problem ist that i have to wait after the NavigateWithReourceRequest(...) until the "Resonse" is finished an all data are available.
How can i wait in the code until response is finished ?
Who can the main() thread wait until all data are received?
If i call AfxMessageBox(L"hello") after the NavigateWithWebResourceRequest(...) it works.
Thank you very much for your help
Thomas
modified 19-May-22 4:44am.
|
|
|
|
|
Thanks for the more information.
But I am not getting one point, for just making a web request (If UI is not needed) why are you using the WebView?
InternetOpenUrl(hInternetSession, szURL, szHead, szHead.GetLength(), INTERNET_FLAG_NO_UI, 0)
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, buffer, &dwLengthBufQuery, NULL)
DWORD dwReturnCode = _wtoi(buffer);
if (dwReturnCode == 200)
{
dwLengthBufQuery = sizeof(buffer);
if (HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH, buffer, &dwLengthBufQuery, NULL))
{
DWORD dwLength = _wtoi(buffer);
if (dwLength > 0)
{
if (InternetReadFile(hRequest, bfTemp, 10000, &dwSize))
{
break;
}
}
}
}
I this will work without the need of CHTMLView or WebView2.
|
|
|
|
|
The problem ist, that we have to use PKI Login via Browser (that is the reason, why we use CHtmlView) in the nativ C++ Program. So we have to call a webside, login via PKI, and after success communicate using a C# handler. After success Login we do not need the Web Interface, that's correct and it works fine with CHtmlView, CInternetSession, CHttpConnection.
The PKI Login Informations are stored in Cookies.
In the communication to the server Software the Cookies in the header are necessary and we do not want to make our own Cookie Management.
Best
Thomas
|
|
|
|
|
Hello Ayush,
the problem ist, that we have to use PKI Login via Browser (that is the reason, why we use CHtmlView) in the nativ C++ Program. So we have to call a webside, login via PKI, and after success communicate using a C# System.Web.IHttpHandler on the server. After success Login we do not need the Web Interface, that's correct and it works fine with CHtmlView (login via PKI), CInternetSession, CHttpConnection - communicate after login.
The PKI Login Informations are stored in Cookies.
In the communication to the server Software the Cookies in the header are necessary and we do not want to make our own Cookie Management.
C# Server Code after success Login:
var xmlRequest = HandlerUtils.GetXmlRequest(_context.Request.InputStream);
if (!ValidateRequest(xmlRequest)) // Validation
{
return;
}
if (!GetXmlData()) // read data from database depending or request
return;
// Respond with 200 Ok and write xmlData to response
_context.Response.StatusCode = (int) HttpStatusCode.OK;
_context.Response.Write(XmlData); // send xml data to C++ Client
Best
Thomas
|
|
|
|
|
Not sure if this way it's possible with WebView2.
What you can do as an alternate solution is when you make this request, then Send a windows message to your application.
And handle this message and start the waiting process.
And when you get the response then also you can send a further window message to your application.
Handle this message and process the response then.
This is an easy approach and I am very sure that it will work for you.
|
|
|
|
|
Hi Ayush,
thank you. I also thought this is not so easy. Everything i tried to wait in the main program for end of response does not work.
I'll try it with your idea and windows messages.
Best
Thomas
|
|
|
|
|
This will work 100%.
But keep in mind, what if due for any reason response did not come. So in order to handle such a case, you should start a timer also when you handle the first message for example WM_REQUEST_SENT.
If the response doesn't come i.e. in 30 seconds then this timer will send the second window message WM_RESPONSE.
So that your application does not remain in waiting mode if no message came.
(By the way, you downgrade my reply. I am trying to help you)
|
|
|
|
|
Sorry for the downgrade. I do not recognize that i'm doing this.
This was my first communication.
Thank you again very much.
I'v first to study a little Windows's messaging. I'm not fit in writing "own" messages or using timers.
Best
Thomas
|
|
|
|
|
I am working on a project which uses old CHtmlView and I want to use WebView2 instead.
But we want to use the old CHtmlView if user is not using Edge browser on his machine. What do you recommend?
|
|
|
|
|
This is very simple, you can check if WebView2 is installed or not by using the below code.
wil::unique_cotaskmem_string version_info;
HRESULT hr = GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info);
if (hr == S_OK && version_info != nullptr)
{
dlgEdgeWebViewHost = new EdgeWebView2(this, m_szHtmlPaneURL);
dlgEdgeWebViewHost->Create(IDD_EDGEBROWSER_HOST_DIALOG, this);
m_bIsWebViewRunning = TRUE;
}
else
{
m_bIsWebViewRunning = FALSE;
}
|
|
|
|
|
It is not easy to open two webviews. Please give me a simple example
|
|
|
|
|
By using the same code, I am opening n number of HTML Panes (WebView2) in my project.
There is no limit regarding that.
|
|
|
|
|
Hi Ayush
Thanks for the article.
Has anyone had much success with building the solution with VS 2015?
Windows 10 PC.
I can build and run the solution with VS 2019. All good
VS2015 - started by unzipping into a dedicated solution folder, reduced to v140. The build fails with hundreds of errors.
Please help!
Appreciated
|
|
|
|
|
Hello,
Sorry, I don't have Visual Studio 2015 installed on my PC.
That's why I can't help here.
Thanks and regards
Ayush
|
|
|
|
|
Hi Ayush
I tried building official Microsoft WebView2 sample solution with VS2015, same story.
Im getting desperate as we have a massive customer running our legacy application.
If you have a few minutes I can let you access (TeamView) my dev box with VS2015. Would you be able to?
Much appreciated
I'm in Sydney, Australia.
|
|
|
|
|
|
Hello,
I've a problem with WebView2. To summarize, I use webview2 in my C++ application. In the documentation of ICoreWebView2, I haven't seen a function "NavigateWithPostData" or something like that.
So, I tried with the function "Navigate" and then, in my "add_NavigationCompleted" listener, I tried :
m_controlsWebView->PostWebMessageAsJson(utils::stringToWstring(EdgeBrowser::_post).c_str());
where EdgeBrowser::_post is :
file={"features":[{"geometry":{"coordinates":[39.000000,7.000000],"type":"Point"},
"properties":{"Code postal":"2","Commune":"1","Coordonnee (X)":"39.0000000","Coordonnee (Y)":"7.0000000","Numero de maison":"7","Rue":"40"},"type":"Feature"},{"geometry":{"coordinates":[41.000000,7.000000],"type":"Point"},
"properties":{"Code postal":"4","Commune":"3","Coordonnee (X)":"41.0000000","Coordonnee (Y)":"7.0000000","Numero de maison":"7","Rue":"42"},"type":"Feature"},{"geometry":{"coordinates":[43.000000,7.000000],"type":"Point"},
"properties":{"Code postal":"6","Commune":"5","Coordonnee (X)":"43.0000000","Coordonnee (Y)":"7.0000000","Numero de maison":"7","Rue":"44"},"type":"Feature"},{"geometry":{"coordinates":[45.000000,7.000000],"type":"Point"},
"properties":{"Code postal":"8","Commune":"7","Coordonnee (X)":"45.0000000","Coordonnee (Y)":"7.0000000","Numero de maison":"7","Rue":"46"},"type":"Feature"},{"geometry":{"coordinates":[47.000000,7.000000],"type":"Point"},
"type":"Feature"}],"type":"FeatureCollection"}
Yes, I have my webpage (it's a map) who displays well but I don't see the different markers on the map because the post datas do not work.
Can you help me please ? Can you propose a solution ?
Please help me.
Thank you
|
|
|
|
|
Hallo,
ich bin auf der Suche wie ich in einer MFC Anwendung mit WebView2 auf Elemente eine HTML Seite zugreifen kann.
z.B get_tagName() getId()
Jürgen
|
|
|
|
|
Yes, this is possible and very easy to do that.
Follow these steps.
1. Add Handler for Navigation Complete in OnCreateCoreWebView2ControllerCompleted, like this.
HRESULT EdgeWebView2::OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller)
{
m_webView->add_NavigationCompleted(Callback<ICoreWebView2NavigationCompletedEventHandler>(this, &EdgeWebView2::NavigationCompleted).Get(), &token);
}
2. In Navigation complete execute a script and ask for whole HTML elements of the page like this.
HRESULT EdgeWebView2::NavigationCompleted(ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args)
{
m_webView->ExecuteScript(L"document.documentElement.outerHTML;", Callback<ICoreWebView2ExecuteScriptCompletedHandler>(this, &EdgeWebView2::ExecuteScriptResponse).Get());
}
3. Now you will receive the response of executed script here.
HRESULT EdgeWebView2::ExecuteScriptResponse(HRESULT errorCode, LPCWSTR result)
{
CString source = result;
source.Replace("\\u003C", "<");
source.Replace("\\n", "");
source.Replace("\\t", "");
IHTMLDocument2* pDoc;
IHTMLDocument3* pDoc3;
IHTMLElementCollection* pCollection;
IHTMLElement* pElement;
HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER,
IID_IHTMLDocument2, (void**)&pDoc);
SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 1);
VARIANT* param;
bstr_t bsData = (LPCTSTR)source;
hr = SafeArrayAccessData(psa, (LPVOID*)¶m);
param->vt = VT_BSTR;
param->bstrVal = (BSTR)bsData;
hr = pDoc->write(psa);
hr = pDoc->close();
SafeArrayDestroy(psa);
if (hr != S_OK)
{
return S_FALSE;
}
CComPtr<IHTMLElement> pBody;
if (SUCCEEDED(hr = pDoc->get_body(&pBody)))
{
if (pBody)
{
CComPtr<IHTMLElement> pHTML;
pBody->get_parentElement(&pHTML);
IDispatch* iAll;
IHTMLElementCollection* elementCollection;
hr = pDoc->get_all(&elementCollection);
if (SUCCEEDED(hr))
{
long itemCount;
hr = elementCollection->get_length(&itemCount);
if (FAILED(hr))
return S_FALSE;
CString szCOCIconPath = "";
CString szShortcutIconPath = "";
for (int i = 0; i pDisp = NULL;
hr = elementCollection->item(index, index, &pDisp);
if (FAILED(hr))
return S_FALSE;
if (pDisp)
{
CComPtr pElement;
hr = pDisp->QueryInterface(IID_IHTMLElement, (void**)&pElement);
if (FAILED(hr))
return S_FALSE;
CComBSTR bstr;
pElement->get_tagName(&bstr);
bstr_t tmp(bstr, FALSE);
CString tagName(static_cast(tmp));
if (tagName == "LINK")
{
VARIANT attr;
BSTR bstrtemp;
bstrtemp = SysAllocString(L"rel");
pElement->getAttribute(bstrtemp, 1, &attr);
bstr_t tmpVal(attr.bstrVal, FALSE);
CString attributeVal(static_cast(tmpVal));
attributeVal.Replace("\\", "");
attributeVal.Replace("\"", "");
}
}
}
}
}
}
}
I hope it will help you!
|
|
|
|
|
Trying to download your code is timing out so I can't verify the answer to my question.
When I create my own MFC CDialog project and add the Nuget packages, it won't compile for code like this:
Error (active) E0020 identifier "CloseWebView" is undefined MFCTestWebView2 C:\Users\ajtru\Source\Repos\MFCTestWebView2\MFCTestWebView2\MFCTestWebView2Dlg.cpp 164
How do we fix these issues?
|
|
|
|
|