Figure 9    Add/Modify Button Script


 sub add_modify_onclick()
     i=window.external.set_property(property_name.value, property_value.value)
     if i <> 0 then
         MsgBox "Successfully inserted property '" & property_name.value & "'" , 
             0 , ""
         property_name.value=""
         property_value.value=""
     else
         MsgBox "Property '" & property_name.value & "' could not be inserted", 
             0 , ""
     end if
 end sub

Figure 11   Property Report


 <html>
 <head>
     <style>
         body
         {
             margin-left: 50;
             background-repeat: repeat-y;
         }
         th
         {
             font-style: italic;
         }
     </style>
 
     <script language="vbscript">
         sub main_onclick()
             window.navigate "form1.htm"
         end sub
     </script>
 </head>
 
 <body background="background.jpg">
 <div style="font-size: 18pt; font-style: italic">
     Property Management System
 </div>
 <div style="font-size: 14pt">
     Property Report
 </div>
 <br>
 <script language="vbscript">
     pc=window.external.get_propertycount()
     'document.write "<b><i>Number of properties: " & pc & "</i></b><br><br>"
     if pc > 0 then
         document.write "<table border=1 width=100%>"
         document.write "<tr>"
         document.write "<th width=25 align=right>Property</td>"
         document.write "<th width=* align=left>Value</td>"
         document.write "</tr>"
         for i=0 to pc-1 
             window.external.enum_property i, n, v
             document.write "<tr><td width=25>" & n & "</td>"
             document.write "<td width=*>" & v & "</td></tr>"
         next 
         document.write "</table>"
     else
         document.write "No properties to report<br>"
     end if
 </script>
 <br>
 <input type="button" name="main" value="Back to Main">
 </body>
 </html>

Figure 12   Sinking Events


 void html_document::operator=(IHTMLDocument2* i_newdoc)
 {
     // assign a new COM HTML document
     release();
 
     if (i_newdoc)
     {
         i_htmldoc2=i_newdoc;
 
     // connect up document events
        com_util::establish_connection_point(i_htmldoc2,
                                             DIID_HTMLDocumentEvents,
                                             &xHTMLDocumentEvents,
                                             cookie);
 
     // retrive window for the docuement, and assign
     // to new C++ html window object
     IHTMLWindow2* i_window2;
     if (SUCCEEDED(i_newdoc->get_parentWindow(&i_window2)))
     {
         window=create_new_window();
         if (window)
         {
             *window=i_window2;
         }
     }
     }
 }

Figure 13   Ahtml_document.Invoke


 STDMETHODIMP html_document::XHTMLDocumentEvents::Invoke(DISPID dispidMember,
                                                         REFIID riid,
                                                         LCID lcid,
                                                         WORD wFlags,
                                                         DISPPARAMS* pdispparams,
                                                         VARIANT* pvarResult,
                                                         EXCEPINFO* pexcepinfo,
                                                         UINT* puArgErr)
 {
     // route DISPIDs to methods on outer object
 
     switch (dispidMember)
     {
     case DISPID_HTMLDOCUMENTEVENTS_ONHELP:
     {
         return This()->onhelp();
     } 
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONCLICK:
         {
         return This()->onclick();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK:
     {
         return This()->ondblclick();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN:
     {
         return This()->onkeydown();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONKEYUP:
     {
         return This()->onkeyup();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS:
     {
         return This()->onkeypress();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN:
     {
         return This()->onmousedown();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE:
     {
         return This()->onmousemove();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP:
     {
         return This()->onmouseup();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT:
     {
         return This()->onmouseout();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER:
     {
         return This()->onmouseover();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE:
     {
         return This()->onreadystatechange();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONBEFOREUPDATE:
     {
         return This()->onbeforeupdate();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONAFTERUPDATE:
     {
         return This()->onafterupdate();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONROWEXIT:
     {
         return This()->onrowexit();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONROWENTER:
     {
         return This()->onrowenter();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART:
     {
         return This()->ondragstart();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONSELECTSTART:
     {
         return This()->onselectstart();
     }
     break;
 
     case DISPID_HTMLDOCUMENTEVENTS_ONERRORUPDATE:
     {
           return This()->onerrorupdate();
     }
     break;
     }
 
     return E_NOTIMPL;
 }

Figure 14   my_document::onmouseover


 STDMETHODIMP my_document::onmouseout()
 {
     // cause status text change in frame
 
     CMainFrame* mf=dynamic_cast<CMainFrame*>(get_browser_control()->get_frame());
     if (mf)
     {
     mf->set_status_text("");
     }
 
     return S_OK;
 }
 
 STDMETHODIMP my_document::onmouseover()
 {
     html_eventobj eo;
     html_element se;
 
     // get the event object from the associated window
     window->get_event(eo);
 
     // get the source HTML element object
     eo.get_srcElement(se);
 
     string tagname;
     string id;
 
     // get tagname and id
     se.get_tagName(tagname);
     se.get_id(id);
 
     // cause status text change in frame
     CMainFrame* mf=dynamic_cast<CMainFrame*>(get_browser_control()->get_frame());
     if (mf)
     {
     string text=string("OnMouseOver; Tag: '")+tagname+"' ";
     text+=string("Id: '")+id+"'";
     mf->set_status_text(text.c_str());
     }
 
     return S_OK;
 }

Figure 15   Resolving Method Calls in my_application


 //------------------------------------------------------
 
 #include "stdafx.h"
 
 //------------------------------------------------------
 
 #include "my_app.h"
 
 //------------------------------------------------------
 
 #include "com_util.h"
 
 //------------------------------------------------------
 
 using namespace com_utility;
 
 //------------------------------------------------------
 
 my_application::my_application()
 {
     // register strings and dispids
 
     id_map["SET_PROPERTY"]           =id_set_property;
     id_map["GET_PROPERTY"]           =id_get_property;
     id_map["DELETE_PROPERTY"]        =id_delete_property;
     id_map["GET_PROPERTYCOUNT"]      =id_get_propertycount;
     id_map["ENUM_PROPERTY"]          =id_enum_property;
 
     method_map[id_set_property]      =set_property;
     method_map[id_get_property]      =get_property;
     method_map[id_delete_property]   =delete_property;
     method_map[id_get_propertycount] =get_propertycount;
     method_map[id_enum_property]     =enum_property;
 }
 
 my_application::~my_application()
 {
     // clear out any stored properties
     for (string2variant_map::iterator i=propertymap.begin();
          i!=propertymap.end();
          i++)
     {
         VariantClear(&(*i).second);
     }
 }
 
 //-------------------------------------------------------
 
 STDMETHODIMP my_application::GetIDsOfNames(REFIID    riid,
                                            LPOLESTR* names,
                                            UINT      count,
                                            LCID      lcid,
                                            DISPID*   dispids)
 {
     // lookup dispids
 
     HRESULT ret=S_OK;
 
     for (int i=0;i<count;i++)
     {
         string str;
         com_util::olestr2string(names[i],str);
 
         strupr((char*)(str.c_str()));
         
         string2dispid_map::iterator it=id_map.find(str);
         if (it!=id_map.end())
         {
             dispids[i]=(*it).second;
         }
         else
         {
             dispids[i]=DISPID_UNKNOWN;
             ret       =DISP_E_UNKNOWNNAME;
         }
     }
 
     return ret;
 }
 
 STDMETHODIMP my_application::Invoke(DISPID dispidMember,
                                     REFIID riid,
                                     LCID cid,
                                     WORD wFlags,
                                     DISPPARAMS* pdispparams,
                                     VARIANT* pvarResult,
                                     EXCEPINFO* pexcepinfo,
                                     UINT* puArgErr)
 {
     // call the appropriate method for the dispid
 
     HRESULT ret=DISP_E_MEMBERNOTFOUND;
 
     dispid2method_map::iterator it=method_map.find(dispidMember);
     if (it!=method_map.end())
     {
         ret=(this->*(*it).second)(lcid,wFlags,pdispparams,pvarResult,
                                   pexcepinfo,puArgErr);
     }
 
     return ret;
 }
 
 //---------------------------------------------------------
 
 HRESULT my_application::set_property(LCID lcid,
                                      WORD wFlags,
                                      DISPPARAMS* pdispparams,
                                      VARIANT* pvarResult,
                                      EXCEPINFO* pexcepinfo,
                                      UINT* puArgErr)
 {
     if (wFlags & DISPATCH_METHOD)
     {
         if (pdispparams->cArgs!=2)
         {
             return DISP_E_BADPARAMCOUNT;
         }
 
     // force to string
     VARIANT varg;
     VariantInit(&varg);
     HRESULT hr=VariantChangeType(&varg,
                                  &pdispparams->rgvarg[1],
                                  0,
                                  VT_BSTR);
         if (FAILED(hr))
         {
         *puArgErr=1;
         return hr;
         }
 
         string str;
         com_util::bstr2string(varg.bstrVal,str);
 
     if (!str.length())
     {
         if (!pexcepinfo)
         {
            return E_INVALIDARG;
         }
 
         // raise exception
 
         memset(pexcepinfo,0,sizeof(EXCEPINFO));
         pexcepinfo->wCode            =1000;
         pexcepinfo->bstrSource       =SysAllocString(L"WebBrowser Demo");
         pexcepinfo->bstrDescription  
             =SysAllocString(L"Index can not be empty string");
 
         return DISP_E_EXCEPTION;
    }
 
         VARIANT copy;
         VariantInit(&copy);
         VariantCopy(&copy,&pdispparams->rgvarg[0]);
 
     // store in the map
         propertymap[str]=copy;
 
     if (pvarResult)
     {
         V_VT(pvarResult)  =VT_BOOL;
         V_BOOL(pvarResult)=TRUE;
     }
 
     return S_OK;
     }
 
     return DISP_E_MEMBERNOTFOUND;
 }