# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1116785243 0 # Node ID 420c6c94abc7a815864b8ba2dfc332877f91c722 # Parent 67dfd0cea50dac74b95825945358a0a50f00daba Added dw_html_* functionality for embedding HTML pages in Dynamic Windows applications. diff -r 67dfd0cea50d -r 420c6c94abc7 dw.h --- a/dw.h Fri May 20 11:28:16 2005 +0000 +++ b/dw.h Sun May 22 18:07:23 2005 +0000 @@ -447,6 +447,7 @@ #define ClassName "dynamicwindows" #define SplitbarClassName "dwsplitbar" #define ObjectClassName "dwobjectclass" +#define BrowserClassName "dwbrowserclass" #define DefaultFont NULL typedef struct _color { @@ -882,6 +883,14 @@ #define DW_ERROR_NO_MEM 4 #define DW_ERROR_INTERRUPT 5 +/* Embedded HTML actions */ +#define DW_HTML_GOBACK 0 +#define DW_HTML_GOFORWARD 1 +#define DW_HTML_GOHOME 2 +#define DW_HTML_SEARCH 3 +#define DW_HTML_RELOAD 4 +#define DW_HTML_STOP 5 + #ifndef API #define API #endif @@ -1121,5 +1130,9 @@ HSHM API dw_named_memory_new(void **dest, int size, char *name); HSHM API dw_named_memory_get(void **dest, int size, char *name); int API dw_named_memory_free(HSHM handle, void *ptr); +void API dw_html_action(HWND hwnd, int action); +int API dw_html_raw(HWND hwnd, char *string); +int API dw_html_url(HWND hwnd, char *url); +HWND API dw_html_new(unsigned long id); #endif diff -r 67dfd0cea50d -r 420c6c94abc7 dwtest.c --- a/dwtest.c Fri May 20 11:28:16 2005 +0000 +++ b/dwtest.c Sun May 22 18:07:23 2005 +0000 @@ -65,6 +65,7 @@ notebookbox4, notebookbox5, notebookbox6, + html, notebook, vscrollbar, hscrollbar, @@ -865,6 +866,7 @@ ULONG notebookpage4; ULONG notebookpage5; ULONG notebookpage6; + ULONG notebookpage7; dw_init(TRUE, argc, argv); @@ -913,6 +915,12 @@ dw_notebook_page_set_text( notebook, notebookpage6, "mdi"); mdi_add(); + html = dw_html_new(0); + notebookpage7 = dw_notebook_page_new( notebook, 1, FALSE ); + dw_notebook_pack( notebook, notebookpage7, html ); + dw_notebook_page_set_text( notebook, notebookpage7, "html"); + dw_html_url(html, "http://dwindows.netlabs.org"); + dw_signal_connect(mainwindow, DW_SIGNAL_DELETE, DW_SIGNAL_FUNC(exit_callback), (void *)mainwindow); timerid = dw_timer_connect(1000, DW_SIGNAL_FUNC(timer_callback), 0); dw_window_set_icon(mainwindow, fileicon); diff -r 67dfd0cea50d -r 420c6c94abc7 dww.def --- a/dww.def Fri May 20 11:28:16 2005 +0000 +++ b/dww.def Sun May 22 18:07:23 2005 +0000 @@ -258,3 +258,9 @@ dw_named_event_post @463 dw_named_event_wait @464 dw_named_event_close @465 + + dw_html_new @470 + dw_html_action @471 + dw_html_raw @472 + dw_html_url @473 + diff -r 67dfd0cea50d -r 420c6c94abc7 makefile.vc --- a/makefile.vc Fri May 20 11:28:16 2005 +0000 +++ b/makefile.vc Sun May 22 18:07:23 2005 +0000 @@ -14,7 +14,7 @@ CFLAGS = -c -G5 -GD -Zp1 -DWIN32 -D__WIN32__ -DMSVC -DBUILD_DLL -I$(SRCDIR)\platform -I$(SRCDIR) CFLAGS_DEBUG = -Zi CFLAGS_COMPILE = -MTd -LIBS = wsock32.lib kernel32.lib user32.lib comctl32.lib gdi32.lib advapi32.lib shell32.lib comdlg32.lib +LIBS = wsock32.lib kernel32.lib user32.lib comctl32.lib gdi32.lib advapi32.lib shell32.lib comdlg32.lib ole32.lib oleaut32.lib RES = LINKFLAGS = -machine:i386 -debug:full DLLLINKFLAGS = -dll @@ -22,7 +22,7 @@ DEFFILE = $(SRCDIR)\dww.def DEFFILE2 = $(SRCDIR)\dwcompatw.def -OBJS = dw.obj +OBJS = dw.obj browser.obj OBJS2 = compat.obj dirent.obj @@ -71,6 +71,9 @@ dw.obj: $(SRCDIR)\win\dw.c $(CC) $(CFLAGS) $(CFLAGS_DEBUG) $(CFLAGS_COMPILE) $(SRCDIR)\win\dw.c +browser.obj: $(SRCDIR)\win\browser.c + $(CC) $(CFLAGS) $(CFLAGS_DEBUG) $(CFLAGS_COMPILE) $(SRCDIR)\win\browser.c + dirent.obj: $(SRCDIR)\win\dirent.c $(CC) $(CFLAGS) $(CFLAGS_DEBUG) $(CFLAGS_COMPILE) $(SRCDIR)\win\dirent.c diff -r 67dfd0cea50d -r 420c6c94abc7 win/browser.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/win/browser.c Sun May 22 18:07:23 2005 +0000 @@ -0,0 +1,1702 @@ +/* Browser.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). + +The example opens a main window with a child window into which we embed the browser object. We also +put "Back", "Forward", and "Stop" buttons in the main window to control the browser navigation. We +size the child window containing the browser so that there is room at the top of the main window for +the buttons. We also disable the pop-up context menu. + +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. + +This requires IE 5.0 (or better) -- due to the IDocHostUIHandler interface, or a browser that supports +the same level of OLE in-place activation. +*/ + + + + + +#include +#include // Defines of stuff like IWebBrowser2. This is an include file with Visual C 6 and above +#include // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above +#include // Defines of stuff like IDocHostUIHandler. This is an include file with Visual C 6 and above +#include // for _ASSERT() +#include "dw.h" + +// 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 return 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 { + IOleInPlaceFrame frame; // The IOleInPlaceFrame must be first! + + /////////////////////////////////////////////////// + // Here you add any extra 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 IDocHostUIHandler functions that the browser may call +HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR* This, REFIID riid, void ** ppvObject); +HRESULT STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR* This); +HRESULT STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR* This); +HRESULT STDMETHODCALLTYPE UI_ShowContextMenu(IDocHostUIHandler FAR* This, DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved); +HRESULT STDMETHODCALLTYPE UI_GetHostInfo(IDocHostUIHandler FAR* This, DOCHOSTUIINFO __RPC_FAR *pInfo); +HRESULT STDMETHODCALLTYPE UI_ShowUI(IDocHostUIHandler FAR* This, DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc); +HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR* This); +HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR* This); +HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR* This, BOOL fEnable); +HRESULT STDMETHODCALLTYPE UI_OnDocWindowActivate(IDocHostUIHandler FAR* This, BOOL fActivate); +HRESULT STDMETHODCALLTYPE UI_OnFrameWindowActivate(IDocHostUIHandler FAR* This, BOOL fActivate); +HRESULT STDMETHODCALLTYPE UI_ResizeBorder(IDocHostUIHandler FAR* This, LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow); +HRESULT STDMETHODCALLTYPE UI_TranslateAccelerator(IDocHostUIHandler FAR* This, LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID); +HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath(IDocHostUIHandler FAR* This, LPOLESTR __RPC_FAR *pchKey, DWORD dw); +HRESULT STDMETHODCALLTYPE UI_GetDropTarget(IDocHostUIHandler FAR* This, IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); +HRESULT STDMETHODCALLTYPE UI_GetExternal(IDocHostUIHandler FAR* This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch); +HRESULT STDMETHODCALLTYPE UI_TranslateUrl(IDocHostUIHandler FAR* This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut); +HRESULT STDMETHODCALLTYPE UI_FilterDataObject(IDocHostUIHandler FAR* This, IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet); + +// Our IDocHostUIHandler VTable. This is the array of pointers to the above functions in our C +// program that the browser may call in order to replace/set certain user interface considerations +// (such as whether to display a pop-up context menu when the user right-clicks on the embedded +// browser object). We must define a particular set of functions that comprise the +// IDocHostUIHandler 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 +// IDocHostUIHandler structure. +IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = {UI_QueryInterface, +UI_AddRef, +UI_Release, +UI_ShowContextMenu, +UI_GetHostInfo, +UI_ShowUI, +UI_HideUI, +UI_UpdateUI, +UI_EnableModeless, +UI_OnDocWindowActivate, +UI_OnFrameWindowActivate, +UI_ResizeBorder, +UI_TranslateAccelerator, +UI_GetOptionKeyPath, +UI_GetDropTarget, +UI_GetExternal, +UI_TranslateUrl, +UI_FilterDataObject}; + +// We'll allocate our IDocHostUIHandler object dynamically with GlobalAlloc() for reasons outlined later. + + + +// Our IOleInPlaceSite functions that the browser may call +HRESULT STDMETHODCALLTYPE InPlace_QueryInterface(IOleInPlaceSite FAR* This, REFIID riid, void ** ppvObject); +HRESULT STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR* This, HWND FAR* lphwnd); +HRESULT STDMETHODCALLTYPE InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode); +HRESULT STDMETHODCALLTYPE InPlace_CanInPlaceActivate(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_OnInPlaceActivate(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_OnUIActivate(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext(IOleInPlaceSite FAR* This, LPOLEINPLACEFRAME FAR* lplpFrame,LPOLEINPLACEUIWINDOW FAR* lplpDoc,LPRECT lprcPosRect,LPRECT lprcClipRect,LPOLEINPLACEFRAMEINFO lpFrameInfo); +HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent); +HRESULT STDMETHODCALLTYPE InPlace_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable); +HRESULT STDMETHODCALLTYPE InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_DiscardUndoState(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_DeactivateAndUndo(IOleInPlaceSite FAR* This); +HRESULT STDMETHODCALLTYPE InPlace_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 = {InPlace_QueryInterface, +InPlace_AddRef, +InPlace_Release, +InPlace_GetWindow, +InPlace_ContextSensitiveHelp, +InPlace_CanInPlaceActivate, +InPlace_OnInPlaceActivate, +InPlace_OnUIActivate, +InPlace_GetWindowContext, +InPlace_Scroll, +InPlace_OnUIDeactivate, +InPlace_OnInPlaceDeactivate, +InPlace_DiscardUndoState, +InPlace_DeactivateAndUndo, +InPlace_OnPosRectChange}; + +// We need to pass our IOleClientSite structure to OleCreate (which in turn gives it to the browser). +// But the browser is also going to ask our IOleClientSite's QueryInterface() to return a pointer to +// our IOleInPlaceSite and/or IDocHostUIHandler structs. So we'll need to have those pointers 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 the IOleClientSite, IOleInPlaceSite, IDocHostUIHandler, and IOleInPlaceFrame structs all inside +// it (so we can easily get a pointer to any one from any of those structs' functions). As long as the +// IOleClientSite struct is the very first thing in this custom struct, it's all ok. We can still pass +// it to OleCreate() and pretend that it's an ordinary IOleClientSite. 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 a unique _IOleClientSiteEx struct for +// each window. So, we're not going to declare this struct globally. We'll allocate it later +// using GlobalAlloc, and then initialize the structs within it. + +typedef struct { + IOleInPlaceSite inplace; // My IOleInPlaceSite object. Must be first with in _IOleInPlaceSiteEx. + + /////////////////////////////////////////////////// + // Here you add any extra variables that you need + // to access in your IOleInPlaceSite functions. + // + // So here is where I added my IOleInPlaceFrame + // struct. If you need extra variables, add them + // at the end. + /////////////////////////////////////////////////// + _IOleInPlaceFrameEx frame; // My IOleInPlaceFrame object. Must be first within my _IOleInPlaceFrameEx +} _IOleInPlaceSiteEx; + +typedef struct { + IDocHostUIHandler ui; // My IDocHostUIHandler object. Must be first. + + /////////////////////////////////////////////////// + // Here you add any extra variables that you need + // to access in your IDocHostUIHandler functions. + /////////////////////////////////////////////////// +} _IDocHostUIHandlerEx; + +typedef struct { + IOleClientSite client; // My IOleClientSite object. Must be first. + _IOleInPlaceSiteEx inplace; // My IOleInPlaceSite object. A convenient place to put it. + _IDocHostUIHandlerEx ui; // My IDocHostUIHandler object. Must be first within my _IDocHostUIHandlerEx. + + /////////////////////////////////////////////////// + // Here you add any extra 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 IDocHostUIHandler functions ////////////////////////////////////// +// The browser object asks us for the pointer to our IDocHostUIHandler object by calling our IOleClientSite's +// QueryInterface (ie, Site_QueryInterface) and specifying a REFIID of IID_IDocHostUIHandler. +// +// NOTE: You need at least IE 4.0. Previous versions do not ask for, nor utilize, our IDocHostUIHandler functions. + +HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR* This, REFIID riid, LPVOID FAR* ppvObj) +{ + // The browser assumes that our IDocHostUIHandler object is associated with our IOleClientSite + // object. So it is possible that the browser may call our IDocHostUIHandler's QueryInterface() + // to ask us to return a pointer to our IOleClientSite, in the same way that the browser calls + // our IOleClientSite's QueryInterface() to ask for a pointer to our IDocHostUIHandler. + // + // Rather than duplicate much of the code in IOleClientSite's QueryInterface, let's just get + // a pointer to our _IOleClientSiteEx object, substitute it as the 'This' arg, and call our + // our IOleClientSite's QueryInterface. Note that since our _IDocHostUIHandlerEx is embedded right + // inside our _IOleClientSiteEx, and comes immediately after the _IOleInPlaceSiteEx, we can employ + // the following trickery to get the pointer to our _IOleClientSiteEx. + return(Site_QueryInterface((IOleClientSite *)((char *)This - sizeof(IOleClientSite) - sizeof(_IOleInPlaceSiteEx)), riid, ppvObj)); +} + +HRESULT STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR* This) +{ + return(1); +} + +HRESULT STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR* This) +{ + return(1); +} + +// Called when the browser object is about to display its context menu. +HRESULT STDMETHODCALLTYPE UI_ShowContextMenu(IDocHostUIHandler FAR* This, DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) +{ + // If desired, we can pop up your own custom context menu here. Of course, + // we would need some way to get our window handle, so what we'd probably + // do is like what we did with our IOleInPlaceFrame object. We'd define and create + // our own IDocHostUIHandlerEx object with an embedded IDocHostUIHandler at the + // start of it. Then we'd add an extra HWND field to store our window handle. + // It could look like this: + // + // typedef struct _IDocHostUIHandlerEx { + // IDocHostUIHandler ui; // The IDocHostUIHandler must be first! + // HWND window; + // } IDocHostUIHandlerEx; + // + // Of course, we'd need a unique IDocHostUIHandlerEx object for each window, so + // EmbedBrowserObject() would have to allocate one of those too. And that's + // what we'd pass to our browser object (and it would in turn pass it to us + // here, instead of 'This' being a IDocHostUIHandler *). + + // We will return S_OK to tell the browser not to display its default context menu, + // or return S_FALSE to let the browser show its default context menu. For this + // example, we wish to disable the browser's default context menu. + return(S_OK); +} + +// Called at initialization of the browser object UI. We can set various features of the browser object here. +HRESULT STDMETHODCALLTYPE UI_GetHostInfo(IDocHostUIHandler FAR* This, DOCHOSTUIINFO __RPC_FAR *pInfo) +{ + pInfo->cbSize = sizeof(DOCHOSTUIINFO); + + // Set some flags. We don't want any 3D border. You can do other things like hide + // the scroll bar (DOCHOSTUIFLAG_SCROLL_NO), display picture display (DOCHOSTUIFLAG_NOPICS), + // disable any script running when the page is loaded (DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE), + // open a site in a new browser window when the user clicks on some link (DOCHOSTUIFLAG_OPENNEWWIN), + // and lots of other things. See the MSDN docs on the DOCHOSTUIINFO struct passed to us. + pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; + + // Set what happens when the user double-clicks on the object. Here we use the default. + pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; + + return(S_OK); +} + +// Called when the browser object shows its UI. This allows us to replace its menus and toolbars by creating our +// own and displaying them here. +HRESULT STDMETHODCALLTYPE UI_ShowUI(IDocHostUIHandler FAR* This, DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) +{ + // We've already got our own UI in place so just return S_OK to tell the browser + // not to display its menus/toolbars. Otherwise we'd return S_FALSE to let it do + // that. + return(S_OK); +} + +// Called when browser object hides its UI. This allows us to hide any menus/toolbars we created in ShowUI. +HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR* This) +{ + return(S_OK); +} + +// Called when the browser object wants to notify us that the command state has changed. We should update any +// controls we have that are dependent upon our embedded object, such as "Back", "Forward", "Stop", or "Home" +// buttons. +HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR* This) +{ + // We update our UI in our window message loop so we don't do anything here. + return(S_OK); +} + +// Called from the browser object's IOleInPlaceActiveObject object's EnableModeless() function. Also +// called when the browser displays a modal dialog box. +HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR* This, BOOL fEnable) +{ + return(S_OK); +} + +// Called from the browser object's IOleInPlaceActiveObject object's OnDocWindowActivate() function. +// This informs off of when the object is getting/losing the focus. +HRESULT STDMETHODCALLTYPE UI_OnDocWindowActivate(IDocHostUIHandler FAR* This, BOOL fActivate) +{ + return(S_OK); +} + +// Called from the browser object's IOleInPlaceActiveObject object's OnFrameWindowActivate() function. +HRESULT STDMETHODCALLTYPE UI_OnFrameWindowActivate(IDocHostUIHandler FAR* This, BOOL fActivate) +{ + return(S_OK); +} + +// Called from the browser object's IOleInPlaceActiveObject object's ResizeBorder() function. +HRESULT STDMETHODCALLTYPE UI_ResizeBorder(IDocHostUIHandler FAR* This, LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) +{ + return(S_OK); +} + +// Called from the browser object's TranslateAccelerator routines to translate key strokes to commands. +HRESULT STDMETHODCALLTYPE UI_TranslateAccelerator(IDocHostUIHandler FAR* This, LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) +{ + // We don't intercept any keystrokes, so we do nothing here. But for example, if we wanted to + // override the TAB key, perhaps do something with it ourselves, and then tell the browser + // not to do anything with this keystroke, we'd do: + // + // if (pMsg && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB) + // { + // // Here we do something as a result of a TAB key press. + // + // // Tell the browser not to do anything with it. + // return(S_FALSE); + // } + // + // // Otherwise, let the browser do something with this message. + // return(S_OK); + + // For our example, we want to make sure that the user can invoke some key to popup the context + // menu, so we'll tell it to ignore all messages. + return(S_FALSE); +} + +// Called by the browser object to find where the host wishes the browser to get its options in the registry. +// We can use this to prevent the browser from using its default settings in the registry, by telling it to use +// some other registry key we've setup with the options we want. +HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath(IDocHostUIHandler FAR* This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) +{ + // Let the browser use its default registry settings. + return(S_FALSE); +} + +// Called by the browser object when it is used as a drop target. We can supply our own IDropTarget object, +// IDropTarget functions, and IDropTarget VTable if we want to determine what happens when someone drags and +// drops some object on our embedded browser object. +HRESULT STDMETHODCALLTYPE UI_GetDropTarget(IDocHostUIHandler FAR* This, IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) +{ + // Return our IDropTarget object associated with this IDocHostUIHandler object. I don't + // know why we don't do this via UI_QueryInterface(), but we don't. + + // NOTE: If we want/need an IDropTarget interface, then we would have had to setup our own + // IDropTarget functions, IDropTarget VTable, and create an IDropTarget object. We'd want to put + // a pointer to the IDropTarget object in our own custom IDocHostUIHandlerEx object (like how + // we may add an HWND field for the use of UI_ShowContextMenu). So when we created our + // IDocHostUIHandlerEx object, maybe we'd add a 'idrop' field to the end of it, and + // store a pointer to our IDropTarget object there. Then we could return this pointer as so: + // + // *pDropTarget = ((IDocHostUIHandlerEx FAR *)This)->idrop; + // return(S_OK); + + // But for our purposes, we don't need an IDropTarget object, so we'll tell whomever is calling + // us that we don't have one. + return(S_FALSE); +} + +// Called by the browser when it wants a pointer to our IDispatch object. This object allows us to expose +// our own automation interface (ie, our own COM objects) to other entities that are running within the +// context of the browser so they can call our functions if they want. An example could be a javascript +// running in the URL we display could call our IDispatch functions. We'd write them so that any args passed +// to them would use the generic datatypes like a BSTR for utmost flexibility. +HRESULT STDMETHODCALLTYPE UI_GetExternal(IDocHostUIHandler FAR* This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) +{ + // Return our IDispatch object associated with this IDocHostUIHandler object. I don't + // know why we don't do this via UI_QueryInterface(), but we don't. + + // NOTE: If we want/need an IDispatch interface, then we would have had to setup our own + // IDispatch functions, IDispatch VTable, and create an IDispatch object. We'd want to put + // a pointer to the IDispatch object in our custom _IDocHostUIHandlerEx object (like how + // we may add an HWND field for the use of UI_ShowContextMenu). So when we defined our + // _IDocHostUIHandlerEx object, maybe we'd add a 'idispatch' field to the end of it, and + // store a pointer to our IDispatch object there. Then we could return this pointer as so: + // + // *ppDispatch = ((_IDocHostUIHandlerEx FAR *)This)->idispatch; + // return(S_OK); + + // But for our purposes, we don't need an IDispatch object, so we'll tell whomever is calling + // us that we don't have one. Note: We must set ppDispatch to 0 if we don't return our own + // IDispatch object. + *ppDispatch = 0; + return(S_FALSE); +} + +// Called by the browser object to give us an opportunity to modify the URL to be loaded. +HRESULT STDMETHODCALLTYPE UI_TranslateUrl(IDocHostUIHandler FAR* This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) +{ + // We don't need to modify the URL. Note: We need to set ppchURLOut to 0 if we don't + // return an OLECHAR (buffer) containing a modified version of pchURLIn. + *ppchURLOut = 0; + return(S_FALSE); +} + +// Called by the browser when it does cut/paste to the clipboard. This allows us to block certain clipboard +// formats or support additional clipboard formats. +HRESULT STDMETHODCALLTYPE UI_FilterDataObject(IDocHostUIHandler FAR* This, IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet) +{ + // Return our IDataObject object associated with this IDocHostUIHandler object. I don't + // know why we don't do this via UI_QueryInterface(), but we don't. + + // NOTE: If we want/need an IDataObject interface, then we would have had to setup our own + // IDataObject functions, IDataObject VTable, and create an IDataObject object. We'd want to put + // a pointer to the IDataObject object in our custom _IDocHostUIHandlerEx object (like how + // we may add an HWND field for the use of UI_ShowContextMenu). So when we defined our + // _IDocHostUIHandlerEx object, maybe we'd add a 'idata' field to the end of it, and + // store a pointer to our IDataObject object there. Then we could return this pointer as so: + // + // *ppDORet = ((_IDocHostUIHandlerEx FAR *)This)->idata; + // return(S_OK); + + // But for our purposes, we don't need an IDataObject object, so we'll tell whomever is calling + // us that we don't have one. Note: We must set ppDORet to 0 if we don't return our own + // IDataObject object. + *ppDORet = 0; + return(S_FALSE); +} + + + + + +////////////////////////////////////// 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. +// +// We give the browser object a pointer to our IStorage object when we call OleCreate(). + +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 functions ///////////////////////////////////// +// We give the browser object a pointer to our IOleClientSite object when we call OleCreate() or DoVerb(). + +/************************* Site_QueryInterface() ************************* + * The browser object calls this when it wants a pointer to one of our + * IOleClientSite, IDocHostUIHandler, or IOleInPlaceSite structures. They + * are all accessible via 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. + else if (!memcmp(riid, &IID_IOleInPlaceSite, sizeof(GUID))) + *ppvObject = &((_IOleClientSiteEx *)This)->inplace; + + // If the browser is asking us to match IID_IDocHostUIHandler, then it wants us to return a pointer to + // our IDocHostUIHandler struct. Then the browser will use the VTable in that struct to call our + // IDocHostUIHandler functions. It will also pass this same pointer to all of our IDocHostUIHandler + // 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 _IDocHostUIHandlerEx + // struct, and tell the browser that it's a IDocHostUIHandler struct. It's ok. The first thing in + // our _IDocHostUIHandlerEx is an embedded IDocHostUIHandler, so the browser doesn't mind. We want the + // browser to continue passing our _IDocHostUIHandlerEx pointer wherever it would normally pass a + // IDocHostUIHandler pointer. My, we're really playing dirty tricks on the browser here. heheh. + else if (!memcmp(riid, &IID_IDocHostUIHandler, sizeof(GUID))) + *ppvObject = &((_IOleClientSiteEx *)This)->ui; + + // 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; +} + + + + + + + + + + + +////////////////////////////////////// My IOleInPlaceSite functions ///////////////////////////////////// +// The browser object asks us for the pointer to our IOleInPlaceSite object by calling our IOleClientSite's +// QueryInterface (ie, Site_QueryInterface) and specifying a REFIID of IID_IOleInPlaceSite. + +HRESULT STDMETHODCALLTYPE InPlace_QueryInterface(IOleInPlaceSite FAR* This, REFIID riid, LPVOID FAR* ppvObj) +{ + // The browser assumes that our IOleInPlaceSite object is associated with our IOleClientSite + // object. So it is possible that the browser may call our IOleInPlaceSite's QueryInterface() + // to ask us to return a pointer to our IOleClientSite, in the same way that the browser calls + // our IOleClientSite's QueryInterface() to ask for a pointer to our IOleInPlaceSite. + // + // Rather than duplicate much of the code in IOleClientSite's QueryInterface, let's just get + // a pointer to our _IOleClientSiteEx object, substitute it as the 'This' arg, and call our + // our IOleClientSite's QueryInterface. Note that since our IOleInPlaceSite is embedded right + // inside our _IOleClientSiteEx, and comes immediately after the IOleClientSite, we can employ + // the following trickery to get the pointer to our _IOleClientSiteEx. + return(Site_QueryInterface((IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); +} + +HRESULT STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR* This) +{ + return(1); +} + +HRESULT STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR* This) +{ + return(1); +} + +HRESULT STDMETHODCALLTYPE InPlace_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 InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode) +{ + NOTIMPLEMENTED; +} + +HRESULT STDMETHODCALLTYPE InPlace_CanInPlaceActivate(IOleInPlaceSite FAR* This) +{ + // Tell the browser we can in place activate + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_OnInPlaceActivate(IOleInPlaceSite FAR* This) +{ + // Tell the browser we did it ok + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_OnUIActivate(IOleInPlaceSite FAR* This) +{ + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_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 *)This)->frame; + + // We have no OLEINPLACEUIWINDOW + *lplpDoc = 0; + + // Fill in some other info for the browser + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*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. + // We do this in InPlace_OnPosRectChange() which is called right when a window is first + // created anyway, so no need to duplicate it here. +// GetClientRect(lpFrameInfo->hwndFrame, lprcPosRect); +// GetClientRect(lpFrameInfo->hwndFrame, lprcClipRect); + + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent) +{ + NOTIMPLEMENTED; +} + +HRESULT STDMETHODCALLTYPE InPlace_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable) +{ + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR* This) +{ + return(S_OK); +} + +HRESULT STDMETHODCALLTYPE InPlace_DiscardUndoState(IOleInPlaceSite FAR* This) +{ + NOTIMPLEMENTED; +} + +HRESULT STDMETHODCALLTYPE InPlace_DeactivateAndUndo(IOleInPlaceSite FAR* This) +{ + NOTIMPLEMENTED; +} + +// Called when the position of the browser object is changed, such as when we call the IWebBrowser2's put_Width(), +// put_Height(), put_Left(), or put_Right(). +HRESULT STDMETHODCALLTYPE InPlace_OnPosRectChange(IOleInPlaceSite FAR* This, LPCRECT lprcPosRect) +{ + IOleObject *browserObject; + IOleInPlaceObject *inplace; + + // We need to get the browser's IOleInPlaceObject object so we can call its SetObjectRects + // function. + browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - sizeof(IOleClientSite))); + if (!browserObject->lpVtbl->QueryInterface(browserObject, &IID_IOleInPlaceObject, (void**)&inplace)) + { + // Give the browser the dimensions of where it can draw. + inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, 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 *)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 0, you may have called this + // for a window that wasn't successfully passed to EmbedBrowserObject(). Bad boy! Or, we + // may have failed the EmbedBrowserObject() call in WM_CREATE, in which case, our window + // may get a WM_DESTROY which could call this a second time (ie, since we may call + // UnEmbedBrowserObject in EmbedBrowserObject). + if ((browserHandle = (IOleObject **)dw_window_get_data(hwnd, "_dw_html"))) + { + // Unembed the browser object, and release its resources. + browserObject = *browserHandle; + browserObject->lpVtbl->Close(browserObject, OLECLOSE_NOSAVE); + browserObject->lpVtbl->Release(browserObject); + + // Zero out the pointer just in case UnEmbedBrowserObject is called again for this window + dw_window_set_data(hwnd, "_dw_html", NULL); + } +} + + + + + + +/******************************* dw_html_action() ************************** + * Implements the functionality of a "Back". "Forward", "Home", "Search", + * "Refresh", or "Stop" button. + * + * hwnd = Handle to the window hosting the browser object. + * action = One of the following: + * 0 = Move back to the previously viewed web page. + * 1 = Move forward to the previously viewed web page. + * 2 = Move to the home page. + * 3 = Search. + * 4 = Refresh the page. + * 5 = Stop the currently loading page. + * + * 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. + */ + +void API dw_html_action(HWND hwnd, int action) +{ + IWebBrowser2 *webBrowser2; + 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 **)dw_window_get_data(hwnd, "_dw_html")); + + // 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. + + // Call the desired function + switch (action) + { + case DW_HTML_GOBACK: + { + // Call the IWebBrowser2 object's GoBack function. + webBrowser2->lpVtbl->GoBack(webBrowser2); + break; + } + + case DW_HTML_GOFORWARD: + { + // Call the IWebBrowser2 object's GoForward function. + webBrowser2->lpVtbl->GoForward(webBrowser2); + break; + } + + case DW_HTML_GOHOME: + { + // Call the IWebBrowser2 object's GoHome function. + webBrowser2->lpVtbl->GoHome(webBrowser2); + break; + } + + case DW_HTML_SEARCH: + { + // Call the IWebBrowser2 object's GoSearch function. + webBrowser2->lpVtbl->GoSearch(webBrowser2); + break; + } + + case DW_HTML_RELOAD: + { + // Call the IWebBrowser2 object's Refresh function. + webBrowser2->lpVtbl->Refresh(webBrowser2); + } + + case DW_HTML_STOP: + { + // Call the IWebBrowser2 object's Stop function. + webBrowser2->lpVtbl->Stop(webBrowser2); + } + } + + // Release the IWebBrowser2 object. + webBrowser2->lpVtbl->Release(webBrowser2); + } +} + + + + + +/******************************* dw_html_raw() **************************** + * 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... + * + *

This is a picture.

+ * + * hwnd = Handle to the window hosting the browser object. + * string = Pointer to nul-terminated string containing the HTML BODY. + * (NOTE: No 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. + */ + +int API dw_html_raw(HWND hwnd, char *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 **)dw_window_get_data(hwnd, "_dw_html")); + + // 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); + + // Close the document. If we don't do this, subsequent calls to DisplayHTMLStr + // would append to the current contents of the page + htmlDoc2->lpVtbl->close(htmlDoc2); + + // 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); +} + + + + + + +/******************************* dw_html_url() **************************** + * 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. + */ + +int API dw_html_url(HWND hwnd, char *url) +{ + 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 **)dw_window_get_data(hwnd, "_dw_html")); + + // 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; + + { + wchar_t *buffer; + DWORD size; + + size = MultiByteToWideChar(CP_ACP, 0, url, -1, 0, 0); + if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) goto badalloc; + MultiByteToWideChar(CP_ACP, 0, url, -1, buffer, size); + myURL.bstrVal = SysAllocString(buffer); + GlobalFree(buffer); + } + 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); +} + + + + + +/******************************* ResizeBrowser() **************************** + * Resizes the browser object for the specified window to the specified + * width and height. + * + * hwnd = Handle to the window hosting the browser object. + * width = Width. + * height = Height. + * + * 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 resize the browser object. + */ + +void _ResizeBrowser(HWND hwnd, DWORD width, DWORD height) +{ + IWebBrowser2 *webBrowser2; + 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 **)dw_window_get_data(hwnd, "_dw_html")); + + // 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. + + // Call are put_Width() and put_Height() to set the new width/height. + webBrowser2->lpVtbl->put_Width(webBrowser2, width); + webBrowser2->lpVtbl->put_Height(webBrowser2, height); + + // 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); + } +} + + + + + +/***************************** 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; + _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 unique IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame + // structs for each window. And we'll put an extra field at the end of those structs to store our extra + // data such as a window handle. So, our functions won't have to touch global data, and can therefore be + // re-entrant and work with multiple objects/windows. + // + // 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. + // + // Furthermore, since the browser will be calling our IOleClientSite's QueryInterface to get a pointer to + // our IOleInPlaceSite and IDocHostUIHandler objects, that means that our IOleClientSite QueryInterface + // must have an easy way to grab those pointers. Probably the easiest thing to do is just embed our + // IOleInPlaceSite and IDocHostUIHandler objects inside of an extended IOleClientSite which we'll call + // a _IOleClientSiteEx. As long as they come after the pointer to the IOleClientSite VTable, then we're + // ok. + // + // Of course, we need to GlobalAlloc the above structs now. We'll just get all 4 with a single call to + // GlobalAlloc, especially since 3 of them are all contained inside of our _IOleClientSiteEx anyway. + // + // So, we're not actually allocating separate IOleClientSite, IOleInPlaceSite, IOleInPlaceFrame and + // IDocHostUIHandler structs. + // + // 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(_IOleClientSiteEx) + sizeof(IOleObject *)))) + return(-1); + + // Initialize our IOleClientSite object with a pointer to our IOleClientSite VTable. + _iOleClientSiteEx = (_IOleClientSiteEx *)(ptr + sizeof(IOleObject *)); + _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; + + // Initialize our IOleInPlaceSite object with a pointer to our IOleInPlaceSite VTable. + _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; + + // Initialize our IOleInPlaceFrame object with a pointer to our IOleInPlaceFrame VTable. + _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; + + // Save our HWND (in the IOleInPlaceFrame object) so our IOleInPlaceFrame functions can retrieve it. + _iOleClientSiteEx->inplace.frame.window = hwnd; + + // Initialize our IDocHostUIHandler object with a pointer to our IDocHostUIHandler VTable. + _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; + + // 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; + dw_window_set_data(hwnd, "_dw_html", (void *)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); +} + + + + + + +/************************** browserWindowProc() ************************* + * Our message handler for our window to host the browser. + */ + +LRESULT CALLBACK _browserWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_SIZE: + { + // Resize the browser object to fit the window + _ResizeBrowser(hwnd, LOWORD(lParam), HIWORD(lParam)); + return(0); + } + + case 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); + + // Success + return(0); + } + + case WM_DESTROY: + { + // Detach the browser object from this window, and free resources. + _UnEmbedBrowserObject(hwnd); + + return(TRUE); + } + } + + return(DefWindowProc(hwnd, uMsg, wParam, lParam)); +} + diff -r 67dfd0cea50d -r 420c6c94abc7 win/dw.c --- a/win/dw.c Fri May 20 11:28:16 2005 +0000 +++ b/win/dw.c Sun May 22 18:07:23 2005 +0000 @@ -2,8 +2,8 @@ * Dynamic Windows: * A GTK like implementation of the Win32 GUI * - * (C) 2000-2004 Brian Smith - * (C) 2003-2004 Mark Hessling + * (C) 2000-2005 Brian Smith + * (C) 2003-2005 Mark Hessling * */ #define _WIN32_IE 0x0500 @@ -74,6 +74,7 @@ HBRUSH _colors[18]; +LRESULT CALLBACK _browserWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void _resize_notebook_page(HWND handle, int pageid); void _handle_splitbar_resize(HWND hwnd, float percent, int type, int x, int y); int _lookup_icon(HWND handle, HICON hicon, int type); @@ -3277,6 +3278,13 @@ RegisterClass(&wc); + /* Register HTML renderer class */ + memset(&wc, 0, sizeof(WNDCLASS)); + wc.lpfnWndProc = (WNDPROC)_browserWindowProc; + wc.lpszClassName = BrowserClassName; + wc.style = CS_HREDRAW|CS_VREDRAW; + RegisterClass(&wc); + /* Create a set of brushes using the default OS/2 and DOS colors */ for(z=0;z<18;z++) _colors[z] = CreateSolidBrush(RGB(_red[z],_green[z],_blue[z])); @@ -3339,6 +3347,7 @@ InitializeSecurityDescriptor(&_dwsd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&_dwsd, TRUE, (PACL) NULL, FALSE); + OleInitialize(NULL); return 0; } @@ -4018,6 +4027,23 @@ } /* + * Create a new HTML browser frame to be packed. + * Parameters: + * id: An ID to be used with dw_window_from_id or 0L. + */ +HWND API dw_html_new(unsigned long id) +{ + return CreateWindow(BrowserClassName, + "", + WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, + 0,0,2000,1000, + DW_HWND_OBJECT, + (HMENU)id, + DWInstance, + NULL); +} + +/* * Create a bitmap object to be packed. * Parameters: * id: An ID to be used with dw_window_from_id or 0L. @@ -8165,6 +8191,7 @@ */ void API dw_exit(int exitcode) { + OleUninitialize(); exit(exitcode); }