Solved

IDeskBand Document complete event?

Posted on 2004-08-25
14
990 Views
Last Modified: 2008-03-17
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
0
Comment
Question by:AaronReams
  • 7
  • 6
14 Comments
 
LVL 8

Author Comment

by:AaronReams
ID: 11898481
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.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 11900150
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
0
 
LVL 9

Accepted Solution

by:
_ys_ earned 500 total points
ID: 11910806
Firstly, you need to obtain a path into the web browser from the band. This is achieved within your implementation of IObjectWithSite::SetSite.
Hopefully you've already done this - if not let me know.

Secondly, and more importantly, the event sink itself.
- Query the web browser for the connection point DIID_DWebBrowserEvents2, Achieved via IConnectionPointContainer and IConnectionPoint.
- Advise it we are listening for events

class CBandObj // : CWnd
{
private:
    IWebBrowser2 *m_pWebBrowser; //= NULL
    IConnectionPoint *m_pConnectionPoint; //= NULL
    DWORD m_dwCookie
//  ...
public:
    CBandObj ( ) : m_pWebBrowser(NULL), m_pConnectionPoint(NULL) { /* ... */ }
//  ...
private:
    void CreateEventSink ( );
//  ...
};

//  To be called to create your event sink.
//  Should be called as the final step of CBandObj::SetSite
//  m_pWebBrowser already has a value - CBandObj::SetSite does this
void CBandObj::CreateEventSink ( )
{
//  disconnect old sinks
    if (m_pConnectionPoint)
    {
        m_pConnectionPoint->Unadvise(m_dwCookie);
        m_pConnectionPoint->Release ( );
        m_pConnectionPoint = NULL;
    }

    IConnectionPointContainer *pConnectionPointContainer = NULL;
    if (SUCCEEDED(m_pWebBrowser->QueryInterface(
        IID_IConnectionPointContainer, reinterpret_cast<void**>(&pConnectionPointContainer))))
    {
        if (SUCCEEDED(pConnectionPointContainer->FindConnectionPoint(
            DIID_DWebBrowserEvents2, &m_pConnectionPoint)))
        {
        //  obtain the IUnknown of the event sink
        //  ... could be seperate class ...
            IUnknown *pUnkSink = static_cast<IUnknown*>(this);
        //  I assume it's the same class

        //*****
        //**NB the event sink needs to implement the interface DWebBrowserEvents2**
        //*****
            if (FAILED(HRESULT hr = m_pConnectionPoint->Advise(pUnkSink, &dwCookie)))
            {
            //  inspect hr to see why
            //  CONNECT_E_CANNOTCONNECT - your event sink doesn't implement DWebBrowserEvents2
            //  CONNECT_E_ADVISELIMIT - too many connections exist. You're out of luck!!

                m_pConnectionPoint->Release ( );
                m_pConnectionPoint = NULL;
            }
            else
            {
            //  we are in business !!!!
            }
        }
        else
        {
        //  connection point DWebBrowserEvent is not supported by web browser !?!?
        }

        pConnectionPointContainer->Release ( );
    }
    else
    {
    //  connection points are not supported by web browser !?!?
    }
}
0
 
LVL 8

Author Comment

by:AaronReams
ID: 11915576
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
0
 
LVL 9

Expert Comment

by:_ys_
ID: 11940817
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
0
 
LVL 8

Author Comment

by:AaronReams
ID: 11944723
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

0
 
LVL 9

Expert Comment

by:_ys_
ID: 11950227
>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.
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 9

Expert Comment

by:_ys_
ID: 12110069
Do you need any more on this?
0
 
LVL 8

Author Comment

by:AaronReams
ID: 12114744
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
0
 
LVL 9

Expert Comment

by:_ys_
ID: 12114878
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'.
0
 
LVL 8

Author Comment

by:AaronReams
ID: 12115854
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 );

0
 
LVL 9

Expert Comment

by:_ys_
ID: 12120771
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.
0
 
LVL 8

Author Comment

by:AaronReams
ID: 12343242
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
0
 
LVL 9

Expert Comment

by:_ys_
ID: 12345314
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.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now