/* CWebPage.c
 
This is a Win32 C application (ie, no MFC, WTL, nor even any C++ -- just plain C) that demonstrates
how to embed a browser "control" (actually, an OLE object) in your own window (in order to display a
web page, or an HTML file on disk).

This is very loosely based upon a C++ example written by Chris Becke. I used that to learn the minimum
of what I needed to know about hosting the browser object. Then I wrote this example from the ground up
in C.
*/



#include <windows.h>
#include <exdisp.h>  // Defines of stuff like IWebBrowser2. This is an include file with Visual C 6 and above
#include <mshtml.h>  // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above
#include <crtdbg.h>  // for _ASSERT()




// A running count of how many windows we have open that contain a browser object
unsigned char WindowCount = 0;

// The class name of our Window to host the browser. It can be anything of your choosing.
static const TCHAR ClassName[] = "Browser Example";

// This is used by DisplayHTMLStr(). It can be global because we never change it.
static const SAFEARRAYBOUND ArrayBound = {1, 0};



// Our IStorage functions that the browser may call
HRESULT STDMETHODCALLTYPE Storage_QueryInterface(IStorage FAR* This, REFIID riid, LPVOID FAR* ppvObj);
HRESULT STDMETHODCALLTYPE Storage_AddRef(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_Release(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_CreateStream(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm);
HRESULT STDMETHODCALLTYPE Storage_OpenStream(IStorage FAR* This, const WCHAR * pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
HRESULT STDMETHODCALLTYPE Storage_CreateStorage(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg);
HRESULT STDMETHODCALLTYPE Storage_OpenStorage(IStorage FAR* This, const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
HRESULT STDMETHODCALLTYPE Storage_CopyTo(IStorage FAR* This, DWORD ciidExclude, IID const *rgiidExclude, SNB snbExclude,IStorage *pstgDest);
HRESULT STDMETHODCALLTYPE Storage_MoveElementTo(IStorage FAR* This, const OLECHAR *pwcsName,IStorage * pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags);
HRESULT STDMETHODCALLTYPE Storage_Commit(IStorage FAR* This, DWORD grfCommitFlags);
HRESULT STDMETHODCALLTYPE Storage_Revert(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_EnumElements(IStorage FAR* This, DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum);
HRESULT STDMETHODCALLTYPE Storage_DestroyElement(IStorage FAR* This, const OLECHAR *pwcsName);
HRESULT STDMETHODCALLTYPE Storage_RenameElement(IStorage FAR* This, const WCHAR *pwcsOldName, const WCHAR *pwcsNewName);
HRESULT STDMETHODCALLTYPE Storage_SetElementTimes(IStorage FAR* This, const WCHAR *pwcsName, FILETIME const *pctime, FILETIME const *patime, FILETIME const *pmtime);
HRESULT STDMETHODCALLTYPE Storage_SetClass(IStorage FAR* This, REFCLSID clsid);
HRESULT STDMETHODCALLTYPE Storage_SetStateBits(IStorage FAR* This, DWORD grfStateBits, DWORD grfMask);
HRESULT STDMETHODCALLTYPE Storage_Stat(IStorage FAR* This, STATSTG * pstatstg, DWORD grfStatFlag);

// Our IStorage VTable. This is the array of pointers to the above functions in our C
// program that someone may call in order to store some data to disk. We must define a
// particular set of functions that comprise the IStorage set of functions (see above),
// and then stuff pointers to those functions in their respective 'slots' in this table.
// We want the browser to use this VTable with our IStorage structure (object).
IStorageVtbl MyIStorageTable = {Storage_QueryInterface,
Storage_AddRef,
Storage_Release,
Storage_CreateStream,
Storage_OpenStream,
Storage_CreateStorage,
Storage_OpenStorage,
Storage_CopyTo,
Storage_MoveElementTo,
Storage_Commit,
Storage_Revert,
Storage_EnumElements,
Storage_DestroyElement,
Storage_RenameElement,
Storage_SetElementTimes,
Storage_SetClass,
Storage_SetStateBits,
Storage_Stat};

// Our IStorage structure. NOTE: All it contains is a pointer to our IStorageVtbl, so we can easily initialize it
// here instead of doing that programmably.
IStorage   MyIStorage = { &MyIStorageTable };



// Our IOleInPlaceFrame functions that the browser may call
HRESULT STDMETHODCALLTYPE Frame_QueryInterface(IOleInPlaceFrame FAR* This, REFIID riid, LPVOID FAR* ppvObj);
HRESULT STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR* This);
HRESULT STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR* This);
HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR* This, HWND FAR* lphwnd);
HRESULT STDMETHODCALLTYPE Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR* This, BOOL fEnterMode);
HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR* This, LPRECT lprectBorder);
HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths);
HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths);
HRESULT STDMETHODCALLTYPE Frame_SetActiveObject(IOleInPlaceFrame FAR* This, IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName);
HRESULT STDMETHODCALLTYPE Frame_InsertMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR* This, HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared);
HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR* This, LPCOLESTR pszStatusText);
HRESULT STDMETHODCALLTYPE Frame_EnableModeless(IOleInPlaceFrame FAR* This, BOOL fEnable);
HRESULT STDMETHODCALLTYPE Frame_TranslateAccelerator(IOleInPlaceFrame FAR* This, LPMSG lpmsg, WORD wID);

// Our IOleInPlaceFrame VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleInPlaceFrame set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleInPlaceFrame structure.
IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = {Frame_QueryInterface,
Frame_AddRef,
Frame_Release,
Frame_GetWindow,
Frame_ContextSensitiveHelp,
Frame_GetBorder,
Frame_RequestBorderSpace,
Frame_SetBorderSpace,
Frame_SetActiveObject,
Frame_InsertMenus,
Frame_SetMenu,
Frame_RemoveMenus,
Frame_SetStatusText,
Frame_EnableModeless,
Frame_TranslateAccelerator};

// We need to pass an IOleInPlaceFrame struct to the browser object. And one of our IOleInPlaceFrame
// functions (Frame_GetWindow) is going to need to access our window handle. So let's create our own
// struct that starts off with an IOleInPlaceFrame struct (and that's important -- the IOleInPlaceFrame
// struct *must* be first), and then has an extra data field where we can store our own window's HWND.
//
// And because we may want to create multiple windows, each hosting its own browser object (to
// display its own web page), then we need to create a IOleInPlaceFrame struct for each window. So,
// we're not going to declare our IOleInPlaceFrame struct globally. We'll allocate it later using
// GlobalAlloc, and then stuff the appropriate HWND in it then, and also stuff a pointer to
// MyIOleInPlaceFrameTable in it. But let's just define it here.
typedef struct _IOleInPlaceFrameEx {
 IOleInPlaceFrame frame;  // The IOleInPlaceFrame must be first!

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleInPlaceFrame functions.
 // You don't want those functions to access global
 // variables, because then you couldn't use more
 // than one browser object. (ie, You couldn't have
 // multiple windows, each with its own embedded
 // browser object to display a different web page).
 //
 // So here is where I added my extra HWND that my
 // IOleInPlaceFrame function Frame_GetWindow() needs
 // to access.
 ///////////////////////////////////////////////////
 HWND    window;
} IOleInPlaceFrameEx;




// Our IOleClientSite functions that the browser may call
HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR* This, REFIID riid, void ** ppvObject);
HRESULT STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_Release(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR* This, DWORD dwAssign, DWORD dwWhichMoniker, IMoniker ** ppmk);
HRESULT STDMETHODCALLTYPE Site_GetContainer(IOleClientSite FAR* This, LPOLECONTAINER FAR* ppContainer);
HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR* This, BOOL fShow);
HRESULT STDMETHODCALLTYPE Site_RequestNewObjectLayout(IOleClientSite FAR* This);

// Our IOleClientSite VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleClientSite set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleClientSite structure.
IOleClientSiteVtbl MyIOleClientSiteTable = {Site_QueryInterface,
Site_AddRef,
Site_Release,
Site_SaveObject,
Site_GetMoniker,
Site_GetContainer,
Site_ShowObject,
Site_OnShowWindow,
Site_RequestNewObjectLayout};




// Our IOleInPlaceSite functions that the browser may call
HRESULT STDMETHODCALLTYPE Site_GetWindow(IOleInPlaceSite FAR* This, HWND FAR* lphwnd);
HRESULT STDMETHODCALLTYPE Site_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode);
HRESULT STDMETHODCALLTYPE Site_CanInPlaceActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnInPlaceActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnUIActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_GetWindowContext(IOleInPlaceSite FAR* This, LPOLEINPLACEFRAME FAR* lplpFrame,LPOLEINPLACEUIWINDOW FAR* lplpDoc,LPRECT lprcPosRect,LPRECT lprcClipRect,LPOLEINPLACEFRAMEINFO lpFrameInfo);
HRESULT STDMETHODCALLTYPE Site_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent);
HRESULT STDMETHODCALLTYPE Site_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable);
HRESULT STDMETHODCALLTYPE Site_OnInPlaceDeactivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_DiscardUndoState(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_DeactivateAndUndo(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnPosRectChange(IOleInPlaceSite FAR* This, LPCRECT lprcPosRect);

// Our IOleInPlaceSite VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleInPlaceSite set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleInPlaceSite structure.
IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable =  {Site_QueryInterface, // This gives a compiler warning because we're using
               // the same function as the MyIOleClientSiteTable uses.
               // And the first arg to that Site_QueryInterface() is
               // a pointer to a IOleClientSite. What we really should
               // have here is a separate function with its first arg
               // as a pointer to a IOleInPlaceSite (even though what
               // will really get passed to it is a _IOleClientSiteEx *).
               // But it's easier to use one function for QueryInterface()
               // in order to support "inheritance". ie, Sometimes, the
               // browser may call your IOleClientSite's QueryInterface
               // in order to get a pointer to your IOleInPlaceSite, or
               // vice versa. This is because these two structs will be
               // passed to the browser inside of a single struct. And
               // that makes them "interconnected" as far as
               // QueryInterface is concerned.
Site_AddRef,    // Ditto as above.
Site_Release,    // Ditto as above.
Site_GetWindow,
Site_ContextSensitiveHelp,
Site_CanInPlaceActivate,
Site_OnInPlaceActivate,
Site_OnUIActivate,
Site_GetWindowContext,
Site_Scroll,
Site_OnUIDeactivate,
Site_OnInPlaceDeactivate,
Site_DiscardUndoState,
Site_DeactivateAndUndo,
Site_OnPosRectChange};

// The structure we need to pass to the browser object must contain an IOleClientSite structure. The
// IOleClientSite struct *must* be first. Our IOleClientSite's QueryInterface() may also be asked to
// return a pointer to our IOleInPlaceSite struct. So we'll need to have that object handy. Plus,
// some of our IOleClientSite and IOleInPlaceSite functions will need to have the HWND to our window,
// and also a pointer to our IOleInPlaceFrame struct. So let's create a single struct that has both
// the IOleClientSite and IOleInPlaceSite structs inside it, and also has an extra data field to
// store a pointer to our IOleInPlaceFrame struct. (The HWND is stored in the IOleInPlaceFrame
// struct, so we can get at it there). As long as the IOleClientSite struct is the first thing, it's
// all ok. We'll call this new struct a _IOleClientSiteEx.
//
// And because we may want to create multiple windows, each hosting its own browser object (to
// display its own web page), then we need to create IOleClientSite and IOleInPlaceSite structs for
// each window. (ie, Each window needs its own _IOleClientSiteEx struct). So, we're not going to
// declare this struct globally. We'll allocate it later using GlobalAlloc, and then store the
// appropriate IOleInPlaceFrame struct pointer then.

typedef struct __IOleInPlaceSiteEx {
 IOleInPlaceSite  inplace;  // My IOleInPlaceSite object. Must be first.

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleInPlaceSite functions.
 // You don't want those functions to access global
 // variables, because then you couldn't use more
 // than one browser object. (ie, You couldn't have
 // multiple windows, each with its own embedded
 // browser object to display a different web page.
 //
 // So here is where I added my extra pointer to my
 // IOleInPlaceFrame struct.
 ///////////////////////////////////////////////////
 IOleInPlaceFrameEx *frame;
} _IOleInPlaceSiteEx;

typedef struct __IOleClientSiteEx {
 IOleClientSite  client;   // My IOleClientSite object. Must be first.
 _IOleInPlaceSiteEx inplace;  // My IOleInPlaceSite object.

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleClientSite functions.
 ///////////////////////////////////////////////////

} _IOleClientSiteEx;



// This is a simple C example. There are lots more things you can control about the browser object, but
// we don't do it in this example. _Many_ of the functions we provide below for the browser to call, will
// never actually be called by the browser in our example. Why? Because we don't do certain things
// with the browser that would require it to call those functions (even though we need to provide
// at least some stub for all of the functions).
//
// So, for these "dummy functions" that we don't expect the browser to call, we'll just stick in some
// assembly code that causes a debugger breakpoint and tells the browser object that we don't support
// the functionality. That way, if you try to do more things with the browser object, and it starts
// calling these "dummy functions", you'll know which ones you should add more meaningful code to.
#define NOTIMPLEMENTED _ASSERT(0); return(E_NOTIMPL)



////////////////////////////////////// My IStorage functions  /////////////////////////////////////////
// NOTE: The browser object doesn't use the IStorage functions, so most of these are us just returning
// E_NOTIMPL so that anyone who *does* call these functions knows nothing is being done here.

HRESULT STDMETHODCALLTYPE Storage_QueryInterface(IStorage FAR* This, REFIID riid, LPVOID FAR* ppvObj)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_AddRef(IStorage FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Storage_Release(IStorage FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Storage_CreateStream(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_OpenStream(IStorage FAR* This, const WCHAR * pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_CreateStorage(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_OpenStorage(IStorage FAR* This, const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_CopyTo(IStorage FAR* This, DWORD ciidExclude, IID const *rgiidExclude, SNB snbExclude,IStorage *pstgDest)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_MoveElementTo(IStorage FAR* This, const OLECHAR *pwcsName,IStorage * pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Commit(IStorage FAR* This, DWORD grfCommitFlags)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Revert(IStorage FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_EnumElements(IStorage FAR* This, DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_DestroyElement(IStorage FAR* This, const OLECHAR *pwcsName)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_RenameElement(IStorage FAR* This, const WCHAR *pwcsOldName, const WCHAR *pwcsNewName)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_SetElementTimes(IStorage FAR* This, const WCHAR *pwcsName, FILETIME const *pctime, FILETIME const *patime, FILETIME const *pmtime)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_SetClass(IStorage FAR* This, REFCLSID clsid)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Storage_SetStateBits(IStorage FAR* This, DWORD grfStateBits, DWORD grfMask)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Stat(IStorage FAR* This, STATSTG * pstatstg, DWORD grfStatFlag)
{
 NOTIMPLEMENTED;
}





///////////////////////////////// My IOleClientSite/IOleInPlaceSite functions  /////////////////////////////////

/************************* Site_QueryInterface() *************************
 * The browser object calls this when it wants a pointer to one of our
 * IOleClientSite or IOleInPlaceSite structures. They are both in the
 * _IOleClientSiteEx struct we allocated in EmbedBrowserObject() and
 * passed to DoVerb() and OleCreate().
 *
 * This =  A pointer to whatever _IOleClientSiteEx struct we passed to
 *    OleCreate() or DoVerb().
 * riid =  A GUID struct that the browser passes us to clue us as to
 *    which type of struct (object) it would like a pointer
 *    returned for.
 * ppvObject = Where the browser wants us to return a pointer to the
 *    appropriate struct. (ie, It passes us a handle to fill in).
 *
 * RETURNS: S_OK if we return the struct, or E_NOINTERFACE if we don't have
 * the requested struct.
 */

HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR* This, REFIID riid, void ** ppvObject)
{
 // It just so happens that the first arg passed to us is our _IOleClientSiteEx struct we allocated
 // and passed to DoVerb() and OleCreate(). Nevermind that 'This' is declared is an IOleClientSite *.
 // Remember that in EmbedBrowserObject(), we allocated our own _IOleClientSiteEx struct, and lied
 // to OleCreate() and DoVerb() -- passing our _IOleClientSiteEx struct and saying it was an
 // IOleClientSite struct. It's ok. An _IOleClientSiteEx starts with an embedded IOleClientSite, so
 // the browser didn't mind. So that's what the browser object is passing us now. The browser doesn't
 // know that it's really an _IOleClientSiteEx struct. But we do. So we can recast it and use it as
 // so here.

 // If the browser is asking us to match IID_IOleClientSite, then it wants us to return a pointer to
 // our IOleClientSite struct. Then the browser will use the VTable in that struct to call our
 // IOleClientSite functions. It will also pass this same pointer to all of our IOleClientSite
 // functions.
 //
 // Actually, we're going to lie to the browser again. We're going to return our own _IOleClientSiteEx
 // struct, and tell the browser that it's a IOleClientSite struct. It's ok. The first thing in our
 // _IOleClientSiteEx is an embedded IOleClientSite, so the browser doesn't mind. We want the browser
 // to continue passing our _IOleClientSiteEx pointer wherever it would normally pass a IOleClientSite
 // pointer.
 //
 // The IUnknown interface uses the same VTable as the first object in our _IOleClientSiteEx
 // struct (which happens to be an IOleClientSite). So if the browser is asking us to match
 // IID_IUnknown, then we'll also return a pointer to our _IOleClientSiteEx.

 if (!memcmp(riid, &IID_IUnknown, sizeof(GUID)) || !memcmp(riid, &IID_IOleClientSite, sizeof(GUID)))
  *ppvObject = &((_IOleClientSiteEx *)This)->client;

 // If the browser is asking us to match IID_IOleInPlaceSite, then it wants us to return a pointer to
 // our IOleInPlaceSite struct. Then the browser will use the VTable in that struct to call our
 // IOleInPlaceSite functions.  It will also pass this same pointer to all of our IOleInPlaceSite
 // functions (except for Site_QueryInterface, Site_AddRef, and Site_Release. Those will always get
 // the pointer to our _IOleClientSiteEx.
 //
 // Actually, we're going to lie to the browser. We're going to return our own IOleInPlaceSiteEx
 // struct, and tell the browser that it's a IOleInPlaceSite struct. It's ok. The first thing in
 // our IOleInPlaceSiteEx is an embedded IOleInPlaceSite, so the browser doesn't mind. We want the
 // browser to continue passing our IOleInPlaceSiteEx pointer wherever it would normally pass a
 // IOleInPlaceSite pointer. My, we're really playing dirty tricks on the browser here. heheh.
 else if (!memcmp(riid, &IID_IOleInPlaceSite, sizeof(GUID)))
  *ppvObject = &((_IOleClientSiteEx *)This)->inplace;

 // For other types of objects the browser wants, just report that we don't have any such objects.
 // NOTE: If you want to add additional functionality to your browser hosting, you may need to
 // provide some more objects here. You'll have to investigate what the browser is asking for
 // (ie, what REFIID it is passing).
 else
 {
  *ppvObject = 0;
  return(E_NOINTERFACE);
 }

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Site_Release(IOleClientSite FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR* This, DWORD dwAssign, DWORD dwWhichMoniker, IMoniker ** ppmk)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetContainer(IOleClientSite FAR* This, LPOLECONTAINER FAR* ppContainer)
{
 // Tell the browser that we are a simple object and don't support a container
 *ppContainer = 0;

 return(E_NOINTERFACE);
}

HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR* This)
{
 return(NOERROR);
}

HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR* This, BOOL fShow)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_RequestNewObjectLayout(IOleClientSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetWindow(IOleInPlaceSite FAR* This, HWND FAR* lphwnd)
{
 // Return the HWND of the window that contains this browser object. We stored that
 // HWND in our _IOleInPlaceSiteEx struct. Nevermind that the function declaration for
 // Site_GetWindow says that 'This' is an IOleInPlaceSite *. Remember that in
 // EmbedBrowserObject(), we allocated our own _IOleInPlaceSiteEx struct which
 // contained an embedded IOleInPlaceSite struct within it. And when the browser
 // called Site_QueryInterface() to get a pointer to our IOleInPlaceSite object, we
 // returned a pointer to our _IOleClientSiteEx. The browser doesn't know this. But
 // we do. That's what we're really being passed, so we can recast it and use it as
 // so here.
 *lphwnd = ((_IOleInPlaceSiteEx FAR*)This)->frame->window;

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_CanInPlaceActivate(IOleInPlaceSite FAR* This)
{
 // Tell the browser we can in place activate
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnInPlaceActivate(IOleInPlaceSite FAR* This)
{
 // Tell the browser we did it ok
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnUIActivate(IOleInPlaceSite FAR* This)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_GetWindowContext(IOleInPlaceSite FAR* This, LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
 // Give the browser the pointer to our IOleInPlaceFrame struct. We stored that pointer
 // in our _IOleInPlaceSiteEx struct. Nevermind that the function declaration for
 // Site_GetWindowContext says that 'This' is an IOleInPlaceSite *. Remember that in
 // EmbedBrowserObject(), we allocated our own _IOleInPlaceSiteEx struct which
 // contained an embedded IOleInPlaceSite struct within it. And when the browser
 // called Site_QueryInterface() to get a pointer to our IOleInPlaceSite object, we
 // returned a pointer to our _IOleClientSiteEx. The browser doesn't know this. But
 // we do. That's what we're really being passed, so we can recast it and use it as
 // so here.
 //
 // Actually, we're giving the browser a pointer to our own IOleInPlaceSiteEx struct,
 // but telling the browser that it's a IOleInPlaceSite struct. No problem. Our
 // IOleInPlaceSiteEx starts with an embedded IOleInPlaceSite, so the browser is
 // cool with it. And we want the browser to pass a pointer to this IOleInPlaceSiteEx
 // wherever it would pass a IOleInPlaceSite struct to our IOleInPlaceSite functions.
 *lplpFrame = (LPOLEINPLACEFRAME)((_IOleInPlaceSiteEx FAR*)This)->frame;

 // We have no OLEINPLACEUIWINDOW
 *lplpDoc = 0;

 // Fill in some other info for the browser
 lpFrameInfo->fMDIApp = FALSE;
 lpFrameInfo->hwndFrame = ((IOleInPlaceFrameEx FAR*)*lplpFrame)->window;
 lpFrameInfo->haccel = 0;
 lpFrameInfo->cAccelEntries = 0;

 // Give the browser the dimensions of where it can draw. We give it our entire window to fill
 GetClientRect(lpFrameInfo->hwndFrame, lprcPosRect);
 GetClientRect(lpFrameInfo->hwndFrame, lprcClipRect);

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnInPlaceDeactivate(IOleInPlaceSite FAR* This)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_DiscardUndoState(IOleInPlaceSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_DeactivateAndUndo(IOleInPlaceSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_OnPosRectChange(IOleInPlaceSite FAR* This, LPCRECT lprcPosRect)
{
 return(S_OK);
}




////////////////////////////////////// My IOleInPlaceFrame functions  /////////////////////////////////////////

HRESULT STDMETHODCALLTYPE Frame_QueryInterface(IOleInPlaceFrame FAR* This, REFIID riid, LPVOID FAR* ppvObj)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR* This, HWND FAR* lphwnd)
{
 // Give the browser the HWND to our window that contains the browser object. We
 // stored that HWND in our IOleInPlaceFrame struct. Nevermind that the function
 // declaration for Frame_GetWindow says that 'This' is an IOleInPlaceFrame *. Remember
 // that in EmbedBrowserObject(), we allocated our own IOleInPlaceFrameEx struct which
 // contained an embedded IOleInPlaceFrame struct within it. And then we lied when
 // Site_GetWindowContext() returned that IOleInPlaceFrameEx. So that's what the
 // browser is passing us. It doesn't know that. But we do. So we can recast it and
 // use it as so here.
 *lphwnd = ((IOleInPlaceFrameEx FAR*)This)->window;
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR* This, BOOL fEnterMode)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR* This, LPRECT lprectBorder)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetActiveObject(IOleInPlaceFrame FAR* This, IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_InsertMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR* This, HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR* This, LPCOLESTR pszStatusText)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_EnableModeless(IOleInPlaceFrame FAR* This, BOOL fEnable)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_TranslateAccelerator(IOleInPlaceFrame FAR* This, LPMSG lpmsg, WORD wID)
{
 NOTIMPLEMENTED;
}





/*************************** UnEmbedBrowserObject() ************************
 * Called to detach the browser object from our host window, and free its
 * resources, right before we destroy our window.
 *
 * hwnd =  Handle to the window hosting the browser object.
 *
 * NOTE: The pointer to the browser object must have been stored in the
 * window's USERDATA field. In other words, don't call UnEmbedBrowserObject().
 * with a HWND that wasn't successfully passed to EmbedBrowserObject().
 */

void UnEmbedBrowserObject(HWND hwnd)
{
 IOleObject **browserHandle;
 IOleObject *browserObject;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 if ((browserHandle = (IOleObject **)GetWindowLong(hwnd, GWL_USERDATA)))
 {
  // Unembed the browser object, and release its resources.
  browserObject = *browserHandle;
  browserObject->lpVtbl->Close(browserObject, OLECLOSE_NOSAVE);
  browserObject->lpVtbl->Release(browserObject);

  GlobalFree(browserHandle);

  return;
 }

 // You must have called this for a window that wasn't successfully passed to EmbedBrowserObject().
 // Bad boy!
 _ASSERT(0);
}




/******************************* DisplayHTMLStr() ****************************
 * Takes a string containing some HTML BODY, and displays it in the specified
 * window. For example, perhaps you want to display the HTML text of...
 *
 * <P>This is a picture.<P><IMG src="mypic.jpg">
 *
 * hwnd =  Handle to the window hosting the browser object.
 * string =  Pointer to nul-terminated string containing the HTML BODY.
 *    (NOTE: No <BODY></BODY> tags are required in the string).
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: EmbedBrowserObject() must have been successfully called once with the
 * specified window, prior to calling this function. You need call
 * EmbedBrowserObject() once only, and then you can make multiple calls to
 * this function to display numerous pages in the specified window.
 */

long DisplayHTMLStr(HWND hwnd, LPCTSTR string)
{
 IWebBrowser2 *webBrowser2;
 LPDISPATCH  lpDispatch;
 IHTMLDocument2 *htmlDoc2;
 IOleObject  *browserObject;
 SAFEARRAY  *sfArray;
 VARIANT   myURL;
 VARIANT   *pVar;
 BSTR   bstr;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 browserObject = *((IOleObject **)GetWindowLong(hwnd, GWL_USERDATA));

 // Assume an error.
 bstr = 0;

 // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
 // object, so we can call some of the functions in the former's table.
 if (!browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
 {
  // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
  // webBrowser2->lpVtbl.

  // Before we can get_Document(), we actually need to have some HTML page loaded in the browser. So,
  // let's load an empty HTML page. Then, once we have that empty page, we can get_Document() and
  // write() to stuff our HTML string into it.
  VariantInit(&myURL);
  myURL.vt = VT_BSTR;
  myURL.bstrVal = SysAllocString(L"about:blank");

  // Call the Navigate2() function to actually display the page.
  webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0);

  // Free any resources (including the BSTR).
  VariantClear(&myURL);

  // Call the IWebBrowser2 object's get_Document so we can get its DISPATCH object. I don't know why you
  // don't get the DISPATCH object via the browser object's QueryInterface(), but you don't.
  if (!webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch))
  {
   // We want to get a pointer to the IHTMLDocument2 object embedded within the DISPATCH
   // object, so we can call some of the functions in the former's table.
   if (!lpDispatch->lpVtbl->QueryInterface(lpDispatch, &IID_IHTMLDocument2, (void**)&htmlDoc2))
   {
    // Ok, now the pointer to our IHTMLDocument2 object is in 'htmlDoc2', and so its VTable is
    // htmlDoc2->lpVtbl.

    // Our HTML must be in the form of a BSTR. And it must be passed to write() in an
    // array of "VARIENT" structs. So let's create all that.
    if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, (SAFEARRAYBOUND *)&ArrayBound)))
    {
     if (!SafeArrayAccessData(sfArray, (void**)&pVar))
     {
      pVar->vt = VT_BSTR;
#ifndef UNICODE
      {
      wchar_t  *buffer;
      DWORD  size;

      size = MultiByteToWideChar(CP_ACP, 0, string, -1, 0, 0);
      if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) goto bad;
      MultiByteToWideChar(CP_ACP, 0, string, -1, buffer, size);
      bstr = SysAllocString(buffer);
      GlobalFree(buffer);
      }
#else
      bstr = SysAllocString(string);
#endif
      // Store our BSTR pointer in the VARIENT.
      if ((pVar->bstrVal = bstr))
      {
       // Pass the VARIENT with its BSTR to write() in order to shove our desired HTML string
       // into the body of that empty page we created above.
       htmlDoc2->lpVtbl->write(htmlDoc2, sfArray);

       // Normally, we'd need to free our BSTR, but SafeArrayDestroy() does it for us
//       SysFreeString(bstr);
      }
     }

     // Free the array. This also frees the VARIENT that SafeArrayAccessData created for us,
     // and even frees the BSTR we allocated with SysAllocString
     SafeArrayDestroy(sfArray);
    }

    // Release the IHTMLDocument2 object.
bad:   htmlDoc2->lpVtbl->Release(htmlDoc2);
   }

   // Release the DISPATCH object.
   lpDispatch->lpVtbl->Release(lpDispatch);
  }

  // Release the IWebBrowser2 object.
  webBrowser2->lpVtbl->Release(webBrowser2);
 }

 // No error?
 if (bstr) return(0);

 // An error
 return(-1);
}




/******************************* DisplayHTMLPage() ****************************
 * Displays a URL, or HTML file on disk.
 *
 * hwnd =  Handle to the window hosting the browser object.
 * webPageName = Pointer to nul-terminated name of the URL/file.
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: EmbedBrowserObject() must have been successfully called once with the
 * specified window, prior to calling this function. You need call
 * EmbedBrowserObject() once only, and then you can make multiple calls to
 * this function to display numerous pages in the specified window.
 */

long DisplayHTMLPage(HWND hwnd, LPTSTR webPageName)
{
 IWebBrowser2 *webBrowser2;
 VARIANT   myURL;
 IOleObject  *browserObject;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 browserObject = *((IOleObject **)GetWindowLong(hwnd, GWL_USERDATA));

 // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
 // object, so we can call some of the functions in the former's table.
 if (!browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
 {
  // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
  // webBrowser2->lpVtbl.

  // 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.
  // A BSTR is like a pascal version of a double-byte character string. In other words, the
  // first unsigned short is a count of how many characters are in the string, and then this
  // is followed by those characters, each expressed as an unsigned short (rather than a
  // char). The string is not nul-terminated. The OS function SysAllocString can allocate and
  // copy a UNICODE C string to a BSTR. Of course, we'll need to free that BSTR after we're done
  // with it. If we're not using UNICODE, we first have to convert to a UNICODE string.
  //
  // What's more, our BSTR needs to be stuffed into a VARIENT struct, and that VARIENT struct is
  // then passed to Navigate2(). Why? The VARIENT struct makes it possible to define generic
  // 'datatypes' that can be used with all languages. Not all languages support things like
  // nul-terminated C strings. So, by using a VARIENT, whose first field tells what sort of
  // data (ie, string, float, etc) is in the VARIENT, COM interfaces can be used by just about
  // any language.
  VariantInit(&myURL);
  myURL.vt = VT_BSTR;

#ifndef UNICODE
  {
  wchar_t  *buffer;
  DWORD  size;

  size = MultiByteToWideChar(CP_ACP, 0, webPageName, -1, 0, 0);
  if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) goto badalloc;
  MultiByteToWideChar(CP_ACP, 0, webPageName, -1, buffer, size);
  myURL.bstrVal = SysAllocString(buffer);
  GlobalFree(buffer);
  }
#else
  myURL.bstrVal = SysAllocString(webPageName);
#endif
  if (!myURL.bstrVal)
  {
badalloc: webBrowser2->lpVtbl->Release(webBrowser2);
   return(-6);
  }

  // Call the Navigate2() function to actually display the page.
  webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0);

  // Free any resources (including the BSTR we allocated above).
  VariantClear(&myURL);

  // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it,
  // so we can release our hold on it). Note that we'll still maintain our hold on the browser
  // object.
  webBrowser2->lpVtbl->Release(webBrowser2);

  // Success
  return(0);
 }

 return(-5);
}



/***************************** EmbedBrowserObject() **************************
 * Puts the browser object inside our host window, and save a pointer to this
 * window's browser object in the window's GWL_USERDATA field.
 *
 * hwnd =  Handle of our window into which we embed the browser object.
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: We tell the browser object to occupy the entire client area of the
 * window.
 *
 * NOTE: No HTML page will be displayed here. We can do that with a subsequent
 * call to either DisplayHTMLPage() or DisplayHTMLStr(). This is merely once-only
 * initialization for using the browser object. In a nutshell, what we do
 * here is get a pointer to the browser object in our window's GWL_USERDATA
 * so we can access that object's functions whenever we want, and we also pass
 * pointers to our IOleClientSite, IOleInPlaceFrame, and IStorage structs so that
 * the browser can call our functions in our struct's VTables.
 */

long EmbedBrowserObject(HWND hwnd)
{
 IOleObject   *browserObject;
 IWebBrowser2  *webBrowser2;
 RECT    rect;
 char    *ptr;
 IOleInPlaceFrameEx *iOleInPlaceFrameEx;
 _IOleClientSiteEx *_iOleClientSiteEx;

 // Our IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame functions need to get our window handle. We
 // could store that in some global. But then, that would mean that our functions would work with only that
 // one window. If we want to create multiple windows, each hosting its own browser object (to display its
 // own web page), then we need to create a unique IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame
 // structs for each window. (Actually, the IOleClientSite and IOleInPlaceSite must be allocated as a
 // single struct, with the IOleClientSite embedded first). And we'll put an extra field at the end of those
 // structs to store our extra data such as a window handle. Remember that a pointer to our IOleClientSite we
 // create here will be passed as the first arg to every one of our IOleClientSite functions. Ditto with the
 // IOleInPlaceFrame object we create here, and the IOleInPlaceFrame functions. So, our functions are able to
 // retrieve the window handle we'll store here, and then, they'll work with all such windows containing a
 // browser control. Of course, that means we need to GlobalAlloc the structs now. We'll just get all 3 with
 // a single call to GlobalAlloc, but you could do them separately if desired.
 //
 // Um, we're not actually allocating a IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame structs. Because
 // we're appending our own fields to them, we're getting an IOleInPlaceFrameEx and an _IOleClientSiteEx (which
 // contains both the IOleClientSite and IOleInPlaceSite. But as far as the browser is concerned, it thinks that
 // we're giving it the plain old IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame.
 //
 // One final thing. We're going to allocate extra room to store the pointer to the browser object.
 if (!(ptr = (char *)GlobalAlloc(GMEM_FIXED, sizeof(IOleInPlaceFrameEx) + sizeof(_IOleClientSiteEx) + sizeof(IOleObject *))))
  return(-1);
 
 // Initialize our IOleInPlaceFrame object with a pointer to our IOleInPlaceFrame VTable.
 iOleInPlaceFrameEx = (IOleInPlaceFrameEx *)(ptr + sizeof(IOleObject *));
 iOleInPlaceFrameEx->frame.lpVtbl = &MyIOleInPlaceFrameTable;

 // Save our HWND (in the IOleInPlaceFrame object) so our IOleInPlaceFrame functions can retrieve it.
 iOleInPlaceFrameEx->window = hwnd;

 // Initialize our IOleClientSite object with a pointer to our IOleClientSite VTable.
 _iOleClientSiteEx = (_IOleClientSiteEx *)(ptr + sizeof(IOleInPlaceFrameEx) + sizeof(IOleObject *));
 _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable;

 // Initialize our IOleInPlaceSite object with a pointer to our IOleInPlaceSite VTable.
 _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable;

 // Save a pointer to our IOleInPlaceFrameEx object so that our IOleInPlaceSite functions can retrieve it.
 _iOleClientSiteEx->inplace.frame = iOleInPlaceFrameEx;

 // Get a pointer to the browser object and lock it down (so it doesn't "disappear" while we're using
 // it in this program). We do this by calling the OS function OleCreate().
 //
 // NOTE: We need this pointer to interact with and control the browser. With normal WIN32 controls such as a
 // Static, Edit, Combobox, etc, you obtain an HWND and send messages to it with SendMessage(). Not so with
 // the browser object. You need to get a pointer to its "base structure" (as returned by OleCreate()). This
 // structure contains an array of pointers to functions you can call within the browser object. Actually, the
 // base structure contains a 'lpVtbl' field that is a pointer to that array. We'll call the array a 'VTable'.
 //
 // For example, the browser object happens to have a SetHostNames() function we want to call. So, after we
 // retrieve the pointer to the browser object (in a local we'll name 'browserObject'), then we can call that
 // function, and pass it args, as so:
 //
 // browserObject->lpVtbl->SetHostNames(browserObject, SomeString, SomeString);
 //
 // There's our pointer to the browser object in 'browserObject'. And there's the pointer to the browser object's
 // VTable in 'browserObject->lpVtbl'. And the pointer to the SetHostNames function happens to be stored in an
 // field named 'SetHostNames' within the VTable. So we are actually indirectly calling SetHostNames by using
 // a pointer to it. That's how you use a VTable.
 //
 // NOTE: We pass our _IOleClientSiteEx struct and lie -- saying that it's a IOleClientSite. It's ok. A
 // _IOleClientSiteEx struct starts with an embedded IOleClientSite. So the browser won't care, and we want
 // this extended struct passed to our IOleClientSite functions.

 if (!OleCreate(&CLSID_WebBrowser, &IID_IOleObject, OLERENDER_DRAW, 0, (IOleClientSite *)_iOleClientSiteEx, &MyIStorage, (void**)&browserObject))
 {
  // Ok, we now have the pointer to the browser object in 'browserObject'. Let's save this in the
  // memory block we allocated above, and then save the pointer to that whole thing in our window's
  // USERDATA field. That way, if we need multiple windows each hosting its own browser object, we can
  // call EmbedBrowserObject() for each one, and easily associate the appropriate browser object with
  // its matching window and its own objects containing per-window data.
  *((IOleObject **)ptr) = browserObject;
  SetWindowLong(hwnd, GWL_USERDATA, (LONG)ptr);

  // We can now call the browser object's SetHostNames function. SetHostNames lets the browser object know our
  // application's name and the name of the document in which we're embedding the browser. (Since we have no
  // document name, we'll pass a 0 for the latter). When the browser object is opened for editing, it displays
  // these names in its titlebar.
  //
  // We are passing 3 args to SetHostNames. You'll note that the first arg to SetHostNames is the base
  // address of our browser control. This is something that you always have to remember when working in C
  // (as opposed to C++). When calling a VTable function, the first arg to that function must always be the
  // structure which contains the VTable. (In this case, that's the browser control itself). Why? That's
  // because that function is always assumed to be written in C++. And the first argument to any C++ function
  // must be its 'this' pointer (ie, the base address of its class, which in this case is our browser object
  // pointer). In C++, you don't have to pass this first arg, because the C++ compiler is smart enough to
  // produce an executable that always adds this first arg. In fact, the C++ compiler is smart enough to
  // know to fetch the function pointer from the VTable, so you don't even need to reference that. In other
  // words, the C++ equivalent code would be:
  //
  // browserObject->SetHostNames(L"My Host Name", 0);
  //
  // So, when you're trying to convert C++ code to C, always remember to add this first arg whenever you're
  // dealing with a VTable (ie, the field is usually named 'lpVtbl') in the standard objects, and also add
  // the reference to the VTable itself.
  //
  // Oh yeah, the L is because we need UNICODE strings. And BTW, the host and document names can be anything
  // you want.

  browserObject->lpVtbl->SetHostNames(browserObject, L"My Host Name", 0);

  GetClientRect(hwnd, &rect);

  // Let browser object know that it is embedded in an OLE container.
  if (!OleSetContainedObject((struct IUnknown *)browserObject, TRUE) &&

   // Set the display area of our browser control the same as our window's size
   // and actually put the browser object into our window.
   !browserObject->lpVtbl->DoVerb(browserObject, OLEIVERB_SHOW, NULL, (IOleClientSite *)_iOleClientSiteEx, -1, hwnd, &rect) &&

   // Ok, now things may seem to get even trickier, One of those function pointers in the browser's VTable is
   // to the QueryInterface() function. What does this function do? It lets us grab the base address of any
   // other object that may be embedded within the browser object. And this other object has its own VTable
   // containing pointers to more functions we can call for that object.
   //
   // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
   // object, so we can call some of the functions in the former's table. For example, one IWebBrowser2 function
   // we intend to call below will be Navigate2(). So we call the browser object's QueryInterface to get our
   // pointer to the IWebBrowser2 object.
   !browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
  {
   // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
   // webBrowser2->lpVtbl.

   // Let's call several functions in the IWebBrowser2 object to position the browser display area
   // in our window. The functions we call are put_Left(), put_Top(), put_Width(), and put_Height().
   // Note that we reference the IWebBrowser2 object's VTable to get pointers to those functions. And
   // also note that the first arg we pass to each is the pointer to the IWebBrowser2 object.
   webBrowser2->lpVtbl->put_Left(webBrowser2, 0);
   webBrowser2->lpVtbl->put_Top(webBrowser2, 0);
   webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right);
   webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom);

   // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it
   // right now, so we can release our hold on it). Note that we'll still maintain our hold on the
   // browser object until we're done with that object.
   webBrowser2->lpVtbl->Release(webBrowser2);

   // Success
   return(0);
  }

  // Something went wrong!
  UnEmbedBrowserObject(hwnd);
  return(-3);
 }

 GlobalFree(ptr);
 return(-2);
}




/****************************** WindowProc() ***************************
 * Our message handler for our window to host the browser.
 */

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 if (uMsg == WM_CREATE)
 {
  // Embed the browser object into our host window. We need do this only
  // once. Note that the browser object will start calling some of our
  // IOleInPlaceFrame and IOleClientSite functions as soon as we start
  // calling browser object functions in EmbedBrowserObject().
  if (EmbedBrowserObject(hwnd)) return(-1);

  // Another window created with an embedded browser object
  ++WindowCount;

  // Success
  return(0);
 }

 if (uMsg == WM_DESTROY)
 {
  // Detach the browser object from this window, and free resources.
  UnEmbedBrowserObject(hwnd);

  // One less window
  --WindowCount;

  // If all the windows are now closed, quit this app
  if (!WindowCount) PostQuitMessage(0);

  return(TRUE);
 }

 // NOTE: If you want to resize the area that the browser object occupies when you
 // resize the window, then handle WM_SIZE and use the IWebBrowser2's put_Width()
 // and put_Height() to give it the new dimensions.

 return(DefWindowProc(hwnd, uMsg, wParam, lParam));
}



/****************************** WinMain() ***************************
 * C program entry point.
 *
 * This creates a window to host the web browser, and displays a web
 * page.
 */

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hInstNULL, LPSTR lpszCmdLine, int nCmdShow)
{
 MSG   msg;

 // Initialize the OLE interface. We do this once-only.
 if (OleInitialize(NULL) == S_OK)
 {
  WNDCLASSEX  wc;

  // Register the class of our window to host the browser. 'WindowProc' is our message handler
  // and 'ClassName' is the class name. You can choose any class name you want.
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hInstance = hInstance;
  wc.lpfnWndProc = WindowProc;
  wc.lpszClassName = &ClassName[0];
  RegisterClassEx(&wc);

  // Create a window. NOTE: We embed the browser object duing our WM_CREATE handling for
  // this window.
/*  if ((msg.hwnd = CreateWindowEx(0, &ClassName[0], "An HTML string", WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
       HWND_DESKTOP, NULL, hInstance, 0)))
  {
   // For this window, display a string in the BODY of a web page.
   DisplayHTMLStr(msg.hwnd, "<H2><CENTER>HTML string test</CENTER></H2><P><FONT COLOR=RED>This is a <U>HTML string</U> in memory.</FONT>");

   // Show the window.
   ShowWindow(msg.hwnd, nCmdShow);
   UpdateWindow(msg.hwnd);
  }
*/
  // Create another window with another browser object embedded in it.
  if ((msg.hwnd = CreateWindowEx(0, &ClassName[0], "Microsoft's web site", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
      HWND_DESKTOP, NULL, hInstance, 0)))
  {
   // For this window, display a URL. This could also be a HTML file on disk such as "c:\\myfile.htm".
   DisplayHTMLPage(msg.hwnd, "http://www.naver.com/");

   // Show the window.
   ShowWindow(msg.hwnd, nCmdShow);
   UpdateWindow(msg.hwnd);
  }

  // Do a message loop until WM_QUIT.
  while (GetMessage(&msg, 0, 0, 0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

  // Free the OLE library.
  OleUninitialize();

  return(0);
 }

 MessageBox(0, "Can't open OLE!", "ERROR", MB_OK);
 return(-1);
}


코드 구루였나.. 거기서 가져온겁니다...
문제는 C언어라.. C++로 돌리면 컴파일 에러가 난다는거...
음... C++로 돌려야 할텐데..

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

댓글을 달아 주세요