COM callback interface and sink object

COM/DCOM callback supports callback mechanism. Server can call client's functions or events using a callback interface and this interface is denoted as [source] interface in IDL. Client implements this source interface class and its all callback events and creates an object. This object is passed to server as an sink object.

Internet Explorer sink interface

DWebBrowserEvents2 is the source interface or event interface of Web Browser Control/InternetExplorer class. We are showing only three event functions here. These are events triggered during Navigate call. These are DownloadBegin, ProgressChange and DownloadComplete.

#define DISPID_PROGRESSCHANGE     108
#define DISPID_DOWNLOADCOMPLETE   104
#define DISPID_DOWNLOADBEGIN      106

[
  uuid(34A715A0-6587-11D0-924A-0020AFC7AC4D), // IID_DWebBrowserEvents2
  helpstring("Web Browser Control events interface"),
  hidden
]
dispinterface DWebBrowserEvents2
{
properties:
  methods:

  [id(DISPID_PROGRESSCHANGE),
  helpstring("Fired when download progress is updated."), helpcontext(0x0000)]

  void ProgressChange([in] long Progress, [in] long ProgressMax);

  [id(DISPID_DOWNLOADCOMPLETE),
  helpstring("Download of page complete."), helpcontext(0x0000)]

  void DownloadComplete();

  [id(DISPID_DOWNLOADBEGIN),
   helpstring("Download of a page started."), helpcontext(0x000)]
   
  void DownloadBegin();

}
  
[uuid(0002DF01-0000-0000-C000-000000000046), // CLSID_InternetExplorer
helpstring("Internet Explorer Application."),
]

coclass InternetExplorer
{
  [default]     interface   IWebBrowser2;
  interface   IWebBrowserApp;
  [default, source] dispinterface DWebBrowserEvents2;
  [source]      dispinterface DWebBrowserEvents;
}

Internet Explorer sink class

A COM client which wish to receive events should implement a class derived from dispinterface or IDispatch. This class should handle IDispatch::Invoke and these three dispatch ids at runtime. We have a switch block in Invoke to handle DISPID_DOWNLOADBEGIN, DISPID_PROGRESSCHANGE and DISPID_PROGRESSCHANGE. We an UI dialog in the current process and we are calling dialog events from these places.

class CIExplorerEvents : public IDispatch 
{
private:
  DWORD m_nref;

public:
  CSinkObjectDemoDlg *m_pDlgMain;
  long __stdcall QueryInterface(const struct _GUID & iid,LPVOID* ppvObject);
  unsigned long __stdcall AddRef(void);
  unsigned long __stdcall Release(void);
  long __stdcall GetTypeInfoCount(unsigned int *);
  long __stdcall GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** );
  long __stdcall GetIDsOfNames(const struct _GUID &,
  unsigned short ** ,unsigned int,unsigned long,long *);
  long __stdcall Invoke(long,
  const struct _GUID &,
		unsigned long,
		unsigned short,
		struct tagDISPPARAMS *,
		struct tagVARIANT *,
		struct tagEXCEPINFO *,
		unsigned int *);
};
CIExplorerEvents::CIExplorerEvents()
{
    m_nref = 0;
}
long __stdcall CIExplorerEvents::QueryInterface(
  const struct _GUID & iid,
  LPVOID* ppvObject
)
{ 
  // Match the interface and return the proper pointer
  if ( iid == IID_IUnknown) {

    *ppvObject = dynamic_cast( this );

  } else  if ( iid == IID_IDispatch) {

    *ppvObject = dynamic_cast( this );

  } else {

    /* It didn't match */
    *ppvObject = NULL;
    return E_NOINTERFACE;
  }

  /* Increment refcount and return to client */
  this->AddRef();
  
  // Return success
  return S_OK;
}

ULONG __stdcall CIExplorerEvents::AddRef(void)
{
  return ++m_nref;
}

ULONG __stdcall CIExplorerEvents::Release(void)
{
  return --m_nref;
}

long __stdcall CIExplorerEvents::GetTypeInfoCount(UINT *)
{
  return E_NOTIMPL;
}
long __stdcall CIExplorerEvents::GetTypeInfo(
  unsigned int,
  unsigned long,
  struct ITypeInfo **
)
{
  return E_NOTIMPL;
}

long __stdcall CIExplorerEvents::GetIDsOfNames(
  const struct _GUID &,
  unsigned short ** ,
  unsigned int,
  unsigned long,
  long *
)
{
  return E_NOTIMPL;
}

long __stdcall CIExplorerEvents::Invoke(
  long dispid,
  const struct _GUID &riid,
  unsigned long,
  unsigned short,
  struct tagDISPPARAMS *pDispParam,
  struct tagVARIANT *,
  struct tagEXCEPINFO *,
  unsigned int *)
{
  switch (dispid)
  {
    case  DISPID_PROGRESSCHANGE:
      m_pDlgMain->ProgressChange(pDispParam->rgvarg[0].lVal,
                                 pDispParam->rgvarg[1].lVal);
      break;
    case DISPID_DOWNLOADCOMPLETE:
      m_pDlgMain->DownloadComplete();
      break;
    case DISPID_DOWNLOADBEGIN:
      m_pDlgMain->DownloadBegin();
      break;
    default:
  }
  return 0;
}

Dialog event handlers

Sink class handles DISPID_DOWNLOADBEGIN, DISPID_PROGRESSCHANGE and DISPID_DOWNLOADCOMPLETE events in Invoke function. This sink object has a member called m_pDlgMain to hold the UI dialog pointer. Sink class calls the corresponding dialog events functions using the dialog pointer. So we see DownloadBegin, ProgressChange and DownloadComplete are called one by one from com server and UI prints the messages in the text area.

class CSinkObjectDemoDlg : public CDialog
{
  CString                    m_strEventLog;
  IWebBrowser2              *m_pBrowser;
  IConnectionPointContainer *m_pCPC;
  IConnectionPoint          *m_pCP;
  CIExplorerEvents           m_Events;
  DWORD                     *m_dwC;

  virtual BOOL OnInitDialog();

  afx_msg void OnDownload();
  virtual void OnOK();
  afx_msg void OnOk();

public:
  CSinkObjectDemoDlg(CWnd* pParent = NULL);

  /* Callback functions and events */
  void ProgressChange(long Progress, long ProgressMax);
  void DownloadComplete();
  void DownloadBegin();
};
BEGIN_MESSAGE_MAP(CSinkObjectDemoDlg, CDialog)

	ON_BN_CLICKED(IDC_DOWNLOAD, OnDownload)
	ON_BN_CLICKED(IDCANCEL, OnOk)
	ON_COMMAND(IDCANCEL, OnOk)
	ON_COMMAND(IDOK, OnOk)

END_MESSAGE_MAP()
void CSinkObjectDemoDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EVENTLOG, m_strEventLog);
}
void CSinkObjectDemoDlg::ProgressChange(long Progress,
                                        long ProgressMax)
{
  CString strProgress;
  if (Progress < 0)
  {
    m_strEventLog += "Event: DownloadComplete()\r\n";
  }
  else if (ProgressMax ==0)
  {
    m_strEventLog += "Event: ProgressChange 0%\r\n";
  }
  else
  {
    strProgress.Format("Event: ProgressChange %.2f%c\r\n",
                       ((float)Progress*100)/ProgressMax, '%');
    m_strEventLog += strProgress;
  }
  UpdateData(FALSE);
}

void CSinkObjectDemoDlg::DownloadComplete()
{
  m_strEventLog +=	"Event: DownloadComplete()\r\n";
  UpdateData(FALSE);
}

void CSinkObjectDemoDlg::DownloadBegin()
{
  m_strEventLog +=	"Event: DownloadBegin()\r\n";
  UpdateData(FALSE);
}
BOOL CSinkObjectDemoDlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  SetIcon(m_hIcon, TRUE);
  SetIcon(m_hIcon, FALSE);

  if(CoInitialize(NULL) != S_OK)
    return FALSE;

  return TRUE;
}

void CSinkObjectDemoDlg::OnDownload() 
{
  HRESULT hr;
  IUnknown *pUnknown;
  CLSID clsid;
  WCHAR strURL[1000];
  BSTR bstrVal;
  
  m_strEventLog +=	"OnDownload\r\n";
  UpdateData(FALSE);

  if(CLSIDFromProgID(OLESTR("InternetExplorer.Application"),
     &clsid) != S_OK) {
     m_strEventLog +=	"InternetExplorer.Application not found\r\n";
     UpdateData(FALSE);
     return;
  }

  if(CoCreateInstance(clsid,
      NULL,
	  CLSCTX_SERVER,
	  IID_IUnknown, 
	  (LPVOID *) &pUnknown) != S_OK){
     m_strEventLog +=	"CoCreateInstance failed\r\n";
     UpdateData(FALSE);
     return;
  }

  if(pUnknown->QueryInterface(
     IID_IWebBrowser2,
	 (LPVOID *) &m_pBrowser) != S_OK){
     m_strEventLog +=	"QueryInterface of IID_IWebBrowser2 failed\r\n";
     UpdateData(FALSE);
     return;
  }

  if(pUnknown->QueryInterface(
      IID_IConnectionPointContainer,
	  (LPVOID *) &m_pCPC) != S_OK){
     m_strEventLog +=	"QueryInterface of ConnectionPointContainer failed\r\n";
     UpdateData(FALSE);
     return;
  }
  hr = m_pCPC->FindConnectionPoint(DIID_DWebBrowserEvents2, &m_pCP);
  if((hr != S_OK) || (!m_pCP)){
     m_strEventLog +=	"FindConnectionPoint of DIID_DWebBrowserEvents2 failed\r\n";
     UpdateData(FALSE);
     return;
  }
  
  m_Events.m_pDlgMain = this;
  hr = m_pCP->Advise(&m_Events, &m_dwC);
  if(hr != S_OK){
     m_strEventLog +=	"Advise of DIID_DWebBrowserEvents2 failed\r\n";
     UpdateData(FALSE);
     return;
  }
  pUnknown->Release();
  wsprintfW(strURL,
            L"http://download.winzip.com/gl/nkln/winzip21_downwz.exe?ll=%d",
            GetTickCount());
  bstrVal = SysAllocString (strURL);
  m_pBrowser->Navigate(bstrVal, NULL, NULL, NULL, NULL);
  SysFreeString(bstrVal);
  return;
}

void CSinkObjectDemoDlg::OnOK() 
{
  if(m_pBrowser)
    pBrowser->Release();

  if(m_pCP)
    pCP->Unadvise(dwC);

  if(m_pCPC)
    pCPC->Release();

  CoUninitialize();	
  CDialog::OnOK();
}

About our authors: Team EQA

You have viewed 1 page out of 67. Your COM/DCOM learning is 0.00% complete. Login to check your learning progress.

#