Link to home
Start Free TrialLog in
Avatar of AaronReams
AaronReams

asked on

IDeskBand Document complete event?

Does anyone know how to trap the document complete or navigation complete event when using deskbands?

I have developed a toolbar for Internet Explorer but I need to know when the page is done loading.  I assume I need to set up a sink somehow but I have no idea where to look.

Can anyone help?

Many thanks in advance!!

Cheers
Aaron
Avatar of AaronReams
AaronReams

ASKER

Ok.... I think I'm making progress here...  I found another project that uses the following sink for a deskband...

BEGIN_SINK_MAP(CBandObj)
      SINK_ENTRY_EX(0, (__uuidof(DWebBrowserEvents2)), 0x00000103, DocumentComplete)
END_SINK_MAP()

However they have an ATL implementation with no MFC.

I'm using MFC so my CBandObj is derived from CWnd.

I'm getting the following error which I'm really not sure how to fix...

error C2440: 'static_cast' : cannot convert from '_atl_event_classtype *' to 'ATL::_IDispEventLocator<nID,piid> *'

Hmmmmm.... any help is very much appreciated.
As far as i know - and that isn't much - you can't mix MFC and ATL.

For an MFC ActiveX control you have to take

   BEGIN_EVENT_MAP
   END_EVENT_MAP

rather than

   BEGIN_SINK_MAP
   END_SINK_MAP

However, i don't know whether these concepts have same functionality.

Regards, Alex
ASKER CERTIFIED SOLUTION
Avatar of _ys_
_ys_

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for both of your responses!

I tried the BEGIN_EVENT_MAP yesterday and couldn't get that working so ended up doing it kind of like _ys_ suggested.  I was able to get that working by advising connection point returned from the connection container.

I have a question though, I get all the events from explorer now in my Invoke function of my EventHandler object.   I'd like to get the connection point to call each function on its own rather than parsing the event type and doing it myself.

<This is how I'm doing it right now, but I'd rather have the connection point call DocumentComplete directly, is this possible?>

class EventHandler{

...

virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
/* [out] */ VARIANT __RPC_FAR *pVarResult,
/* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
/* [out] */ UINT __RPC_FAR *puArgErr)
{
     TRACE("DISPID = %d\n",dispIdMember);
     switch(dispIdMember)
     {
          case DISPID_DOCUMENTCOMPLETE:
            DocumentComplete(NULL,NULL);
            return S_OK;
          default:
            return E_NOTIMPL;
     }
                  
     return E_NOTIMPL;
}
} // class EventHandler

void CEventHandler::DocumentComplete (IDispatch * pDisp,VARIANT * URL )
{
      printf("Document complete called...");
}

Thanks y'all!!!

Aaron
So long as CEventHandler's only purpose is implementing DWebBrowserEvents2 the API function DispInvoke should do what you want:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/automat/htm/chap5_1e79.asp
Thanks _ys_.  I'm trying to call DispInvoke from my Invoke function.   However, I'm a little confused about the type info parameter.   Here's the code from my Invoke function.

CEventHandler::Invoke(...)
{
     //ITypeInfo* m_ptinfo = NULL;
     //GetTypeInfo(0,0,&m_ptinfo);
   
     return DispInvoke( this, m_ptinfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}


I'm getting an error because m_ptinfo is not a member of the base class DWebBrowserEvents2.   I also tried to retrieve the type info manually by calling GetTypeInfo but that returns NULL.

Am I just totally missing the point here?

Thanks again!
Aaron

>I'm getting an error because m_ptinfo is not a member of the base class DWebBrowserEvents2.
The parameter has to contain the method and paramter info pertaining to the interface DWebBrowserEvents2.

A combination of LoadTypeLib and GetTypeInfoOfGuid should get the info you want.

CEventHandler::Invoke(...)
{
    ITypeLib *pTypeLib = NULL;
    ITypeInfo *pTypeInfo = NULL;
    if (SUCCEEDED (LoadTypeLib ("{fully_qualified_type_library}", &pTypeLib))) // e.g. C:\\MyDeskBand.tlb
    {
        if (FAILED ( pTypeLib->GetTypeInfoOfGuid (IID_IEventHandler, &pTypeInfo)))
            return E_FAIL;
    }
    else
        return E_FAIL;
   
    return DispInvoke( this, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}

This code is based on two assumptions:
- CEventHandler only implements DWebBrowserEvent2, and
- it does this via an empty interface IEventHandler ...

[
    odl,
    uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX),
    helpstring("IEventHandler Interface"),
    dual,
    oleautomation
]
interface IEventHandler : DWebBrowserEvents2
{
// empty
}

The fully qualified type library can be obtained using GetModuleNameA:

char szModuleName[MAX_PATH];
GetModuleFileNameA(hInstance, szModuleName, MAX_PATH);
//Shear off the modules extension -> typically 3 characters
//Assume typelib same name with .tlb extension

If this is not how you've implemented things, then DWebBrosweEvents2 type info can be obtained from
&windir%\system32\shdocvw.dll - LoadTypeLibrary can be used on this as well, although you may need to append the resource identifier 0 [could be 1 - try both]
i.e. C:\\WINDOWS\\SYSTEM32\\shdocvw.dll\\0

The GUID for GetTypeInfoOfGuid would then be the same as that used to obtain the connection point DIID_DWebBrowserEvents2.
Do you need any more on this?
Hi _ys_.

First off, I'd like to extend of huge thanks for following up on this.  You've already been a great help just getting the sink to work at all.

Sorry I didn't do much with this question the past couple weeks.  I was pulled off the project to work on something else.

Basically where I left off was I tried your first suggestion of LoadTypeLib using my tlb file.   That didn't seem to work.  So I did the LoadTypeLib using shdocvw.dll.  That loaded ok, but upon calling DispInvoke I get an HRESULT value of 0x80020003 (Member not found).  

You've already done a lot here so if you're not sure or can't spend anymore time on this let me know and I'll award you the points and figure it out later.  Thanks for the help.

virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke( ... )
{
     ITypeLib *pTypeLib = NULL;
     ITypeInfo *pTypeInfo = NULL;

     // I tried both \\1 and \\0, but only \\1 succeeded
     OLECHAR* oPath = _bstr_t("C:\\WINDOWS\\SYSTEM32\\shdocvw.dll\\1");


     if(SUCCEEDED(LoadTypeLib(oPath,&pTypeLib)))
     {
          if (FAILED ( pTypeLib->GetTypeInfoOfGuid (DIID_DWebBrowserEvents2, &pTypeInfo)))
      return E_FAIL;
         
          HRESULT hr = DispInvoke( this, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);

          if(FAILED(hr))
      TRACE("DispInvoke failed...");   //0x80020003 Member not found
          return hr;
     }
}

Thanks!!!
AAron
Assuming that 'this' does in fact implement the interface DWebBrowserEvents2 my only thoughts on failure is multiple IDispatch implementations on 'this'.

Rather than ->
DispInvoke( this, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);

Try ->
DWebBrowserEvents2* pWebBrowserEvents = NULL;
if (SUCCEEDED (this->QueryInterface (DIID_DWebBrowserEvents2, reinterpret_cast<void**>(&pWebBrowserEvents))))
{
    hr = DispInvoke ( pWebBrowserEvents, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}

i.e. obtain an explicit DWebBrowserEvents2 interface pointer as pass it to DispInvoke, rather than simply passing 'this'.
So I just tried the code you posted and the QueryInterface call succeeds yet the DispInvoke still returns 0x80020003 Member not found.

Could this be due to the how I define my functions in Event Handler class?  

Do I need to add something like STDMETHODCALLTYPE to the function defs?

For example (this is how I currently define them)...
void BeforeNavigate2 (IDispatch * pDisp,VARIANT * URL,VARIANT * Flags, VARIANT * TargetFrameName,VARIANT * PostData,VARIANT * Headers,VARIANT_BOOL * Cancel );
void NewWindow2(IDispatch * * ppDisp,VARIANT_BOOL * Cancel );
void NavigateComplete2 ( IDispatch * pDisp,VARIANT * URL );
void DocumentComplete (IDispatch * pDisp,VARIANT * URL );

Have you implemented all of the events, and in the same order as that defined in it's idl (don't know the filename and don't have VC++ installed on this machine - trying shdocvw.idl would be a good guess though)

DispInvoke used a v-table lookup to find the relevant method based on the DISPID provided. It just could be that you haven't defined enough virtual methods to correspond with what it expects.

If I had VC++ installed I'd help you further, but I'm not at my regular place of work.

> STDMETHODCALLTYPE
If this were wrong, because DispInvoke uses a v-table lookup, we'd get error messages about wrong parameters instead of missing members. So I can't see it making much of a difference.
hey _ys_:

I'm going to wrap this question up.   Thanks a lot for all of your help.  I didn't get the final bit to work but I don't have any time to work on it again within the next few weeks.  

My connection point/sink is working which will suffice for now.  Kudos man.

I'll probably be posting another question about this in a month or so.  Maybe we can follow up on this discussion then (for more points also!).  Heh.

Cheers mate,
Aaron
No problem.

Typically I only log in once a week or so. But I'll keep an eye out for something related.

Talk to you soon.