Objective-C
  1  #ifndef __DISPEXSINKCONNECTOR_H__
  2  #define __DISPEXSINKCONNECTOR_H__
  3  
  4  #include "stdafx.h"
  5  #include <dispex.h>
  6  
  7  class CDispExSinkConnector :
  8  	public IDispatch
  9  {
 10  protected:
 11  	class CTypeInfoHolder
 12  	{
 13  	public:
 14  		const GUID *m_piid;
 15  		const GUID *m_plibid;
 16  		WORD m_wMajorVer;
 17  		WORD m_wMinorVer;
 18  		LCID m_lcid;
 19  		CComPtr<ITypeInfo> m_pTypeInfo;
 20  		CSimpleMap<DISPID, BSTR> m_mapDispId;
 21  
 22  		CTypeInfoHolder(const GUID *piid, const GUID *plibid, WORD wMajorVer,
 23  			WORD wMinorVer, LCID lcid) : 
 24  			m_piid(piid), m_plibid(plibid), m_wMajorVer(wMajorVer), 
 25  			m_wMinorVer(wMinorVer), m_lcid(lcid)
 26  		{
 27  			ATLASSERT(m_piid && m_plibid);
 28  			LoadFunctionNames();
 29  		}
 30  
 31  		~CTypeInfoHolder()
 32  		{
 33  			for(int i = 0; i < m_mapDispId.GetSize(); i++)
 34  				SysFreeString(m_mapDispId.GetValueAt(i));
 35  		}
 36  
 37  	protected:
 38  		HRESULT LoadFunctionNames()
 39  		{
 40  			ATLASSERT(m_pTypeInfo == NULL);
 41  
 42  			HRESULT hr;
 43  			CComPtr<ITypeLib> pTypeLib;
 44  			TYPEATTR *pta;
 45  
 46  			hr = LoadRegTypeLib(*m_plibid, m_wMajorVer, m_wMinorVer, m_lcid,
 47  				&pTypeLib);
 48  			if(FAILED(hr))
 49  				return hr;
 50  			hr = pTypeLib->GetTypeInfoOfGuid(*m_piid, &m_pTypeInfo);
 51  			if(FAILED(hr))
 52  				return hr;
 53  			hr = m_pTypeInfo->GetTypeAttr(&pta);
 54  			if(FAILED(hr))
 55  				return hr;
 56  
 57  			unsigned short i;
 58  			for(i = 0; i < pta->cFuncs; i++)
 59  			{
 60  				BSTR bsName;
 61  				FUNCDESC *pfd;
 62  				unsigned int uiNames = 0;
 63  
 64  				hr = m_pTypeInfo->GetFuncDesc(i, &pfd);
 65  				if(FAILED(hr))
 66  					continue;
 67  
 68  				hr = m_pTypeInfo->GetNames(pfd->memid, &bsName, 1, &uiNames);
 69  				if(SUCCEEDED(hr) && bsName && SysStringLen(bsName))
 70  				{
 71  					m_mapDispId.Add(pfd->memid, bsName);
 72  				}
 73  
 74  				m_pTypeInfo->ReleaseFuncDesc(pfd);
 75  
 76  			}
 77  			m_pTypeInfo->ReleaseTypeAttr(pta);
 78  
 79  			return hr;
 80  		}
 81  	};
 82  
 83  	class CConnectedObject : 
 84  		public IDispatch
 85  	{
 86  	public:
 87  		CConnectedObject(CDispExSinkConnector *pMain, CTypeInfoHolder *ptih,
 88  			IUnknown *pUnkCP) : 
 89  		  m_dwCookie(0xFEFEFEFE), m_pUnkCP(pUnkCP), m_pMain(pMain), 
 90  		  m_pTIH(ptih)
 91  		{ 
 92  			  m_pUnkCP->AddRef();
 93  			  AtlAdvise(m_pUnkCP, this, *m_pTIH->m_piid, &m_dwCookie);			  
 94  		}
 95  		~CConnectedObject()
 96  		{
 97  			Shutdown();
 98  
 99  			if(m_pTIH)
100  			{
101  				delete m_pTIH;
102  				m_pTIH = NULL;
103  			}
104  		}
105  
106  		// Helper
107  		void Shutdown()
108  		{
109  			if(m_pUnkCP && m_dwCookie != 0xFEFEFEFE)
110  			{
111  				AtlUnadvise(m_pUnkCP, *m_pTIH->m_piid, m_dwCookie);
112  				m_pUnkCP->Release();
113  				
114  				m_pUnkCP = NULL;
115  				m_dwCookie = 0xFEFEFEFE;
116  			}
117  		}
118  			
119  		// IUnknown
120  		ULONG WINAPI AddRef() { return 1; }
121  		ULONG WINAPI Release() { return 1; }
122  
123  		HRESULT WINAPI QueryInterface(REFIID riid, void **ppv)
124  		{
125  			if(::InlineIsEqualGUID(riid, *m_pTIH->m_piid))
126  			{
127  				*ppv = this;
128  				return S_OK;
129  			}
130  			
131  			*ppv = NULL;
132  			return E_NOINTERFACE;
133  		}
134  
135  		// IDispatch implementation
136  		STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
137  		{ return E_NOTIMPL; }
138  		STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
139  		{ return E_NOTIMPL; }
140  		STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
141  		{ return E_NOTIMPL; }
142  		STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
143  			DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
144  		{
145  			HRESULT hr;
146  			CComPtr<IDispatchEx> pDispEx;
147  			BOOL bEnabled;
148  
149  			if(m_pMain == NULL)
150  				return S_OK;
151  			
152  			bEnabled = m_pMain->GetEnabled();
153  			m_pMain->GetDispEx(&pDispEx);
154  
155  			if(bEnabled == FALSE)
156  				return S_OK;
157  			
158  			// Make sure we have a dispex pointer
159  			if(pDispEx == NULL)
160  			{
161  				ATLTRACE(_T("No IDispatchEx pointer set\r\n"));
162  				return S_OK;
163  			}
164  			
165  			// Determine the name of the function to try to invoke in the DispEx context
166  			BSTR bsMember = NULL;
167  			CComBSTR bstrFunction;
168  			bstrFunction += m_bstrPrefix;
169  			
170  			bsMember = m_pTIH->m_mapDispId.Lookup(dispIdMember);
171  			if(!bsMember || SysStringLen(bsMember) == 0)
172  			{
173  				ATLTRACE(_T("Could not find DISPID in lookup table\r\n"));
174  				return S_OK;
175  			}
176  			
177  			bstrFunction += bsMember;
178  			
179  			// Now try to locate a function with the same name in our IDispatchEx interface
180  			DISPID dispIdEx;
181  			hr = pDispEx->GetDispID(bstrFunction, 
182  				fdexNameCaseSensitive, &dispIdEx);
183  			if(FAILED(hr))
184  			{
185  				USES_CONVERSION;
186  				ATLTRACE(_T("Could not find matching function '%s' in IDispatchEx\r\n"), 
187  					OLE2T(bstrFunction));
188  				return S_OK;
189  			}
190  			
191  			// Call the function
192  			pDispEx->InvokeEx(dispIdEx, lcid, DISPATCH_METHOD, 
193  				pDispParams, pVarResult, pExcepInfo, NULL);
194  			
195  			return S_OK;
196  		}
197  		
198  		DWORD m_dwCookie;
199  		CTypeInfoHolder *m_pTIH;
200  		IUnknown *m_pUnkCP;
201  		CComBSTR m_bstrPrefix;
202  		CDispExSinkConnector *m_pMain;
203  	};
204  
205  public:
206  
207  	CDispExSinkConnector() : m_bEnabled(TRUE), m_dwRefCount(0), m_pDispEx(NULL)
208  	{
209  		ATLTRACE(_T("CDispExSinkConnector::CDispExSinkConnector()\n"));
210  	}
211  
212  	virtual ~CDispExSinkConnector()
213  	{
214  		ATLTRACE(_T("CDispExSinkConnector::~CDispExSinkConnector()\n"));
215  		Shutdown();
216  	}
217  
218  	// IUnknown
219  	STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
220  	{ return S_OK; }
221  	ULONG STDMETHODCALLTYPE AddRef()
222  	{ return ++m_dwRefCount; }
223  	ULONG STDMETHODCALLTYPE Release()
224  	{
225  		if(--m_dwRefCount == 0)
226  		{
227  			delete this;
228  			return 0;
229  		}
230  
231  		return m_dwRefCount;
232  	}
233  
234  	// IDispatch
235  	STDMETHOD(GetTypeInfoCount)(UINT *pctinfo)
236  	{ return E_NOTIMPL; }
237  	
238  	STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
239  	{ return E_NOTIMPL; }
240  	
241  	STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
242  	{ return E_NOTIMPL; }
243  	
244  	STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
245  	{ return E_NOTIMPL; }
246  
247  	// Adding connectable objects
248  	HRESULT ConnectObject(IUnknown *pUnk, BSTR bstrPrefix, const GUID *piid, const GUID *plibid, WORD wMajorVer = 1, WORD wMinorVer = 0, LCID lcid = 1033)
249  	{
250  		// Ensure our input params are valid
251  		if(!pUnk || !bstrPrefix || SysStringLen(bstrPrefix) == 0 ||
252  			piid == NULL || plibid == NULL)
253  			return E_INVALIDARG;
254  				
255  		CTypeInfoHolder *ptih = new CTypeInfoHolder(piid, plibid, wMajorVer,
256  			wMinorVer, lcid);
257  		if(ptih == NULL)
258  			return E_OUTOFMEMORY;
259  		
260  		HRESULT hr = S_OK;
261  		CConnectedObject *pObj = new CConnectedObject(this, ptih, pUnk);
262  		if(pObj == NULL)
263  			return E_OUTOFMEMORY;
264  		
265  		// Add the object to our map first
266  		pObj->m_bstrPrefix = bstrPrefix;
267  		m_arrObjects.Add(pObj);
268  
269  		return hr;
270  	}
271  
272  	HRESULT DisconnectObject(IUnknown *pUnk, const GUID *piid)
273  	{
274  		for(int i = 0; i < m_arrObjects.GetSize(); i++)
275  		{
276  			if(pUnk == m_arrObjects[i]->m_pUnkCP && 
277  				m_arrObjects[i]->m_pTIH && 
278  				::InlineIsEqualGUID(*m_arrObjects[i]->m_pTIH->m_piid, *piid))
279  			{
280  				CConnectedObject *pObj = m_arrObjects[i];
281  				m_arrObjects.RemoveAt(i);
282  				delete pObj;
283  				return S_OK;
284  			}
285  		}
286  		
287  		return E_INVALIDARG;
288  	}
289  
290  	HRESULT AddNamedObject(BSTR bsName, IDispatch *pDisp)
291  	{
292  		HRESULT hr;
293  
294  		if(m_pDispEx == NULL)
295  			return E_FAIL;
296  
297  		if(!bsName || SysStringLen(bsName) == 0 || !pDisp)
298  			return E_INVALIDARG;
299  
300  		// Also, add our root objects into the script namespace
301  		DISPID dispIdThis = 0;
302  		hr = m_pDispEx->GetDispID(bsName, fdexNameEnsure | fdexNameCaseSensitive,
303  			&dispIdThis);
304  		if(SUCCEEDED(hr) && dispIdThis != 0)
305  		{
306  			//  Add the object
307  			DISPID dispIdPut = DISPID_PROPERTYPUT;
308  			DISPPARAMS params;
309  			VARIANTARG vtArg;
310  			EXCEPINFO ei;
311  			ZeroMemory(&params, sizeof(DISPPARAMS));
312  			
313  			VariantInit(&vtArg);
314  			vtArg.vt = VT_DISPATCH;
315  			vtArg.pdispVal = pDisp;
316  			
317  			params.cArgs = 1;
318  			params.rgvarg = &vtArg;
319  			params.cNamedArgs = 1;
320  			params.rgdispidNamedArgs = &dispIdPut;
321  			
322  			pDisp->AddRef();
323  			
324  			hr = m_pDispEx->InvokeEx(dispIdThis, LOCALE_USER_DEFAULT, 
325  				DISPATCH_PROPERTYPUT, &params, NULL, &ei, NULL);
326  
327  			VariantClear(&vtArg);
328  		}
329  
330  		return hr;
331  	}
332  
333  	HRESULT RemoveNamedObject(BSTR bsName)
334  	{
335  		if(m_pDispEx == NULL)
336  			return E_FAIL;
337  
338  		if(!bsName || SysStringLen(bsName) == 0)
339  			return E_INVALIDARG;
340  
341  		return m_pDispEx->DeleteMemberByName(bsName, fdexNameCaseSensitive);
342  	}
343  
344  	HRESULT AddTypeLib(REFGUID guidTypeLib, WORD wMaj, WORD wMin)
345  	{
346  		HRESULT hr;
347  		CComPtr<ITypeLib> pTypeLib;
348  		UINT uiCount, i;
349  
350  		if(m_pDispEx == NULL)
351  			return E_FAIL;
352  
353  		hr = LoadRegTypeLib(guidTypeLib, wMaj, wMin, 0x407, &pTypeLib);
354  		if(FAILED(hr))
355  			return hr;
356  
357  		uiCount = pTypeLib->GetTypeInfoCount();
358  		for(i = 0; i < uiCount; i++)
359  		{
360  			USES_CONVERSION;
361  			CComPtr<ITypeInfo> pTypeInfo;
362  			TYPEKIND tk;
363  			TYPEATTR *pta;
364  
365  			// We want to get all enumerations
366  			hr = pTypeLib->GetTypeInfoType(i, &tk);
367  			if(tk != TKIND_ENUM || FAILED(hr))
368  				continue;
369  
370  			hr = pTypeLib->GetTypeInfo(i, &pTypeInfo);
371  			if(FAILED(hr))
372  				return hr;
373  			
374  			hr = pTypeInfo->GetTypeAttr(&pta);			
375  			if(FAILED(hr))
376  				return hr;
377  
378  			for(UINT j = 0; j < pta->cVars; j++)
379  			{
380  				CComBSTR bstrName;
381  				VARDESC *pvd;
382  
383  				hr = pTypeInfo->GetVarDesc(j, &pvd);
384  				if(FAILED(hr))
385  					return hr;
386  
387  				// Get the name of the enum property
388  				hr = pTypeInfo->GetDocumentation(pvd->memid, &bstrName, NULL,
389  					NULL, NULL);
390  
391  				if(bstrName.Length())
392  				{
393  					// Now add this value to our dispex
394  					DISPID dispIdThis = DISPID_UNKNOWN;
395  					hr = m_pDispEx->GetDispID(bstrName, fdexNameEnsure | fdexNameCaseSensitive,
396  						&dispIdThis);
397  					if(SUCCEEDED(hr) && dispIdThis != DISPID_UNKNOWN)
398  					{
399  						//  Add the object
400  						DISPID dispIdPut = DISPID_PROPERTYPUT;
401  						DISPPARAMS params;
402  						VARIANTARG vtArg;
403  						EXCEPINFO ei;
404  						ZeroMemory(&params, sizeof(DISPPARAMS));
405  						
406  						VariantInit(&vtArg);
407  						vtArg.vt = VT_I4;
408  						vtArg.lVal = pvd->lpvarValue->lVal;
409  						
410  						params.cArgs = 1;
411  						params.rgvarg = &vtArg;
412  						params.cNamedArgs = 1;
413  						params.rgdispidNamedArgs = &dispIdPut;
414  					
415  						// Try to add the member to our script engine
416  						hr = m_pDispEx->InvokeEx(dispIdThis, LOCALE_USER_DEFAULT, 
417  							DISPATCH_PROPERTYPUT, &params, NULL, &ei, NULL);
418  					}
419  				}
420  
421  				pTypeInfo->ReleaseVarDesc(pvd);
422  			}
423  
424  			pTypeInfo->ReleaseTypeAttr(pta);
425  		}
426  
427  		return S_OK;
428  	}
429  
430  	void Shutdown()
431  	{
432  		for(int i = 0; i < m_arrObjects.GetSize(); i++)
433  			delete m_arrObjects[i];
434  
435  		m_arrObjects.RemoveAll();
436  	}
437  
438  	void SetEnabled(BOOL bEnabled) { m_bEnabled = bEnabled; }
439  	BOOL GetEnabled() { return m_bEnabled; }
440  
441  	void SetDispEx(IDispatchEx *pDispEx)
442  	{
443  		CComBSTR bstrThisGuid("AC0B188C-6B55-408f-9E8C-821B9B5467CB");
444  		
445  		RemoveNamedObject(bstrThisGuid);
446  
447  		// NOTE: We're holding a weak reference to this IDispatchEx*
448  		// in order to prevent a circular refcount ... 
449  		m_pDispEx = pDispEx;
450  		
451  		// We need to add ourselves to the script engine
452  		AddNamedObject(bstrThisGuid, this);
453  	}
454  	BOOL GetDispEx(IDispatchEx **ppDispEx)
455  	{
456  		if(!ppDispEx)
457  			return FALSE;
458  
459  		*ppDispEx = NULL;
460  		if(m_pDispEx)
461  		{
462  			*ppDispEx = m_pDispEx;
463  			(*ppDispEx)->AddRef();
464  		}
465  
466  		return TRUE;
467  	}
468  
469  protected:
470  	DWORD m_dwRefCount;
471  
472  	BOOL m_bEnabled;
473  	IDispatchEx *m_pDispEx;
474  	CSimpleArray<CConnectedObject*> m_arrObjects;
475  };
476  
477  #endif //__DISPEXSINKCONNECTOR_H__