Embed an HTML control in your own window using plain C

Specifically shows how to embed a browser OLE object in your own window, and more generally demonstrates how to manipulate and create COM/OLE objects, in plain C (ie, no MFC, WTL, ATL, .NET, C#, nor even C++). The latter is applicable to many other uses, such as creating your own script engine.

Introduction

There are numerous examples that demonstrate how to embed Internet Explorer as an OLE/COM object in your own window. But these examples typically use Microsoft Foundation Classes (MFC), .NET, C#, or at least Windows Template Library (WTL) because those frameworks have pre-fabricated "wrappers" to easily give you an "HTML control" to embed in your window. If you're trying to use plain C, without MFC, WTL, .NET, C#, or even any C++ code at all, then there is a dearth of examples and information how to deal with COM objects such as IE's IWebBrowser2. Here is an article and working example in C to specifically show you what you need to do in order to embed IE in your own window.

In fact, I've even wrapped up the example C code (to embed IE in your own window) into a Dynamic Link Library (DLL) so that you can simply call one function to display a web page or some HTML string in a window you create. You won't even need to get your hands dirty with COM (unless you plan to modify the source of the DLL).

Before proceeding with this article, you should first read my series of articles regarding COM in plain C. Part 1 discusses information you'll need to know in order to use COM objects. We will also have to deal with objects that have multiple interfaces, as discussed in Part 4. We'll be using automation datatypes, as discussed in Part 2. Finally, we'll also be dealing with events (callbacks), discussed in Part 5.

After reading my above series of COM articles, you've got some needed background on writing COM objects in plain C. Let's now examine what we need to host the browser object. You may wish to peruse the source code file Simple.c (in the Simple directory) as you read the following discussion.

First of all, the browser object expects us to provide (at least) 3 of our own COM objects. We need an IOleInPlaceFrame, IOleClientSite, and an IOleInPlaceSite objects. All of these objects (and their VTables, and GUIDs) are already defined for us in Microsoft's include files (shipped with your C interpreter or in the SDK downloadable from Microsoft's web site). So, they each have their own specific pre-defined set of functions in the VTable.

Let's just examine our IOleClientSite object. It has a VTable which is defined as a IOleClientSiteVtbl struct. Essentially, it's an array of 9 pointers to functions that we must supply in our program. (ie, We have to write 9 specific functions just for our IOleClientSite object). Of course, the first 3 functions will be the QueryInterface, AddRef, and Release functions for our IOleClientSite object. In Simple.c, I've named those three functions Site_QueryInterface, Site_AddRef, and Site_Release (to avoid any name conflicts with the QueryInterface, AddRef, and Release functions of our other COM objects). In fact, I've named the other 6 functions starting with Site_. They have names like Site_SaveObject, Site_ShowObject, etc. Our IOleClientSite functions are called by the browser object to interact with our window that contains the browser object. What the specific purpose of each of those functions is, and what arguments are passed to it, you can check for yourself by looking through the documentation on MSDN about the IOleClientSite object.

To create the VTable for our IOleClientSite object, the easiest thing to do is just declare it as a static global, and initialize it with the pointers to our 9 functions. Here is how we do that in our C source:

Now have a global variable named MyIOleClientSiteTable which is a properly initialized VTable for our IOleClientSite object.

In Simple.c, you'll see that we also declare our other objects' VTables as globals. But we do not declare our objects themselves as globals. We are going to add some extra members to these objects for our own private data. For example, instead of a just an ordinary IOleInPlaceFrame, we're going to define our own _IOleInPlaceFrameEx which contains an embedded IOleInPlaceFrame plus an extra HWND where we can store the handle to our own window. Notice that this extra HWND member is added to the end of the struct, after the IOleInPlaceFrame. That is very important. The IOleInPlaceFrame (with its VTable pointer) must come first. And note that our extra data is window-specific. In other words, we'll need a different IOleInPlaceFrame, IOleClientSite, and IOleInPlaceSite struct per each window that has an embedded browser object. For this reason, we'll allocate them when we create a window, instead of declaring them as globals.

The browser object considers our IOleInPlaceFrame and IOleInPlaceSite objects to be sub-objects of our IOleClientSite object. So, when the browser wants/needs a pointer to one of those, it is going to call one of our IOleClientSite functions to ask us to return a pointer to it. Usually the function it calls is our IOleClientSite's QueryInterface function. (But for some objects, such as our IOleInPlaceFrame object, the browser will request the pointer by calling a different IOleClientSite function. That's just the way it is). The implication here is that our IOleClientSite functions will need to have access to our IOleInPlaceFrame and IOleInPlaceSite objects so that our IOleClientSite functions can return pointers to any one (when asked to do that by the browser). For this reason, we're going to define one large object (_IOleClientSiteEx) which has our 3 objects - IOleClientSite, IOleInPlaceFrame and IOleInPlaceSite - all embedded inside this large struct. That makes it easy to locate any one object from another, simply using pointer arithmetic. The only requirement is that our IOleClientSite be the first thing inside of this larger object. That way, this larger object can masquerade as an ordinary IOleClientSite.

You can consult the MSDN documentation to learn what the functions in our IOleInPlaceFrame, IOleClientSite, and IOleInPlaceSite VTables are supposed to do, and what is passed to them. In Simple.c, we employ only as much functionality as is needed to display a web page in a window of our own creation.

As mentioned, the browser object expects us to supply at least the 3 above objects. But there are other objects we may optionally implement in our program in order to support additional interaction with the browser object. In particular, a IDocHostUIHandler is very useful. It lets us control certain user interface features, such as being able to replace/disable the pop-up context menu when the user right-clicks on the embedded browser object, or determine whether scroll bars or borders or other such things are rendered, or prevent embedded scripts in the web page from running, or have a new browser window automatically open if the user clicks on any link, etc. Because such an object is so useful, we also implement an IDocHostUIHandler interface in our example C code. (ie, We have a IDocHostUIHandler struct, 18 IDocHostUIHandler functions, and a VTable containing pointers to those 18 functions). We embed this IDocHostUIHandler inside of our large _IOleClientSiteEx object.

Before we obtain Microsoft's browser object, we must call OleInitialize once to make sure that the OLE/COM system is initialized for our process. Normally, when using COM, you call CoInitialize. But the browser needs some extra OLE initialization done (which CoInitialize does not do). So we call OleInitialize, which itself calls CoInitialize for us.

Now we're ready to obtain a browser object. Our function EmbedBrowserObject is where we obtain a browser object and embed it into a particular window. We need do this only once, so we call EmbedBrowserObject right when we create the window (and pass the window handle to EmbedBrowserObject).

First, since we need a separate IOleInPlaceFrame, IOleClientSite, IOleInPlaceSite and IDocHostUIHandler object for each window (that hosts the browser control), EmbedBrowserObject allocates these 4 objects (ie, structs). Actually, since we've placed all 4 of these structs inside of our own, larger struct (ie, that we defined as a _IOleClientSiteEx struct), one call to GlobalAlloc a _IOleClientSiteEx struct does it all. After we allocate those 4 COM objects, we have to initialize them (ie, stuff a pointer to each VTable in its respective lpVtbl member). Furthermore, we'll save a pointer to this allocated struct in our window's USERDATA field. That way, our window procedure (and other functions) can easily retrieve our COM objects from the window handle.

Now, we obtain one of Microsoft's IWebBrowser2 objects (ie, the main object of Internet Explorer) with a call to the operating system function CoCreateInstance, passing the GUID for the IWebBrowser2 object (defined as the symbol IID_IWebBrowser2 in Microsoft's include files). We also pass the GUID of the DLL in which Microsoft's browser control resides (defined as the symbol CLSID_WebBrowser).

If all goes well, CoCreateInstance will return a pointer to a newly created IWebBrowser2 object. The pointer is stored in our variable webBrowser2.

Next, we need to get the IWebBrowser2 object's IOleObject sub-object. We get a sub-object by calling the parent object's QueryInterface function. So we call IWebBrowser2's QueryInterface to get a pointer to its IOleObject sub-object (that we store in our variable browserObject). This sub-object is the one we mostly use to embed IE in our own window, and control the display of web pages. The IOleObject sub-object is not yet embedded. It is merely created. For the remainder of this article, I'll refer to this IOleObject sub-object as simply the browser object.

Next, we need to call the browser object's SetClientSite to give it a pointer to our own IOleClientSite object. The browser object will need to call some of our IOleClientSite functions to get information from us.

We also call its SetHostNames() to pass the browser the name of our application (so it can display that in its own message boxes).

So how do we embed the browser object? We need to call the browser object's DoVerb function to send the browser object a command that tells it to embed itself in our window (OLEIVERB_SHOW). We also pass our window handle to DoVerb. While we're inside of this call to DoVerb, the browser object is going to call some of our IOleClientSite functions. It will have called several of them before DoVerb returns.

Sending a OLEIVERB_SHOW command via DoVerb does not display any web page. (We have another function we can call to do that, after we're finished with EmbedBrowserObject). It merely embeds the browser object in our window so that it is ready to display a web page and have it shown in our window.

At the end of EmbedBrowserObject, we call the IWebBrowser2 object's Release function. We don't need this object any more (and if we did, we could call the IOleObject sub-object's QueryInterface. Remember that a sub-object's QueryInterface can always be used to locate its parent). But we don't Release the sub-object. We still need that in order to call its functions to display web pages, and do other things. We won't release the sub-object until later in UnEmbedBrowserObject, when we're finally done using the browser object.

We can call our function DisplayHTMLPage to display a URL or HTML file on disk. What we do in DisplayHTMLPage is very similiar to what we do in EmbedBrowserObject. We use the browser object's QueryInterface() to grab pointers to other objects associated with it, and use the VTables of those other objects to call their functions in order to display a URL or HTML file on disk. Again, you can consult the MSDN documentation to learn more about the objects we're asking for and their functions we're calling.

Basically, we need to call the IWebBrowser2's Navigate2 function to display a web page, passing the URL of the page we wish to display. Our URL (ie, web address, such as "http://www.microsoft.com" or an HTM filename on disk such as "c:\myfile.htm") must be passed to the IWebBrowser2's Navigate2 function as a BSTR. What's more, our BSTR needs to be stuffed into a VARIANT struct, and that VARIANT struct is then passed to Navigate2.

Navigate2 will fetch the contents of the page (from wherever it resides) and display it in the browser object embedded in our window.

What if we have a buffer (in memory) that already contains the HTML page we wish to display? In this case, we can get the brower object to display it, but this involves a few extra steps.

First, we need to create an empty page, which we can do by calling Navigate2 and passing it a URL of about:blank. This is a special URL that the IE engine recognizes as a blank page.

Next, we get the browser's IHTMLDocument2 object, and call its write function to tell it to write the contents of our buffer to this empty web page. We must format our buffer as a BSTR, and also wrap it in a standard COM struct known as a "safe array". COM provides some functions we can call to allocate a safe array (and also free it when we're done with it).

If the user resizes our window containing the browser object, the object will not automatically resize its display area. We need to specifically call some browser functions if we wish to enlarge/shrink the display area. We call put_Width and put_Height, passing the desired width and height, respectively.

Our function ResizeBrowser accomplishes this. Normally, this is called when we process the WM_SIZE message for our window.

In fact, you can create several browser objects if desired, for example, if you wanted several windows - each hosting its own browser object so that each window could display its own web page. Simple.c creates two windows that each host a browser object. (So we call EmbedBrowserObject once for each window). In one window, we call DisplayHTMLPage to display Microsoft's web page. In another window, we call DisplayHTMLStr to display some HTML string in memory.

Indeed, after we've embedded a browser object, we can call DisplayHTMLPage or DisplayHTMLStr repeatedly to change what is being displayed.

The web browser automatically keeps a "history" of the URLs we have displayed. We can cause the browser to go back to displaying a previously-viewed page by calling the browser's GoBack function. This would be akin to clicking on IE's "Back" button. In fact, there are several actions that correspond to IE buttons, such as Refresh, Forward, Search, etc that we can invoke. Our function DoPageAction serves as a generic interface to several of these browser functions. (Although Simple.c doesn't utilize this, you could add Back, Forward, Refresh, Search, etc, buttons to the example code, and utilize DoPageAction).

When we're finally done with the browser object, we need to Release it to free any resources it used. We do that in UnEmbedBrowserObject. This needs to be done only once, so we do it right when the window is being destroyed. And we need to call OleUninitialize before our program exits.

The Simple directory contains a complete C example with everything in one source file. Study this to familiarize yourself with the technique of using the browser object in your own window. It demonstrates how to display either an HTML file on the web or disk, or an HTML string in memory, and creates 2 windows to do such.

The Browser directory also contains a complete C example. It demonstrates how to add "Back", "Forward", "Home", and "Stop" buttons. It creates a child window (inside of the main window) into which the browser object is embedded.

The Events directory also contains a complete C example. It demonstrates how to implement your own special link to display a web page with links to other HTML strings (in memory). You could use this technique to define other specialized types of "links" that can send messages to your window when the user clicks upon the link.

The DLL directory contains a DLL that has the functions EmbedBrowserObject, UnEmbedBrowserObject, DisplayHTMLPage, DisplayHTMLStr, and DoPageAction in it. The DLL also contains all of the IStorage, IOleInPlaceFrame, IOleClientSite, IOleInPlaceSite, and IDocHostUIHandler VTables and their functions. The DLL also calls OleInitialize and OleUninitialize on your behalf. So to use this DLL, you don't need to put any OLE/COM coding in your C program at all. It's all in the DLL instead. And there is a small example called Example.c that uses the DLL. It's just Simple.c with all the OLE/COM stuff ripped out of it and replaced with calls to use the DLL. The DLL functions have been modified slightly to support both UNICODE or ansi. I use the function IsWindowUnicode to discover whether the application window (hosting the browser object) is UNICODE or not.

The DLL also has a few new functions to support events, which will be discussed below.

Events

An HTML page is typically composed of numerous elements, such as various tags like a FONT tag, links, forms, etc. Each element may have various "actions" or "events" associated with it. For example, a link generates an event when the user moves the mouse pointer over it. It generates another event when the user moves the mouse pointer off of it. And there are other events it may generate.

An application may ask the browser to provide feedback when a particular event occurs with a particular element. In order for us to get feedback about an element, the HTML page must be written to give that element an ID (ie, a string name). For example, let's assume that our page has a FONT element on it. Let's arbitrarily give this FONT element an ID of testfont. Here is how the HTML page's FONT element may look:

<FONT id=testfont color=red> This is some red text. </FONT>

Each event itself has a unique string name. For example, when the mouse is moved over the above FONT element (ie, the mouse pointer is moved over the red text), the event which occurs is a mouseover event. When the mouse is moved off of the FONT element, the event which occurs is a mouseout event.

For every element on the web page, the web browser has an IHTMLElement object for it. To get feedback on an element, we first must get its IHTMLElement object. In the DLL directory's Dll.c is a function called GetWebElement which shows how to get an IHTMLElement object for a particular element. GetWebElement is passed the window containing the browser object, and the ID (name) of the desired element. To get the IHTMLElement, we have to run through several other browser objects. We have to get the browser's IHTMLDocument2, and then get the IHTMLElementCollection (for the desired element) from that, and then get the element's IDispatch, and finally get the element's IHTMLElement object from that IDispatch. Whew!

Once we have an element's IHTMLElement, we can then "attach" to the element to receive feedback on its events. As you've learned from my article about COM events, we need to provide the browser with an IDispatch object we create. The browser will call our IDispatch's Invoke function when an event occurs. We must give our event IDispatch to the browser, which we do by obtaining the browser's IHTMLWindow3 object and calling its attachEvent function, passing our IDispatch.

Then, to tell the browser to call our IDispatch's Invoke whenever a FONT element's "mouseover" event occurs, we call that FONT IHTMLElement's put_onmouseover function, passing a pointer to our IDispatch. (Actually, we need to wrap our IDispatch pointer in a VARIANT). To get feedback on that FONT element's "mouseout" event, we call that FONT IHTMLElement's put_onmouseout function, passing a pointer to our IDispatch.

Different types of elements may have different events, and so some elements, such as a FORM, have additional sub-objects we can get via its IHTMLElement's QueryInterface. For example, if we had a FORM element's IHTMLElement, we could call its QueryInterface to get its IHTMLFormElement. Then, we could call the IHTMLFormElement'sput_onsubmit function to attach to its submit event (ie, when the user submits the FORM data). Consult MSDN docs to determine which web page elements have which sub-objects (ie, which elements generate which events).

Of course, we want to isolate all of the COM stuff in our cwebpage.dll, so what we're going to do is provide a function that will create an IDispatch on behalf of the application. That function is CreateWebEvtHandler. The IDispatch's functions will be inside of cwebpage.dll, so the application will not need to create any of its own COM objects. To abstact this, we'll make the application assign an ID number of its choice to each element it wants feedback upon. For example, the app may decide to assign the ID number 1 to the FONT element. Then when our DLL IDispatch Invoke gets feedback on that FONT element's mouseover event, for example, we will pass a custom message to the app's window. The custom message will include the ID number of the element, and the string name of the event (ie, "mouseover").

In the directory HTMLEvents is an example application, and an example web page. The web page has several elements on it, including a FORM, and a FONT element. The application receives feedback on some events for both elements.

An application can also receive feedback for events associated with the web page itself (such as the user double-clicking on a blank area of the page), or the browser's scroll bars, etc. The example receives feedback on some of those non-element events too.

There are lots more events an app could get feedback upon. Consult MSDN docs, and experiment.

History

Initial release upon December 1, 2002.

Update upon December 6, 2002. Added IDocHostUIHandler interface, and the function DoPageAction. Applied a fix to UnEmbedBrowserObject() and DisplayHTMLStr(). Revamped the comments in the code to be more explicit and clear. Added the Browser.c and Events.c examples.

Update upon May 15, 2006. Added the ability to receive "events" from the web page, such as knowing when the user clicks upon some item on the page, or submits a form, etc. Incorporated both UNICODE and ANSI support into the one DLL.

Update upon August 1, 2006. Rewrote the article to reference my series on COM in plain C, while expounding more upon browser specific matters. Also updated the download link on this page to contain the latest code base.

Comments and Discussions

The link you posted is still relevant and contains some good suggestions. Depending upon how you have implemented this code, you have full control of the iWebBrowser environment and so you can do a lot more than simnply rely upon a cookie to pass data to other windows.

Hi , This example works fine but none of the BHO's that are already registered for I.E and which work fine on I.E. get loaded in this embeded web browser control !!! Can any body help me out out with this ???? I am trying since a week but haven't succeeded yet ....

I'm trying to compile cwebpage.h. The dll compiles fine, but when I include cwebpage in my application project, I get this error:

error C2259: 'IDispatch' : cannot instantiate abstractclass

for this structure:

typedef struct{
IDispatch dispatchObj; // The mandatory IDispatch.
DWORD refCount; // Our reference count.
IHTMLWindow2 * htmlWindow2; // Where we store the IHTMLWindow2 so that our IDispatch's Invoke() can get it.
HWND hwnd; // The window hosting the browser page. Our IDispatch's Invoke() sends messages when an event of interest occurs.
short id; // Any numeric value of your choosing that you wish to associate with this IDispatch.
unsigned short extraSize; // Byte size of any extra fields prepended to this struct.
IUnknown *object; // Some object associated with the web page element this IDispatch is for.
void *userdata; // An extra pointer.
} _IDispatchEx;

I can replace dispatchObj with *dispatchObj and it works, but I know it's meant to stay dispatchObj without any modifications. Why wouldn't it compile in my application, if it does in the DLL? I'm not really good at COM, so bear with me if it's a stupid question.

You're right, I am compiling it in C++, because my project is in C++, and the DLL is in C. I need the header file (cwebpage.h) for my application, too. How should I change this structure so that it compiles in my C++ project?

Hi , I have gone through this excellent piece of code manytimes and have read all the comments. I am still not able to display menubar, addressbar , status bar for this embeded control.. I have also changed the return value of UI_ShowUI() to S_FLASE .. but no results.. I also inserted the following lines in the EmbedBrowserObject()

but the menubar was not visible.Please help me out I am trying since last 2 days.. Also BHO's that are registered for IE are not loaded with this embeded control!! any special piece of code to be written for this ???

I think you will need to embed an Internet Explorer Object and not a Browser Object. If you refer to the MSDN documentation regarding IWebBrowser2 you will find that many things are intentionally non-functional.

I've read all way through the comments, but did not found a hint to this problem:

I was successful in implementing a key message handler, as suggested in the "Google" thread.But you have to set the key focus on the page displayed once after starting the app by e.g. clicking somewhere into the page.

Is there any way to set keyboard focus to any object inside the page programmatically(at start, when the hosting window becomes active, ...)? Just like the search field is activated automatically in IE when you load google.com.

I need some help with displaying a flash application on an embedded HTML control. I was wondering if anyone would know how to embed a flash application onto the the embedded HTML control. Many thanks to anyone who can help me cause I could really use some right now.

You don't need code. (This pseudo-article is bulltshit)Just use Win32 ActiveX Host native control(see on Adv. Win32 api newsgroup news://comp.os.ms-windows.programmer.win32where it has been often published...)

I have been trying to open cwebpage_src.zip, but OfficeScan (at work) and Symantec 360 (at home) are detecting cwebpage.dll as a virus. I think that it is a false-positive, but I don't want to disable antivirus to use this amazing DLL. Any ideas of what part of code is misinterpreted as a virus? Thanks in advance.

I add put_onclick events to some (about 250) elements.My hosting window is not the main window, but a popup.The user will load many pages one after the other from strings and from files.The events are detached on unloading.But when my hosting window is closed, the events are not detached.I do not know, if this could result in memory leaksor in problems when the user opens a new hosting window.

It can happen that I cannot put the events,because the html data or file did not load properly.Sometimes my app hangs somewhere in the web control.Perhaps these problems will be gone without the wait loop(see my other posting).

The html contains a simple table and a css script and is valid.There are no hrefs, so I did not test whether the href paths are found(seems to be a common problem, if Navigate2 blank page is used before).I tested the dll on Vista with IE7.

I add put_onclick events to some (about 250) elements.My hosting window is not the main window, but a popup.The user will load many pages one after the other from strings and from files.The events are detached on unloading.But when my hosting window is closed, the events are not detached.I do not know, if this could result in memory leaksor in problems when the user opens a new hosting window.

Firstly, it might be easier/and quicker to trap the onclick event at document level and then determine which element triggered it.

Secondly, I understand what you are saying regarding the closing of your window. I assume that you have confirmed that the events are not detaching. If so, then what you can do is trap the WM_DESTROY message and unload them yourself just prior to unloading the Hosted Browser.

My only real complaint about this code is that it appears to be slower than Internet Explorer for the same pages - I have not yet had time to investigate why this is the case.

But when my hosting window is closed, the events are not detached.I do not know, if this could result in memory leaksor in problems when the user opens a new hosting window.

I had a quick look at the code and yes, you are correct it does appear there is a memory leak. When the window is closed, Dispatch_Release() is not getting called because there has been no unload event.

What you need to do is store a copy of the iDispatchEx pointer returned when you called CreateWebEvtHandler(). You also need to modify Dispatch_Release() to set that Pointer to NULLPTR if it releases the memory itself at any point. Then all you need to do just before you call UnEmbedBrowserObject() is to add code something like this:

if (myCopyiDispatch != NULLPTR) FreeWebEvtHandler(myCopyiDispatch);

One thing that could be a worry is if the idispatch reference count has not hit zero then the memory will not be released. I would be interested to hear if you hit that scenario, I don't think you will, but if you do then there is a problem elsewhere which needs resolving.

I am pretty sure that covers it, let us know here if you find any other problems.Leslie.

I took my own advice and added the changes I mentioned above. Good thing to because I found another problem with the existing code and I can repeat it every time although I am not too sure why at the moment.

The sequence for me is this:Navigate to about:blankthis should create the Event HandlerNow Navigate to about:blank again (not refresh)This triggers the freeing of the existing event handler (idispatch is released) and sets up the new one.I then exit my application - CRASH !!

What has happened is that when I call Free WebEvtHandler() just before unembedding the browser, in the middle of this function the iDispatchEx is Free'd because the reference count hits Zero. So when it comes to the line of code:

if (iDispatch->obj) .......

It crashes due to a dangling pointer.I am still looking at why this is happening and what the potential memory leaks are in this case. It could be that we have to update the iDispatch_release Function to check and release the Object there, I am not sure at this time.

I even thought of a workaround by loading a blank page before closing the hosting window.But nevertheless these bugs must be removed.

What has happened is that when I call Free WebEvtHandler() just before unembedding the browser, in the middle of this function the iDispatchEx is Free'd because the reference count hits Zero. So when it comes to the line of code:

We have to store a list of all the iDispatchEx pointers returned by calls of CreateWebEvtHandler() in the window data, so that we can free them when the window is destroyed. Or are they also stored in an object list of the document somewhere, so that we can get them from there? So we would not have to manage this list, too?

Not checking for WORS_SUCCESS, the html string is written and the page displayed.But all calls of WORS after this will timeout the same way, regardless of the value of the timeout parameter or whether another page is displayed afterwards.

When you close the webbrowser window then, open it again and call DisplayHTMLPage, this works as expected; DisplayHTMLPage does not call WORS. Calling WORS later works as expected, too.But if you call WORS immediately after DisplayHTMLPage, WORS will timeout, and it will timeout always, as described above.

I have no idea, why get_ReadyState hangs on READYSTATE_LOADING.Perhaps it is not a good idea to remove every message in the PeekMessage loop unhandled. Or a race condition? ...

I think, it is not ok to use WORS for waiting for a load to complete.Implementing and using the DocumentComplete event is complicated.So what can we do else?

Yes, I read every posting in this forum and copied interesting parts to a text file for later use.I added nearly all improvements suggested here to the code.As I am a beginner with COM and the WebControl, I tried to avoid to implement DWebBrowserEvents from the description by dreamstorm20 (4 Dec 04).But after experimenting for a long time with wait msg loops and threads, I think the control will not work reliably without using DocumentComplete. So I must try it...

I read somewhere that Windows XP SP2 has broken WaitOnReadyState and so that might be why you are seeing problems. I implemented DWebBrowserEvents from day one, its very simple code which can be kept in its own c file with only two function calls out of the existing code required to implement it. So yes, I recommend you implement it - but thats not to say there could be other issues.

I have not yet had to implement IPersistMoniker for my needs.

BTW: I was also a beginner with this particular stuff when I started but by reading through the code and in fact restructuring it for my own purposes (IMO the code is very poorly structured and hard to read due to shorthand coding everywhere - not cool).Leslie.

I read somewhere that Windows XP SP2 has broken WaitOnReadyState and so that might be why you are seeing problems. I implemented DWebBrowserEvents from day one, its very simple code which can be kept in its own c file with only two function calls out of the existing code required to implement it. So yes, I recommend you implement it - but thats not to say there could be other issues.

I see. That's the cause of the trouble. So I'll try to implement it eventually.

I have not yet had to implement IPersistMoniker for my needs.

Do you use the write method to display html from string, as in Jeff's code:htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); ?If you do: Are the hrefs to images etc. resolved correctly without inserting the base tag?

BTW: I was also a beginner with this particular stuff when I started but by reading through the code and in fact restructuring it for my own purposes (IMO the code is very poorly structured and hard to read due to shorthand coding everywhere - not cool).

Yes, I have already changed a lot of these code parts, so that I'll understand it even next year

} else { size = (*((short *)strIn) + 1) * sizeof(wchar_t);// We do not get the length of BSTR with *((short *)strIn), but the value of the first character!// The length is stored 4 bytes preceeding strIn, and should be retrieved by SysStringLen, see above.// To get the size in bytes:// Fix: size = SysStringByteLen(strIn) + sizeof(wchar_t); // copying the terminator, too

Hi, did some one have a idea how i can get the embed webbrowser to copy the selected text into the clipboard ( [strg]+[c] )?

Receiving the WM_KEYDOWN message is fine,[b]till you selected a text or just click inside the embed webbrowser[/b]then no WM_KEYDOWN message will come! Just some WM_PARENTNOTIFY, and MOUSEMOVE messages!

I tried to compile the application simple.exe using Visual Studio 2008. The program compiles correctly, but when trying to compile as Cpp (ie, renaming simple.c as simple.cpp) generates several errors:

For VC 6, download the latest platform sdk as has already been stated, then enter the paths to the include and lib files in tools\options\directories, then, the things you haven't done, use the arrows and move the platform sdk directories to the top of the list so VC will include those files instead of the default paths of C:\Program Files\Microsoft Visual Studio\VC98\ATL\INCLUDE etc.

One way of doing this would be to use the app: mechanism that Jeff talks about. You display a page that contains something like this:<a href="app:test">APP Test</a>When the user clicks that link, you detect that special URL in BeforeNavigate2. You then call some desired function and cancel the navigation. You will also need a mechanism to return results to the browser. I use a function I wrote (invoke_script) that will call a Javascript function in the page. The Javascript function can then do anything you want it to on your page.

A second (more flexible) way of doing this kind of thing is to include a simple web server in your app. That server can then call a function when the user clicks on something like this:

<a href="cmd?x+y+z">Cmd Test</a>

The function returns its values as HTML code in response to the GET request generated by the above link.

I prefer the second method becuse it can be used remotely.

Chris.

p.s. If you require code samples for the above techniques, please email me.

If you look about 7 questions below this one with the title Incorrect Page display, you will see that the answer to that problem was to define the GetExternal function. I have just updated the title so that it is clearer.

Anyway, the code appears to work and is only 10 lines or so. I have had no problems myself with it as yet, but there has been no feedback.

Hi Jeff,Your article is really great, and i would kindly ask you ( if you have the time ) to give us a 'roadmap' to do the opposite, embed a graphical object into html, an object that draws (create a window an have Message loop), and can interact with IE, get its location and so on.

Which Objects i should get from IE, and where should i create the window? What functions should i write?

That was a very helpful article. How did you learn those details about using COM in C? Is there a book that would be of further help to me? Is there [gasp] a MS Introduction or Reference for C and COM?