comparison win/browser.c @ 2017:686b2d049056

Win: Attempt to add event handler to capture DocumentComplete with embedded IE. This code doesm't work yet, but I needed to commit it to switch computers.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Fri, 08 Nov 2019 07:20:17 +0000
parents f696215e6d4e
children 663d79f28e46
comparison
equal deleted inserted replaced
2016:f696215e6d4e 2017:686b2d049056
25 #include <windows.h> 25 #include <windows.h>
26 #include <exdisp.h> // Defines of stuff like IWebBrowser2. This is an include file with Visual C 6 and above 26 #include <exdisp.h> // Defines of stuff like IWebBrowser2. This is an include file with Visual C 6 and above
27 #include <mshtml.h> // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above 27 #include <mshtml.h> // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above
28 #include <mshtmhst.h> // Defines of stuff like IDocHostUIHandler. This is an include file with Visual C 6 and above 28 #include <mshtmhst.h> // Defines of stuff like IDocHostUIHandler. This is an include file with Visual C 6 and above
29 #include <crtdbg.h> // for _ASSERT() 29 #include <crtdbg.h> // for _ASSERT()
30 #include <initguid.h>
30 #include "dw.h" 31 #include "dw.h"
31 32
32 // This is used by DisplayHTMLStr(). It can be global because we never change it. 33 // This is used by DisplayHTMLStr(). It can be global because we never change it.
33 static const SAFEARRAYBOUND ArrayBound = {1, 0}; 34 static const SAFEARRAYBOUND ArrayBound = {1, 0};
34 35
1073 { 1074 {
1074 NOTIMPLEMENTED; 1075 NOTIMPLEMENTED;
1075 } 1076 }
1076 1077
1077 1078
1078 1079 // The DWEventHandler object must be created by an application using IWebBRowser2,
1079 1080 // and given to IWebBrowser2's IConnectPoint object (via
1080 1081 // IConnectPoint's Advise function).
1081 1082 // {4115B8E2-1823-4bbc-B10D-3D33AAA12ACF}
1083
1084 // DWEventHandler VTable's GUID
1085 // {d2d531c4-83d7-48d7-b733-840245ea2b34}
1086 DEFINE_GUID(DIID_DWEventHandler, 0xd2d531c4, 0x83d7, 0x48d7, 0xb7, 0x33, 0x84, 0x2, 0x45, 0xea, 0x2b, 0x34);
1087
1088 // DWEventHandler's VTable
1089 #undef INTERFACE
1090 #define INTERFACE DWEventHandler
1091 DECLARE_INTERFACE_ (INTERFACE, IUnknown)
1092 {
1093 // IUnknown functions
1094 STDMETHOD (QueryInterface) (THIS_ REFIID, void **) PURE;
1095 STDMETHOD_ (ULONG, AddRef) (THIS) PURE;
1096 STDMETHOD_ (ULONG, Release) (THIS) PURE;
1097 // Extra functions
1098 STDMETHOD_ (void, DocumentComplete) (THIS_ IDispatch*, VARIANT*) PURE;
1099 };
1100
1101 // IWebBrowser2 supports us giving it only 1 DWEventHandler object, so for simplicity, we
1102 // can just declare it as a global. That way, we don't need to allocate it, free it,
1103 // nor maintain a reference count for it. Here's our 1 DWEventHandler object.
1104 static DWEventHandler MyDWEventHandler;
1105
1106
1107 static HRESULT STDMETHODCALLTYPE DWEventHandler_QueryInterface(DWEventHandler *this, REFIID vTableGuid, void **ppv)
1108 {
1109 // This is an DWEventHandler object, so we must recognize DWEventHandler VTable's GUID,
1110 // Our DWEventHandler can also masquerade as an IUnknown
1111 if (!IsEqualIID(vTableGuid, &IID_IUnknown) && !IsEqualIID(vTableGuid, &DIID_DWEventHandler))
1112 {
1113 *ppv = 0;
1114 return(E_NOINTERFACE);
1115 }
1116
1117 *ppv = this;
1118
1119 // Normally, we'd call our AddRef function here. But since our DWEventHandler isn't
1120 // allocated, we don't need to bother with reference counting
1121 return(NOERROR);
1122 }
1123
1124 static ULONG STDMETHODCALLTYPE DWEventHandler_AddRef(DWEventHandler *this)
1125 {
1126 // Our one and only DWEventHandler isn't allocated. Instead, it is statically
1127 // declared above (MyDWEventHandler). So we'll just return a 1
1128 return(1);
1129 }
1130
1131 static ULONG STDMETHODCALLTYPE DWEventHandler_Release(DWEventHandler *this)
1132 {
1133 return(1);
1134 }
1135
1136 // This is the extra function for DWEventHandler.
1137
1138 static void STDMETHODCALLTYPE DWEventHandler_DocumentComplete(DWEventHandler *this, IDispatch* pDisp, VARIANT* URL)
1139 {
1140 // Do a compare of the two elements. We happen to know that
1141 // we'll be passing an array of DWORD values to Sort()
1142 dw_debug("DocumentComplete() called!\n");
1143 }
1144
1145
1146 // Our DWEventHandler VTable. We need only one of these, so we can
1147 // declare it static
1148 static const DWEventHandlerVtbl DWEventHandler_Vtbl = {
1149 DWEventHandler_QueryInterface,
1150 DWEventHandler_AddRef,
1151 DWEventHandler_Release,
1152 DWEventHandler_DocumentComplete
1153 };
1082 1154
1083 1155
1084 /*************************** UnEmbedBrowserObject() ************************ 1156 /*************************** UnEmbedBrowserObject() ************************
1085 * Called to detach the browser object from our host window, and free its 1157 * Called to detach the browser object from our host window, and free its
1086 * resources, right before we destroy our window. 1158 * resources, right before we destroy our window.
1530 IOleObject *browserObject; 1602 IOleObject *browserObject;
1531 IWebBrowser2 *webBrowser2; 1603 IWebBrowser2 *webBrowser2;
1532 RECT rect; 1604 RECT rect;
1533 char *ptr; 1605 char *ptr;
1534 _IOleClientSiteEx *_iOleClientSiteEx; 1606 _IOleClientSiteEx *_iOleClientSiteEx;
1607
1608 MyDWEventHandler.lpVtbl = (DWEventHandlerVtbl *)&DWEventHandler_Vtbl;
1535 1609
1536 // Our IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame functions need to get our window handle. We 1610 // Our IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame functions need to get our window handle. We
1537 // could store that in some global. But then, that would mean that our functions would work with only that 1611 // could store that in some global. But then, that would mean that our functions would work with only that
1538 // one window. If we want to create multiple windows, each hosting its own browser object (to display its 1612 // one window. If we want to create multiple windows, each hosting its own browser object (to display its
1539 // own web page), then we need to create unique IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame 1613 // own web page), then we need to create unique IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame
1610 // USERDATA field. That way, if we need multiple windows each hosting its own browser object, we can 1684 // USERDATA field. That way, if we need multiple windows each hosting its own browser object, we can
1611 // call EmbedBrowserObject() for each one, and easily associate the appropriate browser object with 1685 // call EmbedBrowserObject() for each one, and easily associate the appropriate browser object with
1612 // its matching window and its own objects containing per-window data. 1686 // its matching window and its own objects containing per-window data.
1613 *((IOleObject **)ptr) = browserObject; 1687 *((IOleObject **)ptr) = browserObject;
1614 dw_window_set_data(hwnd, "_dw_html", (void *)ptr); 1688 dw_window_set_data(hwnd, "_dw_html", (void *)ptr);
1615 1689
1616 // We can now call the browser object's SetHostNames function. SetHostNames lets the browser object know our 1690 // We can now call the browser object's SetHostNames function. SetHostNames lets the browser object know our
1617 // application's name and the name of the document in which we're embedding the browser. (Since we have no 1691 // application's name and the name of the document in which we're embedding the browser. (Since we have no
1618 // document name, we'll pass a 0 for the latter). When the browser object is opened for editing, it displays 1692 // document name, we'll pass a 0 for the latter). When the browser object is opened for editing, it displays
1619 // these names in its titlebar. 1693 // these names in its titlebar.
1620 // 1694 //
1658 // object, so we can call some of the functions in the former's table. For example, one IWebBrowser2 function 1732 // object, so we can call some of the functions in the former's table. For example, one IWebBrowser2 function
1659 // we intend to call below will be Navigate2(). So we call the browser object's QueryInterface to get our 1733 // we intend to call below will be Navigate2(). So we call the browser object's QueryInterface to get our
1660 // pointer to the IWebBrowser2 object. 1734 // pointer to the IWebBrowser2 object.
1661 !browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2)) 1735 !browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
1662 { 1736 {
1737 IConnectionPointContainer *container;
1738 IConnectionPoint *point;
1739 HRESULT hr;
1740
1663 // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is 1741 // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
1664 // webBrowser2->lpVtbl. 1742 // webBrowser2->lpVtbl.
1665 1743
1666 // Let's call several functions in the IWebBrowser2 object to position the browser display area 1744 // Let's call several functions in the IWebBrowser2 object to position the browser display area
1667 // in our window. The functions we call are put_Left(), put_Top(), put_Width(), and put_Height(). 1745 // in our window. The functions we call are put_Left(), put_Top(), put_Width(), and put_Height().
1670 webBrowser2->lpVtbl->put_Left(webBrowser2, 0); 1748 webBrowser2->lpVtbl->put_Left(webBrowser2, 0);
1671 webBrowser2->lpVtbl->put_Top(webBrowser2, 0); 1749 webBrowser2->lpVtbl->put_Top(webBrowser2, 0);
1672 webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); 1750 webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right);
1673 webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); 1751 webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom);
1674 1752
1753 // Get IWebBrowser2' IConnectionPointContainer sub-object. We do this by calling
1754 // IWebBrowser2' QueryInterface, and pass it the standard GUID for an
1755 // IConnectionPointContainer VTable
1756 if ((hr = webBrowser2->lpVtbl->QueryInterface(webBrowser2, &IID_IConnectionPointContainer, &container)))
1757 dw_debug("QueryInterface error: Can't get IConnectionPointContainer object");
1758 else
1759 {
1760 // Get IWebBrowser2' IConnectionPoint sub-object for specifically giving
1761 // IWebBRowser2 our DWEventHandler. We do this by calling IConnectionPointContainer's
1762 // FindConnectionPoint, and pass it DWEventHandler VTable's GUID
1763 hr = container->lpVtbl->FindConnectionPoint(container, &DIID_DWEventHandler, &point);
1764
1765 // We don't need the IConnectionPointContainer, now that we got the one IConnectionPoint
1766 // we want (ie, the one we use to give IWebBrowser2 our DWEventHandler)
1767 container->lpVtbl->Release(container);
1768
1769 if (hr)
1770 dw_debug("FindConnectionPoint error: Can't get IConnectionPoint object");
1771 else
1772 {
1773 DWORD cookie;
1774
1775 // Now call the IConnectionPoint's Advise function, giving it some object
1776 // whose QueryInterface it will call to get our DWEventHandler object. Let's
1777 // just give it our DWEventHandler object. So Advise will call our DWEventHandler's
1778 // QueryInterface to tell us to give it a pointer to our DWEventHandler. Dumb?
1779 // You bet. All of this convoluted stuff is a combination of poor pre-planning
1780 // by Microsoft programmers when they designed this stuff, as well as the
1781 // colossal blunder of designing COM to accomodate the limitations of early,
1782 // primitive editions of Visual Basic.
1783 //
1784 // Advise() gives us back a "cookie" value that we'll need to later pass to
1785 // Unadvise() (when we want to tell IWebBrowser2 to stop using our DWEventHandler)
1786 if ((hr = point->lpVtbl->Advise(point, (IUnknown *)&MyDWEventHandler, &cookie)))
1787 dw_debug("Advise error: Can't set our DWEventHandler object");
1788 else
1789 dw_window_set_data(hwnd, "_dw_html_cookie", DW_INT_TO_POINTER(cookie));
1790 }
1791 }
1792
1675 // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it 1793 // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it
1676 // right now, so we can release our hold on it). Note that we'll still maintain our hold on the 1794 // right now, so we can release our hold on it). Note that we'll still maintain our hold on the
1677 // browser object until we're done with that object. 1795 // browser object until we're done with that object.
1678 webBrowser2->lpVtbl->Release(webBrowser2); 1796 webBrowser2->lpVtbl->Release(webBrowser2);
1679 1797