URLDownloadToFile always return S_OK - c++

I'm new to c++ and try to write a update function.
The download with URLDownloadToFile is working without problems but if I changed the url to an invalid one, it stilled return S_OK ... How can I check if the download succede or not?
#include <WinInet.h>
#include <iomanip>
int download_file (const TCHAR urldownload[],const TCHAR target[] )
{
DownloadProgress progress;
IBindStatusCallback* callback = (IBindStatusCallback*)&progress;
SCP(40, NULL); cout << target;
HRESULT status = URLDownloadToFile(NULL, urldownload, target, 0, static_cast<IBindStatusCallback*>(&progress));
Sleep(200);
DeleteUrlCacheEntry(urldownload);
wcout << status;
if (status == S_OK) cout << "yes";
else(cout << "Download failed");
Sleep(10000); return 1;
}
class DownloadProgress : public IBindStatusCallback {
public:
HRESULT __stdcall QueryInterface(const IID &, void **) {
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef(void) {
return 1;
}
ULONG STDMETHODCALLTYPE Release(void) {
return 1;
}
HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, IBinding *pib) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetPriority(LONG *pnPriority) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, LPCWSTR szError) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *grfBINDF, BINDINFO *pbindinfo) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, IUnknown *punk) {
return E_NOTIMPL;
}
virtual HRESULT __stdcall OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
//wcout << ulProgress << L" of " << ulProgressMax << endl; Sleep(200);
if (ulProgress != 0 && ulProgressMax != 0)
{
double output = (double(ulProgress) / ulProgressMax)*100;
cout << "\r" << "Downloading: " << fixed << setprecision(2) << output << " % " ; Sleep(20);
}
return S_OK;
}
};

MSDN article has the answer for you:
URLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.
You need to provide a status callback to receive status of the asynchronous operation. Your code snippet already has the base. OnProgress and OnStopBinding should get you the download failure result.

Related

URLdownloadToFileW Doesn't download a file [duplicate]

I'm new to c++ and try to write a update function.
The download with URLDownloadToFile is working without problems but if I changed the url to an invalid one, it stilled return S_OK ... How can I check if the download succede or not?
#include <WinInet.h>
#include <iomanip>
int download_file (const TCHAR urldownload[],const TCHAR target[] )
{
DownloadProgress progress;
IBindStatusCallback* callback = (IBindStatusCallback*)&progress;
SCP(40, NULL); cout << target;
HRESULT status = URLDownloadToFile(NULL, urldownload, target, 0, static_cast<IBindStatusCallback*>(&progress));
Sleep(200);
DeleteUrlCacheEntry(urldownload);
wcout << status;
if (status == S_OK) cout << "yes";
else(cout << "Download failed");
Sleep(10000); return 1;
}
class DownloadProgress : public IBindStatusCallback {
public:
HRESULT __stdcall QueryInterface(const IID &, void **) {
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef(void) {
return 1;
}
ULONG STDMETHODCALLTYPE Release(void) {
return 1;
}
HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, IBinding *pib) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetPriority(LONG *pnPriority) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved) {
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, LPCWSTR szError) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *grfBINDF, BINDINFO *pbindinfo) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) {
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, IUnknown *punk) {
return E_NOTIMPL;
}
virtual HRESULT __stdcall OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
//wcout << ulProgress << L" of " << ulProgressMax << endl; Sleep(200);
if (ulProgress != 0 && ulProgressMax != 0)
{
double output = (double(ulProgress) / ulProgressMax)*100;
cout << "\r" << "Downloading: " << fixed << setprecision(2) << output << " % " ; Sleep(20);
}
return S_OK;
}
};
MSDN article has the answer for you:
URLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.
You need to provide a status callback to receive status of the asynchronous operation. Your code snippet already has the base. OnProgress and OnStopBinding should get you the download failure result.

How to read from - "unsigned char const *" when use Media Foundation?

I have such an implementation
void coAudioPlayerSampleGrabber::test(SoundDataType dataType,
unsigned char const * pData,
int64_t dataLen)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
IMFSourceReader *pReader = NULL;
IMFByteStream * spByteStream = NULL;
HRESULT hr = S_OK;
// Initialize the COM library.
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Initialize the Media Foundation platform.
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
}
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
if (FAILED(hr))
{
printf("Error MFCreateMFByteStreamOnStreamEx");
}
IMFAttributes * Atrr = NULL;
hr = MFCreateAttributes(&Atrr, 10);
if (FAILED(hr))
{
printf("Error MFCreateAttributes");
}
hr = Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);
if (FAILED(hr))
{
printf("Error Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)");
}
hr = MFCreateSourceReaderFromByteStream(spByteStream, Atrr, &pReader);
if (FAILED(hr))
{
printf("Error MFCreateSourceReaderFromByteStream");
}
if (FAILED(hr))
{
printf("Error opening input file");
}
IMFMediaType *pAudioType = NULL; // Represents the PCM audio format.
hr = ConfigureAudioStream(dataType, pReader, &pAudioType);
if (FAILED(hr))
{
printf("Error ConfigureAudioStream");
}
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
BYTE *pAudioData = NULL;
DWORD cbBuffer = 0;
std::vector<SampleData> samples_vec;
while (true)
{
DWORD dwFlags = 0;
hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &dwFlags, NULL, &pSample);
if (FAILED(hr)) { break; }
if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
printf("Type change - not supported by WAVE file format.\n");
break;
}
if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of input file.\n");
break;
}
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (FAILED(hr)) { break; }
hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
if (FAILED(hr)) { break; }
//Do something with the pAudioData which is an array of unsigned chars of lenth cbBuffer
SampleData tmp;
tmp.pAudioData = new byte[cbBuffer];
memcpy(tmp.pAudioData, pAudioData, cbBuffer);
tmp.cbBuffer = cbBuffer;
samples_vec.push_back(tmp);
// Unlock the buffer.
hr = pBuffer->Unlock();
pAudioData = NULL;
if (FAILED(hr)) { break; }
}
SafeRelease(&pReader);
SafeRelease(&pSample);
SafeRelease(&pBuffer);
SafeRelease(&spByteStream);
SafeRelease(&Atrr);
// Shut down Media Foundation.
MFShutdown();
CoUninitialize();
}
So as you can see I have pointer to data and size this is actually my data I need to decode it. Problem is that here
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
I got an error access violation and as far as I see this is because I try to convert pData to IUnknown*. Question is - How to convert it right?
You can't cut corners like this:
unsigned char const * pData;
...
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
IUnknown is not yet another fancy alias for a byte. You are supposed to literally supply interface pointer representing stream, as documented.
Media Foundation does offer you means to read from memory bytes. You need to create a create a real stream, IStream or IRandomAccessStream or IMFByteStream per docuemntation. Also supply IMFAttributes you created with proper attributes to specify data type (which otherwise in the case of a file are derived from extension or MIME type) and then Source Reader API would be able to process memory bytes as source of media file data, and suitable decoder would decode audio into PCM data (similar to this).
Something you can do real quick: CreateStreamOnHGlobal to create IStream implementation and copy your bytes into underlying buffer (see docs). Then MFCreateMFByteStreamOnStream would create a IMFByteStream wrappr over it, and you can use this wrapper as MFCreateSourceReaderFromByteStream argument.
I used to use a VectorStream class for such stuff. Some functions are not implemented, but you should get the basic idea.
class VectorStream : public IStream
{
public:
bool ReadOnly = false;
ULONG r = 1;
std::vector<char> d;
size_t p = 0;
VectorStream()
{
}
void Clear()
{
d.clear();
p = 0;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IStream))
{
*ppvObject = (IStream*)this;
r++;
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return ++r;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
return --r;
}
HRESULT __stdcall Clone(
IStream** ppstm
)
{
return E_NOTIMPL;
}
HRESULT __stdcall Commit(
DWORD grfCommitFlags
)
{
return S_OK;
}
HRESULT __stdcall CopyTo(
IStream* pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER* pcbRead,
ULARGE_INTEGER* pcbWritten
)
{
return E_NOINTERFACE;
}
HRESULT __stdcall LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall Revert()
{
return E_NOTIMPL;
}
HRESULT __stdcall Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER* plibNewPosition
)
{
LARGE_INTEGER lo = { 0 };
if (dwOrigin == STREAM_SEEK_SET)
{
p = dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_CUR)
{
p += dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_END)
{
p = d.size() - dlibMove.QuadPart;
}
if (p >= d.size())
p = d.size();
if (plibNewPosition)
plibNewPosition->QuadPart = p;
return S_OK;
}
HRESULT __stdcall SetSize(
ULARGE_INTEGER libNewSize
)
{
d.resize(libNewSize.QuadPart);
return S_OK;
}
int eb = 0;
HRESULT __stdcall Stat(
STATSTG* pstatstg,
DWORD grfStatFlag
)
{
pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.QuadPart = d.size();
pstatstg->grfLocksSupported = true;
return S_OK;
}
unsigned long long readbytes = 0;
HRESULT __stdcall Read(
void* pv,
ULONG cb,
ULONG* pcbRead
)
{
auto av = d.size() - p;
if (cb < av)
av = cb;
memcpy(pv, d.data() + p, av);
p += av;
if (pcbRead)
*pcbRead = (ULONG)av;
// if (av < cb)
// return S_FALSE;
return S_OK;
}
HRESULT __stdcall Write(
const void* pv,
ULONG cb,
ULONG* pcbWritten
)
{
if (ReadOnly)
return STG_E_ACCESSDENIED;
if (d.size() < (p + cb))
{
auto exc = (p + cb) - d.size();
d.resize(d.size() + exc);
}
memcpy(d.data() + p, pv, cb);
p += cb;
if (pcbWritten)
*pcbWritten = cb;
return S_OK;
}
};
It encapsulates std::vector<> in a IStream.

WMI detect process creation event - c++

I am trying to create a notify event program when some specific process opens. I follow this post, and it works. I can get event when notepad.exe or powerpoint process opened.
However the program just execute one time, after the first notepad opened, the program stop checking new notepad process.
I know I may need a loop, but where should I add the loop?
Another question is that I check two type of process(notepad & powerpoint), so no matter which process opened, the event will be trigger. Is it possible to know which process trigger the function?
Suppose I open notepad, and I can know notepad process trigger event, not powerpoint
Main.cpp
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#include <atlcomcli.h>
#pragma comment(lib, "wbemuuid.lib")
#include "CreationEvent.h"
class EventSink : public IWbemObjectSink {
friend void CreationEvent::registerCreationCallback(TNotificationFunc callback);
CComPtr<IWbemServices> pSvc;
CComPtr<IWbemObjectSink> pStubSink;
LONG m_IRef;
CreationEvent::TNotificationFunc m_callback;
public:
EventSink(CreationEvent::TNotificationFunc callback) :m_IRef(0), m_callback(callback){}
~EventSink(){
}
virtual ULONG STDMETHODCALLTYPE AddRef() {
return InterlockedIncrement(&m_IRef);
}
virtual ULONG STDMETHODCALLTYPE Release() {
LONG IRef = InterlockedDecrement(&m_IRef);
if (IRef == 0)
delete this;
return IRef;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IUnknown || riid == IID_IWbemObjectSink) {
*ppv = (IWbemObjectSink*) this;
AddRef();
return WBEM_S_NO_ERROR;
}
else return E_NOINTERFACE;
}
virtual HRESULT STDMETHODCALLTYPE Indicate(
LONG lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
){
m_callback();
/* Unregister event sink */
pSvc->CancelAsyncCall(pStubSink);
return WBEM_S_NO_ERROR;
}
virtual HRESULT STDMETHODCALLTYPE SetStatus(LONG IFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) {
return WBEM_S_NO_ERROR;
}
};
void CreationEvent::registerCreationCallback(TNotificationFunc callback) {
CComPtr<IWbemLocator> pLoc;
CoInitializeEx(0, COINIT_MULTITHREADED);
HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hres)) {
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
throw std::exception("CreationEvent initialization failed");
}
CComPtr<EventSink> pSink(new EventSink(callback));
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSink->pSvc);
if (FAILED(hres)) {
cout << "Could not connect. Error code = 0x" << hex << hres << endl;
throw std::exception("CreationEvent initialization failed");
}
hres = CoSetProxyBlanket(pSink->pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hres)) {
cout << "Coult not set proxy blanket, Error code =0x" << hex << hres << endl;
throw std::exception("CreationEvent initialization failed");
}
CComPtr<IUnsecuredApartment> pUnsecApp;
hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&pUnsecApp);
CComPtr<IUnknown> pStubUnk;
pUnsecApp->CreateObjectStub(pSink, &pStubUnk);
pStubUnk->QueryInterface(IID_IWbemObjectSink, (void**)&pSink->pStubSink);
char buffer[512];
sprintf_s(buffer, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND (TargetInstance.Name = 'Notepad.exe' OR TargetInstance.Name = 'Powerpnt.exe')");
hres = pSink->pSvc->ExecNotificationQueryAsync(_bstr_t("WQL"), _bstr_t(buffer), WBEM_FLAG_SEND_STATUS, NULL, pSink->pStubSink);
if (FAILED(hres)) {
cout << "ExecNotificationQueryAsync failed with = 0x" << hex << hres << endl;
throw std::exception("CreationEvent initialization failed");
}
}
void k() { cout << "process open" << endl; }
int main() {
CreationEvent::registerCreationCallback(k);
CoUninitialize();
}
Header.h
#pragma once
#ifndef _Header_h__
#define _Header_h__
#include <boost/function.hpp>
namespace CreationEvent {
typedef boost::function<void(void)> TNotificationFunc;
void registerCreationCallback(TNotificationFunc callback);
}
#endif
you should add a loop after the call to the ExecNotificationQueryAsyncat the end of the function registerCreationCallback, it can be a forever loop with done condition or you can use a WaitForSingleObject, then create an event and signaled when you want to finish your process, also you should release all the resources allocated.
For get the name of the process started, go to the Indicate function and change this
virtual HRESULT STDMETHODCALLTYPE Indicate(
LONG lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
){
for (int i = 0; i < lObjectCount; i++){
_variant_t vtProp;
_variant_t cn;
hr = apObjArray[i]->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0)
IUnknown* str = vtProp;
HRESULT hr = str->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&apObjArray[i]));
if (SUCCEEDED(hr))
{
_variant_t cn;
hr = apObjArray[i]->Get(L"Name", 0, &cn, NULL, NULL);
//here is the name of the process
//cn.bstrVal
m_callback();
}
}
/* Unregister event sink */
//you should not unregister the event if you want to receive more notifications, do this after
//your forever loop in registerCreationCallback
//pSvc->CancelAsyncCall(pStubSink);
return WBEM_S_NO_ERROR;
}
then you can send de name as a paremeter to your callback

C++ check if web browser closed

#include <windows.h>
#include <exdisp.h>
class CWebBrowser{
public:
HRESULT hr;
IWebBrowserApp *www;
HRESULT init(){
CLSID clsid;
const IID IID_IEApplication = {0x0002DF05,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
if(www) hr = www->put_Visible(-1);
if(hr!=0 || www==NULL){
QuitBrowser();
hr=CLSIDFromProgID(L"InternetExplorer.Application",&clsid);
if(hr==0) hr=CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IEApplication,reinterpret_cast<void**>(&www));
if(hr==0) hr = www->put_Visible(-1);
}
return hr;
}
HRESULT browse(BSTR addr){
VARIANT vEmpty;
VariantInit(&vEmpty);
hr=www->Navigate(addr, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
VariantClear(&vEmpty);
return hr;
}
void QuitBrowser(){
if(www){
www->Quit();
www->Release();
www=NULL;
}
}
CWebBrowser(){
hr=CoInitialize(NULL);
}
~CWebBrowser(){
if(www){
www->Quit();
www->Release();
www=NULL;
}
CoUninitialize();
}
};
I am calling the init() function to check if Web Browser is still open before browse() another webpage.
When I run the app the first time there are 2 processes showing in Task Manager (iexplorer.exe) & (iexplorer.exe *32)
When I close the app sometimes processes close and sometimes they don't.
Sometimes (iexplorer.exe *32) closes and only (iexplorer.exe) is open. When I try calling init() in this case the app crashes.
Using CodeBlocks 17.12, Windows 2000 & IE 11.
This works fine for me. Maybe you are calling www->Release() prematurely.
Here is my MCVE:
#include <Windows.h>
#include <assert.h>
#include <Exdisp.h>
#include <iostream>
#pragma comment (lib, "SHDOCVW.lib")
IWebBrowserApp *www;
HRESULT init()
{
CLSID clsid;
const IID IID_IEApplication = {0x0002DF05,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
HRESULT hr = CLSIDFromProgID (L"InternetExplorer.Application", &clsid);
if (hr)
{
std::cout << std::hex << "CLSIDFromProgID failed, error " << hr << "\n";
return hr;
}
hr = CoCreateInstance (clsid, NULL, CLSCTX_ALL, IID_IEApplication, reinterpret_cast<void**>(&www));
if (hr)
{
std::cout << std::hex << "CoCreateInstance failed, error " << hr << "\n";
return hr;
}
hr = www->put_Visible(-1);
if (hr)
{
std::cout << std::hex << "put_Visible failed, error " << hr << "\n";
www->Release ();
www = nullptr;
return hr;
}
return S_OK;
}
HRESULT browse(BSTR addr)
{
HRESULT hr;
if (www)
{
VARIANT vEmpty;
VariantInit (&vEmpty);
hr = www->Navigate (addr, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
VariantClear (&vEmpty);
}
return hr;
}
int main ()
{
HRESULT hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
assert (hr == 0);
int mb_result;
while (1)
{
hr = init ();
if (hr == 0)
{
mb_result = MessageBoxW (NULL, L"About to browse", L"Browser Test", MB_OKCANCEL);
if (mb_result == IDOK)
{
BSTR url = SysAllocString (L"https://www.google.com");
hr = browse (url);
SysFreeString (url);
if (hr)
std::cout << "browse () returned: " << std::hex << hr << "\n";
}
mb_result = MessageBoxW (NULL, L"About to quit", L"Browser Test", MB_OKCANCEL);
if (mb_result == IDOK)
www->Quit ();
www->Release ();
www = nullptr;
}
mb_result = MessageBoxW (NULL, L"Again?", L"Browser Test", MB_YESNO);
if (mb_result == IDNO)
break;
}
CoUninitialize ();
}
And if I close the browser (Edge, in my case, running on Windows 10) between the calls to init and browse I get:
browse () returned: 800706ba
which is entirely understandable, since this error means "The RPC server unavailable". It certainly doesn't crash.
Edit
Stupid Vista bugs, see the OP's recent comments. After calling www->Quit() the next call to CoCreateInstance() fails, if you're too quick about it at least.
So, two suggestions.
1 (might work):
for (int i = 0; i < 10; ++i)
{
hr = CoCreateInstance (clsid, NULL, CLSCTX_ALL, IID_IEApplication, reinterpret_cast<void**>(&www));
if (hr != ERROR_SHUTDOWN_IS_SCHEDULED) // not really
break;
Sleep (1000);
}
2 (a bit more drastic, error handling omitted for brevity):
// www->Quit ();
HWND hWnd;
hr = www->get_HWND ((SHANDLE_PTR *) &hWnd);
if (hr == S_OK)
{
DWORD processID;
if (GetWindowThreadProcessId (hWnd, &processID))
{
HANDLE hProcess = OpenProcess (PROCESS_TERMINATE, FALSE, processID);
if (hProcess)
{
TerminateProcess (hProcess, (DWORD) -1);
CloseHandle (hProcess);
}
}
}
Want to give it a try, OP, and report back?

How to load HTML string to embedded WebBrowser control?

I know there are a lot of articles about this on the internet, and I tried many of them. Although I can make my browser load a webpage on internet, somehow I can't manage to make it load HTML from memory.
Most of the time, the two methods below just don't have any visible effect; other times, they throw errors. Although other people say that "neither pPSI nor pHtmlDoc2 point to a valid object", I don't know if this is true. I have marked the lines that cause the error with a comment.
At the end of my question is the fully working code if you want to reproduce the problem.
Method 1
void WebBrowser::setHTML(const wchar_t *htmlSource) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IPersistStreamInit *pPSI = 0;
IStream *pStream = 0;
HGLOBAL hHTMLContent;
Navigate(L"about:blank");
hr = webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
//BSTR p = 0;
//if (SUCCEEDED(hr)) hr = pHtmlDoc2->get_readyState(&p);
if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
// allocate global memory to copy the HTML content to
hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
if (hHTMLContent)
{
wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
::wcscpy(p_content, htmlSource); // Debbug: p_content contains HTML string
GlobalUnlock(hHTMLContent);
// create a stream object based on the HTML content
if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
{
hr = pPSI->InitNew(); //////////// <- Sometime throw error.
if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
}
}
if (pStream) pStream->Release();
if (pPSI) pPSI->Release();
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
}
The error:
Exception thrown at 0x51539FB1 (mshtml.dll) in Test.exe: 0xC0000005:
Access violation reading location 0x00000030
Method 2
This one is modified from this article on CodeProject. In that source, it works perfectly, but it doesn't work when I try to adapt to my code:
void WebBrowser::setHTML2(const wchar_t *htmlSource) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
this->Navigate(L"about:blank");
hr = this->webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
if (SUCCEEDED(hr) && pHtmlDoc2)
{
BSTR bstr = SysAllocString(htmlSource);
SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (psaStrings)
{
VARIANT *param;
hr = SafeArrayAccessData(psaStrings, (LPVOID*)&param);
if (SUCCEEDED(hr) && param)
{
param->vt = VT_BSTR;
param->bstrVal = bstr;
//if (SUCCEEDED(hr)) hr = SafeArrayUnaccessData(psaStrings);
if (SUCCEEDED(hr))
{
hr = pHtmlDoc2->write(psaStrings); //////////// <- Sometime throw error.
}
}
SafeArrayDestroy(psaStrings);
}
}
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
}
The error:
Exception thrown at 0x51577F2E (mshtml.dll) in Test.exe: 0xC0000005:
Access violation reading location 0x000002C4
A possible third method, Loading HTML content from a Stream from Microsoft, suggests I load from the stream on a DWebBrowserEvents2::DocumentComplete event, which I have tried and failed to figure out how to implement.
Full Source Code
Below is full code. I create a new Win32 project and create/modify the following files:
Browser.h
#include <comdef.h>
#include <Exdisp.h>
#include <ExDispid.h>
#include <MsHTML.h>
#include <Mshtmhst.h>
#include <string>
#include <tchar.h>
#include <Windows.h>
using namespace std;
class WebBrowser :
public IOleClientSite,
public IOleInPlaceSite,
public IStorage
{
public:
WebBrowser(HWND hWndParent);
bool CreateBrowser();
void SetText(const wchar_t* t);
void setHTML(const wchar_t *htmlSource);
void setHTML2(const wchar_t *htmlSource);
RECT PixelToHiMetric(const RECT& _rc);
virtual void SetRect(const RECT& _rc);
// ----- Control methods -----
void GoBack();
void GoForward();
void Refresh();
void Navigate(wstring szUrl);
// ----- IUnknown -----
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void**ppvObject) override;
virtual ULONG STDMETHODCALLTYPE AddRef(void);
virtual ULONG STDMETHODCALLTYPE Release(void);
// ---------- IOleWindow ----------
virtual HRESULT STDMETHODCALLTYPE GetWindow(
__RPC__deref_out_opt HWND *phwnd) override;
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(
BOOL fEnterMode) override;
// ---------- IOleInPlaceSite ----------
virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE OnUIActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE GetWindowContext(
__RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
__RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
__RPC__out LPRECT lprcPosRect,
__RPC__out LPRECT lprcClipRect,
__RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
virtual HRESULT STDMETHODCALLTYPE Scroll(
SIZE scrollExtant) override;
virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(
BOOL fUndoable) override;
virtual HWND GetControlWindow();
virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate(void) override;
virtual HRESULT STDMETHODCALLTYPE DiscardUndoState(void) override;
virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo(void) override;
virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(
__RPC__in LPCRECT lprcPosRect) override;
// ---------- IOleClientSite ----------
virtual HRESULT STDMETHODCALLTYPE SaveObject(void) override;
virtual HRESULT STDMETHODCALLTYPE GetMoniker(
DWORD dwAssign,
DWORD dwWhichMoniker,
__RPC__deref_out_opt IMoniker **ppmk) override;
virtual HRESULT STDMETHODCALLTYPE GetContainer(
__RPC__deref_out_opt IOleContainer **ppContainer) override;
virtual HRESULT STDMETHODCALLTYPE ShowObject(void) override;
virtual HRESULT STDMETHODCALLTYPE OnShowWindow(
BOOL fShow) override;
virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout(void) override;
// ----- IStorage -----
virtual HRESULT STDMETHODCALLTYPE CreateStream(
__RPC__in_string const OLECHAR *pwcsName,
DWORD grfMode,
DWORD reserved1,
DWORD reserved2,
__RPC__deref_out_opt IStream **ppstm) override;
virtual HRESULT STDMETHODCALLTYPE OpenStream(
const OLECHAR *pwcsName,
void *reserved1,
DWORD grfMode,
DWORD reserved2,
IStream **ppstm) override;
virtual HRESULT STDMETHODCALLTYPE CreateStorage(
__RPC__in_string const OLECHAR *pwcsName,
DWORD grfMode,
DWORD reserved1,
DWORD reserved2,
__RPC__deref_out_opt IStorage **ppstg) override;
virtual HRESULT STDMETHODCALLTYPE OpenStorage(
__RPC__in_opt_string const OLECHAR *pwcsName,
__RPC__in_opt IStorage *pstgPriority,
DWORD grfMode,
__RPC__deref_opt_in_opt SNB snbExclude,
DWORD reserved,
__RPC__deref_out_opt IStorage **ppstg) override;
virtual HRESULT STDMETHODCALLTYPE CopyTo(
DWORD ciidExclude,
const IID *rgiidExclude,
__RPC__in_opt SNB snbExclude,
IStorage *pstgDest) override;
virtual HRESULT STDMETHODCALLTYPE MoveElementTo(
__RPC__in_string const OLECHAR *pwcsName,
__RPC__in_opt IStorage *pstgDest,
__RPC__in_string const OLECHAR *pwcsNewName,
DWORD grfFlags) override;
virtual HRESULT STDMETHODCALLTYPE Commit(
DWORD grfCommitFlags) override;
virtual HRESULT STDMETHODCALLTYPE Revert(void) override;
virtual HRESULT STDMETHODCALLTYPE EnumElements(
DWORD reserved1,
void *reserved2,
DWORD reserved3,
IEnumSTATSTG **ppenum) override;
virtual HRESULT STDMETHODCALLTYPE DestroyElement(
__RPC__in_string const OLECHAR *pwcsName) override;
virtual HRESULT STDMETHODCALLTYPE RenameElement(
__RPC__in_string const OLECHAR *pwcsOldName,
__RPC__in_string const OLECHAR *pwcsNewName) override;
virtual HRESULT STDMETHODCALLTYPE SetElementTimes(
__RPC__in_opt_string const OLECHAR *pwcsName,
__RPC__in_opt const FILETIME *pctime,
__RPC__in_opt const FILETIME *patime,
__RPC__in_opt const FILETIME *pmtime) override;
virtual HRESULT STDMETHODCALLTYPE SetClass(
__RPC__in REFCLSID clsid) override;
virtual HRESULT STDMETHODCALLTYPE SetStateBits(
DWORD grfStateBits,
DWORD grfMask) override;
virtual HRESULT STDMETHODCALLTYPE Stat(
__RPC__out STATSTG *pstatstg,
DWORD grfStatFlag) override;
protected:
IOleObject* oleObject;
IOleInPlaceObject* oleInPlaceObject;
IWebBrowser2* webBrowser2;
LONG iComRefCount;
RECT rObject;
HWND hWndParent;
HWND hWndControl;
};
Browser.cpp
#include "stdafx.h"
#include "Browser.h"
namespace tkString
{
wchar_t* Format(const wchar_t* format, ...)
{
va_list args;
va_start(args, format);
wchar_t *w = NULL;
int len = _vsnwprintf(NULL, 0, format, args) + 1;
if (len > 0)
{
w = new wchar_t[len];
w[0] = 0;
_vsnwprintf_s(w, len, len, format, args);
}
va_end(args);
return w;
}
}
void WebBrowser::SetText(const wchar_t* t)
{
const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";
if (t)
{
wchar_t *w = tkString::Format(html, t);
setHTML(w);
//setHTML2(w);
delete[] w;
}
else this->Navigate(L"https://google.com.vn");
}
void WebBrowser::setHTML(const wchar_t *htmlSource) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IPersistStreamInit *pPSI = 0;
IStream *pStream = 0;
HGLOBAL hHTMLContent;
Navigate(L"about:blank");
hr = webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
//BSTR p = 0;
//if (SUCCEEDED(hr)) hr = pHtmlDoc2->get_readyState(&p);
if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
// allocate global memory to copy the HTML content to
hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
if (hHTMLContent)
{
wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
::wcscpy(p_content, htmlSource);
GlobalUnlock(hHTMLContent);
// create a stream object based on the HTML content
if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
}
if (pStream) pStream->Release();
if (pPSI) pPSI->Release();
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
}
void WebBrowser::setHTML2(const wchar_t *htmlSource) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
this->Navigate(L"about:blank");
hr = this->webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
if (SUCCEEDED(hr) && pHtmlDoc2)
{
BSTR bstr = SysAllocString(htmlSource);
SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (psaStrings)
{
VARIANT *param;
hr = SafeArrayAccessData(psaStrings, (LPVOID*)&param);
if (SUCCEEDED(hr) && param)
{
param->vt = VT_BSTR;
param->bstrVal = bstr;
//if (SUCCEEDED(hr)) hr = SafeArrayUnaccessData(psaStrings);
if (SUCCEEDED(hr)) hr = pHtmlDoc2->write(psaStrings);
}
SafeArrayDestroy(psaStrings);
}
}
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
}
WebBrowser::WebBrowser(HWND _hWndParent)
{
iComRefCount = 0;
::SetRect(&rObject, 0, 0, 600, 400);
hWndParent = _hWndParent;
if (CreateBrowser() == FALSE)
{
return;
}
webBrowser2->put_Visible(TRUE);
ShowWindow(GetControlWindow(), SW_SHOW);
this->Navigate(_T("about:blank"));
}
bool WebBrowser::CreateBrowser()
{
HRESULT hr;
hr = ::OleCreate(CLSID_WebBrowser,
IID_IOleObject, OLERENDER_DRAW, 0, this, this,
(void**)&oleObject);
if (FAILED(hr))
{
MessageBox(NULL, _T("Cannot create oleObject CLSID_WebBrowser"),
_T("Error"),
MB_ICONERROR);
return FALSE;
}
hr = oleObject->SetClientSite(this);
hr = OleSetContainedObject(oleObject, TRUE);
RECT posRect;
::SetRect(&rObject, 0, 0, 600, 400);
hr = oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE,
NULL, this, -1, hWndParent, &posRect);
if (FAILED(hr))
{
MessageBox(NULL, _T("oleObject->DoVerb() failed"),
_T("Error"),
MB_ICONERROR);
return FALSE;
}
hr = oleObject->QueryInterface(&webBrowser2);
if (FAILED(hr))
{
MessageBox(NULL, _T("oleObject->QueryInterface(&webBrowser2) failed"),
_T("Error"),
MB_ICONERROR);
return FALSE;
}
return TRUE;
}
RECT WebBrowser::PixelToHiMetric(const RECT& _rc)
{
static bool s_initialized = false;
static int s_pixelsPerInchX, s_pixelsPerInchY;
if (!s_initialized)
{
HDC hdc = ::GetDC(0);
s_pixelsPerInchX = ::GetDeviceCaps(hdc, LOGPIXELSX);
s_pixelsPerInchY = ::GetDeviceCaps(hdc, LOGPIXELSY);
::ReleaseDC(0, hdc);
s_initialized = true;
}
RECT rc;
rc.left = MulDiv(2540, _rc.left, s_pixelsPerInchX);
rc.top = MulDiv(2540, _rc.top, s_pixelsPerInchY);
rc.right = MulDiv(2540, _rc.right, s_pixelsPerInchX);
rc.bottom = MulDiv(2540, _rc.bottom, s_pixelsPerInchY);
return rc;
}
void WebBrowser::SetRect(const RECT& _rc)
{
rObject = _rc;
{
RECT hiMetricRect = PixelToHiMetric(rObject);
SIZEL sz;
sz.cx = hiMetricRect.right - hiMetricRect.left;
sz.cy = hiMetricRect.bottom - hiMetricRect.top;
oleObject->SetExtent(DVASPECT_CONTENT, &sz);
}
if (oleInPlaceObject != 0)
{
oleInPlaceObject->SetObjectRects(&rObject, &rObject);
}
}
// ----- Control methods -----
void WebBrowser::GoBack()
{
this->webBrowser2->GoBack();
}
void WebBrowser::GoForward()
{
this->webBrowser2->GoForward();
}
void WebBrowser::Refresh()
{
this->webBrowser2->Refresh();
}
void WebBrowser::Navigate(wstring szUrl)
{
bstr_t url(szUrl.c_str());
variant_t flags(0x02u); //navNoHistory
this->webBrowser2->Navigate(url, &flags, 0, 0, 0);
}
// ----- IUnknown -----
HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid,
void**ppvObject)
{
if (riid == __uuidof(IUnknown))
{
(*ppvObject) = static_cast<IOleClientSite*>(this);
}
else if (riid == __uuidof(IOleInPlaceSite))
{
(*ppvObject) = static_cast<IOleInPlaceSite*>(this);
}
else
{
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE WebBrowser::AddRef(void)
{
iComRefCount++;
return iComRefCount;
}
ULONG STDMETHODCALLTYPE WebBrowser::Release(void)
{
iComRefCount--;
return iComRefCount;
}
// ---------- IOleWindow ----------
HRESULT STDMETHODCALLTYPE WebBrowser::GetWindow(
__RPC__deref_out_opt HWND *phwnd)
{
(*phwnd) = hWndParent;
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(
BOOL fEnterMode)
{
return E_NOTIMPL;
}
// ---------- IOleInPlaceSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void)
{
OleLockRunning(oleObject, TRUE, FALSE);
oleObject->QueryInterface(&oleInPlaceObject);
oleInPlaceObject->SetObjectRects(&rObject, &rObject);
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(
__RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
__RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
__RPC__out LPRECT lprcPosRect,
__RPC__out LPRECT lprcClipRect,
__RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
HWND hwnd = hWndParent;
(*ppFrame) = NULL;
(*ppDoc) = NULL;
(*lprcPosRect).left = rObject.left;
(*lprcPosRect).top = rObject.top;
(*lprcPosRect).right = rObject.right;
(*lprcPosRect).bottom = rObject.bottom;
*lprcClipRect = *lprcPosRect;
lpFrameInfo->fMDIApp = false;
lpFrameInfo->hwndFrame = hwnd;
lpFrameInfo->haccel = NULL;
lpFrameInfo->cAccelEntries = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(
SIZE scrollExtant)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(
BOOL fUndoable)
{
return S_OK;
}
HWND WebBrowser::GetControlWindow()
{
if (hWndControl != 0)
return hWndControl;
if (oleInPlaceObject == 0)
return 0;
oleInPlaceObject->GetWindow(&hWndControl);
return hWndControl;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void)
{
hWndControl = 0;
oleInPlaceObject = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(
__RPC__in LPCRECT lprcPosRect)
{
return E_NOTIMPL;
}
// ---------- IOleClientSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(
DWORD dwAssign,
DWORD dwWhichMoniker,
__RPC__deref_out_opt IMoniker **ppmk)
{
if ((dwAssign == OLEGETMONIKER_ONLYIFTHERE) &&
(dwWhichMoniker == OLEWHICHMK_CONTAINER))
return E_FAIL;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(
__RPC__deref_out_opt IOleContainer **ppContainer)
{
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(
BOOL fShow)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void)
{
return E_NOTIMPL;
}
// ----- IStorage -----
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(
__RPC__in_string const OLECHAR *pwcsName,
DWORD grfMode,
DWORD reserved1,
DWORD reserved2,
__RPC__deref_out_opt IStream **ppstm)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(
const OLECHAR *pwcsName,
void *reserved1,
DWORD grfMode,
DWORD reserved2,
IStream **ppstm)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(
__RPC__in_string const OLECHAR *pwcsName,
DWORD grfMode,
DWORD reserved1,
DWORD reserved2,
__RPC__deref_out_opt IStorage **ppstg)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(
__RPC__in_opt_string const OLECHAR *pwcsName,
__RPC__in_opt IStorage *pstgPriority,
DWORD grfMode,
__RPC__deref_opt_in_opt SNB snbExclude,
DWORD reserved,
__RPC__deref_out_opt IStorage **ppstg)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(
DWORD ciidExclude,
const IID *rgiidExclude,
__RPC__in_opt SNB snbExclude,
IStorage *pstgDest)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(
__RPC__in_string const OLECHAR *pwcsName,
__RPC__in_opt IStorage *pstgDest,
__RPC__in_string const OLECHAR *pwcsNewName,
DWORD grfFlags)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::Commit(
DWORD grfCommitFlags)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(
DWORD reserved1,
void *reserved2,
DWORD reserved3,
IEnumSTATSTG **ppenum)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(
__RPC__in_string const OLECHAR *pwcsName)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(
__RPC__in_string const OLECHAR *pwcsOldName,
__RPC__in_string const OLECHAR *pwcsNewName)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(
__RPC__in_opt_string const OLECHAR *pwcsName,
__RPC__in_opt const FILETIME *pctime,
__RPC__in_opt const FILETIME *patime,
__RPC__in_opt const FILETIME *pmtime)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(
__RPC__in REFCLSID clsid)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(
DWORD grfStateBits,
DWORD grfMask)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE WebBrowser::Stat(
__RPC__out STATSTG *pstatstg,
DWORD grfStatFlag)
{
return E_NOTIMPL;
}
Win32Project1.cpp
#include "stdafx.h"
#include "Win32Project1.h"
#include "Browser.h"
HWND mainHWND;
WebBrowser* myBrowser = NULL;
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
OleInitialize(NULL);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WIN32PROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
myBrowser = new WebBrowser(mainHWND);
//myBrowser->Navigate(L"https://google.com.vn");
myBrowser->SetText(L"Hello!");
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT1));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32PROJECT1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
mainHWND = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!mainHWND)
{
return FALSE;
}
ShowWindow(mainHWND, nCmdShow);
UpdateWindow(mainHWND);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
// Removed
return (INT_PTR)FALSE;
}
Other source files created by Visual Studio remain untouched.
Question
What problem causes my program to fail to load the HTML string, and how do I fix it? Or do you have any working example (no MFC or ATL) to load an HTML string to the WebBrowser?
Note: I know I can write the HTML string to hard drive and pass the file path to the browser, but I don't want the hard disk involved at all.
Note 2: To win the bounty, please give a complete example of working code, using as much of my code as possible. The browser should load HTML string successfully. Even when it's loading an online web page, it should stop to process this new command.
Navigate(L"about:blank");
The posted source code does not demonstrate the problem well. It shows no errors, but also no "Hello". Only by omitting the Navigate() call does it get close to failing on nullptr errors. The code is simply not finished, it is missing the required plumbing to capture the DocumentComplete event. Only after this event is fired can the code inside WebBrowser::setHtml() work.
Have the WebBrowser class implement the IDispatch and IUnknown interfaces:
class WebBrowser :
public IDispatch,
public IUnknown,
public IOleClientSite,
public IOleInPlaceSite,
public IStorage
{
private:
HRESULT OnCompleted(DISPPARAMS* args);
wchar_t* htmlSource;
IConnectionPoint* callback;
DWORD eventCookie;
// etc...
};
Note how the htmlSource variable is now a class member, the string needs to be stored until the DocumentComplete event fires.
We need to implement IDispatch. That's pretty easy to do, I put the code inline:
// ---------- IDispatch ----------
HRESULT GetTypeInfoCount(UINT *pctinfo) { return E_FAIL; }
HRESULT GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_FAIL; }
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { return E_FAIL; }
HRESULT Invoke(DISPID dispIdMember, REFIID, LCID, WORD,
DISPPARAMS *pDispParams, VARIANT *pVarResult,
EXCEPINFO*, UINT*) {
if (dispIdMember == DISPID_DOCUMENTCOMPLETE) return OnCompleted(pDispParams);
else return S_OK;
}
QueryInterface() needs to be tweaked:
HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid, void**ppvObject)
{
if (riid == __uuidof(IUnknown)) *ppvObject = static_cast<IUnknown*>(this);
else if (riid == __uuidof(IDispatch)) *ppvObject = static_cast<IDispatch*>(this);
else if (riid == __uuidof(IOleClientSite)) *ppvObject = static_cast<IOleClientSite*>(this);
else if (riid == __uuidof(IOleInPlaceSite)) *ppvObject = static_cast<IOleInPlaceSite*>(this);
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
The constructor needs to subscribe the event interface:
WebBrowser::WebBrowser(HWND _hWndParent)
{
//...
// appended:
htmlSource = nullptr;
IConnectionPointContainer* container = nullptr;
webBrowser2->QueryInterface(IID_IConnectionPointContainer, (void**)&container);
container->FindConnectionPoint(__uuidof(DWebBrowserEvents2), &callback);
IUnknown* punk = nullptr;
this->QueryInterface(IID_IUnknown, (void**)&punk);
callback->Advise(punk, &eventCookie);
punk->Release();
container->Release();
}
The setText() function becomes very simple, we're not going to worry about setHtml() at all. We'll just set the htmlSource member so we know what to do when the DocumentComplete event fires:
void WebBrowser::SetText(const wchar_t* t)
{
const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";
if (htmlSource) delete[] htmlSource;
htmlSource = tkString::Format(html, t);
this->Navigate(L"about::blank");
}
And finally the added function that runs when the DocumentComplete event fires. Most of the code got moved from setHtml:
HRESULT WebBrowser::OnCompleted(DISPPARAMS* args) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IPersistStreamInit *pPSI = 0;
IStream *pStream = 0;
HGLOBAL hHTMLContent;
if (!htmlSource) return S_OK;
hr = webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
// allocate global memory to copy the HTML content to
hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
if (hHTMLContent)
{
wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
::wcscpy(p_content, htmlSource);
GlobalUnlock(hHTMLContent);
// create a stream object based on the HTML content
if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
}
if (pStream) pStream->Release();
if (pPSI) pPSI->Release();
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
delete[] htmlSource;
htmlSource = nullptr;
return S_OK;
}
I'll leave proper cleanup as a // todo. Running this code now produces the desired outcome:
Based on Hans Passant's answer, bellow is the full working code, for anyone want to test.
Create new Win32 project named "Win32Project1" and add/edit following files:
WebBrowser.h
#pragma once
#include <comdef.h>
#include <Exdisp.h>
#include <ExDispid.h>
#include <MsHTML.h>
#include <Mshtmhst.h>
#include <string>
#include <tchar.h>
#include <Windows.h>
class WebBrowser :
public IDispatch,
public IOleClientSite,
public IOleInPlaceSite,
public IStorage
{
public:
WebBrowser(HWND hWndParent);
void SetText(const wchar_t* t);
void setHTML(const wchar_t *htmlText, bool shouldWrapinBODYtag = true);
// ----- Create Browser -----
HRESULT RegisterGIWebBrowser();
HRESULT GetGIWebBrowser(IWebBrowser2** pwb);
bool CreateBrowser();
// ----- For Resize Window -----
RECT PixelToHiMetric(const RECT& _rc);
virtual void SetRect(const RECT& _rc);
virtual HWND GetControlWindow();
// ----- Control methods -----
void GoBack();
void GoForward();
void Refresh();
void Navigate(const wchar_t *szUrl);
// ----- IUnknown -----
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject) override;
virtual ULONG STDMETHODCALLTYPE AddRef(void);
virtual ULONG STDMETHODCALLTYPE Release(void);
// ---------- IOleWindow ----------
virtual HRESULT STDMETHODCALLTYPE GetWindow(__RPC__deref_out_opt HWND *phwnd) override;
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;
// ---------- IOleInPlaceSite ----------
virtual HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(__RPC__deref_out_opt IOleInPlaceFrame **ppFrame, __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc, __RPC__out LPRECT lprcPosRect, __RPC__out LPRECT lprcClipRect, __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(SIZE scrollExtant) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(BOOL fUndoable) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(__RPC__in LPCRECT lprcPosRect) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void) override;
// ---------- IOleClientSite ----------
virtual HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(__RPC__deref_out_opt IOleContainer **ppContainer) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(BOOL fShow) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, __RPC__deref_out_opt IMoniker **ppmk) override;
// ----- IStorage -----
virtual HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStream **ppstm) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStorage **ppstg) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgPriority, DWORD grfMode, __RPC__deref_opt_in_opt SNB snbExclude, DWORD reserved, __RPC__deref_out_opt IStorage **ppstg) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, __RPC__in_opt SNB snbExclude, IStorage *pstgDest) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(__RPC__in_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgDest, __RPC__in_string const OLECHAR *pwcsNewName, DWORD grfFlags) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::Commit(DWORD grfCommitFlags) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(__RPC__in_string const OLECHAR *pwcsName) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(__RPC__in_string const OLECHAR *pwcsOldName, __RPC__in_string const OLECHAR *pwcsNewName) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt const FILETIME *pctime, __RPC__in_opt const FILETIME *patime, __RPC__in_opt const FILETIME *pmtime) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(__RPC__in REFCLSID clsid) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(DWORD grfStateBits, DWORD grfMask) override;
virtual HRESULT STDMETHODCALLTYPE WebBrowser::Stat(__RPC__out STATSTG *pstatstg, DWORD grfStatFlag) override;
// ---------- IDispatch ----------
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(__RPC__out UINT *pctinfo) override;
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, LCID, __RPC__deref_out_opt ITypeInfo **) override;
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(__RPC__in REFIID riid, __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames, __RPC__in_range(0, 16384) UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId) override;
virtual HRESULT STDMETHODCALLTYPE Invoke(_In_ DISPID dispIdMember, _In_ REFIID, _In_ LCID, _In_ WORD, _In_ DISPPARAMS *pDispParams, _Out_opt_ VARIANT *pVarResult, _Out_opt_ EXCEPINFO*, _Out_opt_ UINT*) override;
private:
IOleObject* oleObject;
IOleInPlaceObject* oleInPlaceObject;
IWebBrowser2* webBrowser2;
LONG iComRefCount;
RECT rObject;
HWND hWndParent;
HWND hWndControl;
HRESULT OnCompleted(DISPPARAMS* args);
wchar_t* htmlSource;
IConnectionPoint* callback;
DWORD eventCookie;
DWORD dwGIWebBrowserCookie;
bool isFullyCreated;
};
WebBrowser.cpp
#include "stdafx.h"
#include "WebBrowser.h"
namespace tkString
{
wchar_t* Format(const wchar_t* format, ...)
{
va_list args;
va_start(args, format);
wchar_t* w = NULL;
int len = _vscwprintf(format, args) + 1;
if (len > 1)
{
w = new wchar_t[len];
w[0] = 0;
_vsnwprintf_s(w, len, len, format, args);
}
va_end(args);
return w;
}
}
void WebBrowser::SetText(const wchar_t* t)
{
if (isFullyCreated)
{
// TODO: escape text
const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";
if (htmlSource) delete[] htmlSource;
htmlSource = tkString::Format(html, t);
this->Navigate(L"about:blank");
}
else {}
}
void WebBrowser::setHTML(const wchar_t* htmlText, bool shouldWrapinBODYtag)
{
if (isFullyCreated)
{
if (htmlSource) delete[] htmlSource;
htmlSource = NULL;
if (shouldWrapinBODYtag)
{
const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body>%ls</body></html>";
htmlSource = tkString::Format(html, htmlText);
}
else
{
if (htmlText)
{
size_t len = wcslen(htmlText) + 1;
htmlSource = new wchar_t[len];
wmemcpy(htmlSource, htmlText, len);
}
}
this->Navigate(L"about:blank");
}
else {}
}
HRESULT WebBrowser::OnCompleted(DISPPARAMS* args)
{
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IPersistStreamInit *pPSI = 0;
IStream *pStream = 0;
HGLOBAL hHTMLContent;
if (!htmlSource) return S_OK;
hr = webBrowser2->get_Document(&pDispatch);
if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
// allocate global memory to copy the HTML content to
size_t len = wcslen(htmlSource) + 1;
hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, len * sizeof(wchar_t));
if (hHTMLContent)
{
wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
wmemcpy_s(p_content, len, htmlSource, len);
GlobalUnlock(hHTMLContent);
// create a stream object based on the HTML content
if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
}
if (pStream) pStream->Release();
if (pPSI) pPSI->Release();
if (pHtmlDoc2) pHtmlDoc2->Release();
if (pDispatch) pDispatch->Release();
delete[] htmlSource;
htmlSource = nullptr;
return S_OK;
}
// ----- Control methods -----
void WebBrowser::Navigate(const wchar_t *szUrl)
{
HRESULT hr = E_FAIL;
IWebBrowser2 *pwb = NULL;
hr = GetGIWebBrowser(&pwb);
if (SUCCEEDED(hr) && pwb)
{
bstr_t url(szUrl);
variant_t flags(0x02u); //navNoHistory
pwb->Navigate(url, &flags, 0, 0, 0);
}
else { /* Handle error */ }
}
void WebBrowser::GoBack() { this->webBrowser2->GoBack(); }
void WebBrowser::GoForward() { this->webBrowser2->GoForward(); }
void WebBrowser::Refresh() { this->webBrowser2->Refresh(); }
WebBrowser::WebBrowser(HWND _hWndParent) :
isFullyCreated(false)
{
HRESULT hr = E_FAIL;
htmlSource = nullptr;
IConnectionPointContainer* container = nullptr;
IUnknown* punk = nullptr;
iComRefCount = 0;
::SetRect(&rObject, 0, 0, 300, 300);
hWndParent = _hWndParent;
if (CreateBrowser())
{
this->Navigate(L"about:blank");
hr = RegisterGIWebBrowser();
if (SUCCEEDED(hr))
{
webBrowser2->put_Visible(TRUE);
ShowWindow(GetControlWindow(), SW_SHOW);
hr = webBrowser2->QueryInterface(IID_IConnectionPointContainer, (void**)&container);
if (SUCCEEDED(hr) && container) hr = container->FindConnectionPoint(__uuidof(DWebBrowserEvents2), &callback);
if (SUCCEEDED(hr) && container) hr = this->QueryInterface(IID_IUnknown, (void**)&punk);
if (SUCCEEDED(hr) && container) hr = callback->Advise(punk, &eventCookie);
if (SUCCEEDED(hr) && eventCookie) isFullyCreated = true;
if (punk) punk->Release();
if (container) container->Release();
}
}
}
bool WebBrowser::CreateBrowser()
{
HRESULT hr;
hr = ::OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, 0, this, this, (void**)&oleObject);
if (SUCCEEDED(hr) && oleObject)
{
if (SUCCEEDED(hr)) hr = oleObject->SetClientSite(this);
if (SUCCEEDED(hr)) hr = OleSetContainedObject(oleObject, TRUE);
if (SUCCEEDED(hr))
{
RECT posRect;
::SetRect(&posRect, -300, -300, 300, 300);
hr = oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, -1, hWndParent, &posRect);
}
hr = oleObject->QueryInterface(&webBrowser2);
}
if (FAILED(hr) || !webBrowser2)
{
MessageBox(NULL, L"Create Browser failed!", L"Error", MB_ICONERROR);
return false;
}
return true;
}
RECT WebBrowser::PixelToHiMetric(const RECT& _rc)
{
static bool s_initialized = false;
static int s_pixelsPerInchX, s_pixelsPerInchY;
if (!s_initialized)
{
HDC hdc = ::GetDC(0);
s_pixelsPerInchX = ::GetDeviceCaps(hdc, LOGPIXELSX);
s_pixelsPerInchY = ::GetDeviceCaps(hdc, LOGPIXELSY);
::ReleaseDC(0, hdc);
s_initialized = true;
}
RECT rc;
rc.left = MulDiv(2540, _rc.left, s_pixelsPerInchX);
rc.top = MulDiv(2540, _rc.top, s_pixelsPerInchY);
rc.right = MulDiv(2540, _rc.right, s_pixelsPerInchX);
rc.bottom = MulDiv(2540, _rc.bottom, s_pixelsPerInchY);
return rc;
}
void WebBrowser::SetRect(const RECT& _rc)
{
rObject = _rc;
{
RECT hiMetricRect = PixelToHiMetric(rObject);
SIZEL sz;
sz.cx = hiMetricRect.right - hiMetricRect.left;
sz.cy = hiMetricRect.bottom - hiMetricRect.top;
oleObject->SetExtent(DVASPECT_CONTENT, &sz);
}
if (oleInPlaceObject != 0)
{
oleInPlaceObject->SetObjectRects(&rObject, &rObject);
}
}
HWND WebBrowser::GetControlWindow() { if (!hWndControl && oleInPlaceObject) oleInPlaceObject->GetWindow(&hWndControl); return hWndControl; }
// Register IWebBrowser2 instance for use in threads
HRESULT WebBrowser::RegisterGIWebBrowser()
{
HRESULT hr = E_FAIL;
IUnknown* pIUnknown = NULL;
IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
dwGIWebBrowserCookie = 0;
hr = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable);
if (SUCCEEDED(hr) && pIGlobalInterfaceTable) hr = webBrowser2->QueryInterface(IID_IUnknown, (void**)&pIUnknown);
if (SUCCEEDED(hr) && pIUnknown) hr = pIGlobalInterfaceTable->RegisterInterfaceInGlobal(pIUnknown, __uuidof(IWebBrowser2), &dwGIWebBrowserCookie);
if (pIUnknown) pIUnknown->Release();
if (pIGlobalInterfaceTable) pIGlobalInterfaceTable->Release();
if (SUCCEEDED(hr) && dwGIWebBrowserCookie) return hr;
return E_FAIL;
}
// Get IWebBrowser2 instance from threads
HRESULT WebBrowser::GetGIWebBrowser(IWebBrowser2** pwb)
{
HRESULT hr = E_FAIL;
IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
*pwb = NULL;
if (dwGIWebBrowserCookie) hr = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable);
if (SUCCEEDED(hr) && pIGlobalInterfaceTable) hr = pIGlobalInterfaceTable->GetInterfaceFromGlobal(dwGIWebBrowserCookie, __uuidof(IWebBrowser2), (void **)pwb);
if (pIGlobalInterfaceTable) pIGlobalInterfaceTable->Release();
return hr;
}
// ----- IUnknown -----
ULONG STDMETHODCALLTYPE WebBrowser::AddRef(void) { return ++iComRefCount; }
ULONG STDMETHODCALLTYPE WebBrowser::Release(void) { return --iComRefCount; }
HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid, void**ppvObject)
{
if (riid == __uuidof(IUnknown)) *ppvObject = static_cast<IDispatch*>(this);
else if (riid == __uuidof(IDispatch)) *ppvObject = static_cast<IDispatch*>(this);
else if (riid == __uuidof(IOleClientSite)) *ppvObject = static_cast<IOleClientSite*>(this);
else if (riid == __uuidof(IOleInPlaceSite)) *ppvObject = static_cast<IOleInPlaceSite*>(this);
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
// ---------- IOleInPlaceSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void){ return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void){ return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void)
{
OleLockRunning(oleObject, TRUE, FALSE);
oleObject->QueryInterface(&oleInPlaceObject);
oleInPlaceObject->SetObjectRects(&rObject, &rObject);
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(__RPC__deref_out_opt IOleInPlaceFrame **ppFrame, __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc, __RPC__out LPRECT lprcPosRect, __RPC__out LPRECT lprcClipRect, __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
HWND hwnd = hWndParent;
(*ppFrame) = NULL;
(*ppDoc) = NULL;
(*lprcPosRect).left = rObject.left;
(*lprcPosRect).top = rObject.top;
(*lprcPosRect).right = rObject.right;
(*lprcPosRect).bottom = rObject.bottom;
*lprcClipRect = *lprcPosRect;
lpFrameInfo->fMDIApp = false;
lpFrameInfo->hwndFrame = hwnd;
lpFrameInfo->haccel = NULL;
lpFrameInfo->cAccelEntries = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(SIZE scrollExtant) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(BOOL fUndoable) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(__RPC__in LPCRECT lprcPosRect) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void) { hWndControl = 0; oleInPlaceObject = 0; return S_OK; }
// ---------- IDispatch ----------
HRESULT WebBrowser::GetTypeInfoCount(__RPC__out UINT *pctinfo) { return E_FAIL; }
HRESULT WebBrowser::GetTypeInfo(UINT, LCID, __RPC__deref_out_opt ITypeInfo **) { return E_FAIL; }
HRESULT WebBrowser::GetIDsOfNames(__RPC__in REFIID riid, __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames, __RPC__in_range(0, 16384) UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId) { return E_FAIL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Invoke(_In_ DISPID dispIdMember, _In_ REFIID, _In_ LCID, _In_ WORD, _In_ DISPPARAMS *pDispParams, _Out_opt_ VARIANT *pVarResult, _Out_opt_ EXCEPINFO*, _Out_opt_ UINT*)
{
if (dispIdMember == DISPID_DOCUMENTCOMPLETE) return OnCompleted(pDispParams);
else return S_OK;
}
// ---------- IOleWindow ----------
HRESULT STDMETHODCALLTYPE WebBrowser::GetWindow(__RPC__deref_out_opt HWND *phwnd) { (*phwnd) = hWndParent; return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; }
// ---------- IOleClientSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(__RPC__deref_out_opt IOleContainer **ppContainer) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(BOOL fShow) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, __RPC__deref_out_opt IMoniker **ppmk) { if ((dwAssign == OLEGETMONIKER_ONLYIFTHERE) && (dwWhichMoniker == OLEWHICHMK_CONTAINER))return E_FAIL; return E_NOTIMPL; }
// ----- IStorage -----
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStream **ppstm) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStorage **ppstg) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgPriority, DWORD grfMode, __RPC__deref_opt_in_opt SNB snbExclude, DWORD reserved, __RPC__deref_out_opt IStorage **ppstg) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, __RPC__in_opt SNB snbExclude, IStorage *pstgDest) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(__RPC__in_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgDest, __RPC__in_string const OLECHAR *pwcsNewName, DWORD grfFlags) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Commit(DWORD grfCommitFlags) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(__RPC__in_string const OLECHAR *pwcsName) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(__RPC__in_string const OLECHAR *pwcsOldName, __RPC__in_string const OLECHAR *pwcsNewName) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt const FILETIME *pctime, __RPC__in_opt const FILETIME *patime, __RPC__in_opt const FILETIME *pmtime) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(__RPC__in REFCLSID clsid) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(DWORD grfStateBits, DWORD grfMask) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Stat(__RPC__out STATSTG *pstatstg, DWORD grfStatFlag) { return E_NOTIMPL; }
Win32Project1.cpp
#include "stdafx.h"
#include "Win32Project1.h"
#include "WebBrowser.h"
HWND hWnd = NULL;
HWND tkWebBrowserHWND = NULL;
WebBrowser* tkWebBrowser = NULL;
void callFromThread();
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
OleInitialize(NULL);
MyRegisterClass(hInstance);
if (!InitInstance(hInstance, nCmdShow)) return FALSE;
tkWebBrowserHWND = CreateWindow(L"Static", NULL, WS_CHILD | WS_VISIBLE, 221, 0, 300, 300, hWnd, NULL, hInstance, 0);
tkWebBrowser = new WebBrowser(tkWebBrowserHWND);
// Use one of following command
//tkWebBrowser->SetText(L"Hello!");
//tkWebBrowser->setHTML(L"<h1>Title</h1><div>Content</div>");
//tkWebBrowser->setHTML(L"<html><body><h1>Title 2</h1><div>Content 2</div></body></html>", false);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)callFromThread, 0, 0, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
void callFromThread()
{
OleInitialize(NULL);
// Use one of following command
//tkWebBrowser->SetText(L"Hello from thread!");
//tkWebBrowser->setHTML(L"<h1>Title from thread</h1><div>Content from thread</div>");
tkWebBrowser->setHTML(L"<html><body><h1>Title 2 from thread</h1><div>Content 2 from thread</div></body></html>", false);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
HINSTANCE hInst;
const wchar_t* szTitle = L"Title";
const wchar_t* szWindowClass = L"Window Class";
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd) return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}