864
社区成员
发帖
与我相关
我的任务
分享
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(39570840-2098-4803-9150-A49F98530CC7),
dual,
nonextensible,
helpstring("ISendEvent 接口"),
pointer_default(unique)
]
interface ISendEvent : IDispatch{
[id(1), helpstring("方法Start")] HRESULT Start(void);
};
[
uuid(7F0E1638-858D-4806-A869-37EC37028B1B),
version(1.0),
helpstring("EventSender 1.0 类型库")
]
library EventSenderLib
{
importlib("stdole2.tlb");
[
uuid(C40F0451-A58C-4400-A9EE-D66F4C2E2F7D),
helpstring("_ISendEventEvents 接口")
]
dispinterface _ISendEventEvents
{
properties:
methods:
[id(1), helpstring("方法OnEvent")] HRESULT OnEvent(void);
};
[
uuid(8716FB51-3B68-4D2C-9E1C-53988C2151F0),
helpstring("SendEvent Class")
]
coclass SendEvent
{
[default] interface ISendEvent;
[default, source] dispinterface _ISendEventEvents;
};
};
// CSendEvent
STDMETHODIMP CSendEvent::Start(void)
{
Fire_OnEvent();
return S_OK;
}
template<class T>
class CProxy_ISendEventEvents :
public IConnectionPointImpl<T, &__uuidof(_ISendEventEvents)>
{
public:
HRESULT Fire_OnEvent()
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
if (pConnection)
{
CComVariant varResult;
DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
};
Dim WithEvents evt as EventSenderLib.SendEvent
Private Sub Command1_Click()
Evt.Start
End Sub
Private Sub evt_OnEvent()
MsgBox "OnEvent"
End Sub
Private Sub Form_Load()
Set evt = New EventSenderLib.SendEvent
End Sub
DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
/*
Michael Lindig
July 16, 2000
In most cases of my work I develop ATL objects with worker threads.
In the worker threads there must be often fire events for signalling
thread states. The problem of firing events from worker thread is
that they must enter another apartment. In this apartment the sink
interfaces are not valid and thats why some clients would not be receive
the events (eg VB). I found a simple solution for that. (A solution
without PostMessage.)
Only the proxy code of wizard must be changed, to get the firing of
events right. I use the global interface table (GIT) for my solution.
"The GIT holds a list of marshaled interface pointers that, on request
can be unmarshaled any number of times to any number of apartment in
your process, regardless of whether the pointer is for an object or a
proxy."(Professional ATL COM Programming, Dr Richard Grimes).
I wrote a new specialized class for CComDynamicUnkArray which use the
GIT for accessing the sink interfaces.
====================================================================
2010.8.12 modified by yangleigang
original:
//The proxy code use this function to get the interface !
CComPtr GetAt(int nIndex)
{
DWORD dwCookie = (DWORD)CComDynamicUnkArray::GetAt( nIndex );
if( dwCookie == 0 )
return NULL;
if( CookieMap.find( dwCookie ) == CookieMap.end() )
{
return (IUnknown*)dwCookie;
}
if( GIT != NULL )
{
CComPtr ppv;
HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwCookie], //Cookie identifying the desired global
//interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer
//to the desired interface
);
if( hr == S_OK )
{
return ppv;
}
//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)dwCookie;
}
after modified:
//The proxy code use this function to get the interface !
CComPtr<IUnknown> GetAt(int nIndex)
{
IUnknown* pUnknown = CComDynamicUnkArray::GetAt( nIndex );
if(NULL == pUnknown)
{
return NULL;
}
DWORD dwOrgCookie = CComDynamicUnkArray::GetCookie(&pUnknown);
if( dwOrgCookie == 0 )
return NULL;
if( CookieMap.find( dwOrgCookie ) == CookieMap.end() )
{
return (IUnknown*)pUnknown;
}
if( GIT != NULL )
{
CComPtr<IUnknown> ppv;
HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwOrgCookie], //Cookie identifying the desired global interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);
if( hr == S_OK )
{
return ppv;
}
//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)pUnknown;
}
reason:
1. CComDynamicUnkArray::GetAt( nIndex ).
此函数返回Iunknown接口指针而不是Cookie。原来代码中错误的把这个指针
直接当成Cookie来使用,导致程序运行不正确。
2. 在CComDynamicUnkArray::Add()函数中,CComDynamicUnkArray类默认给m_vec
分配4个空间。即使只Add了一个接口指针,m_vec的size也是4。如果我们
只有一个接口,也会调用GetAt函数4次。而且第2/3/4此得到的IUnknown指针
均为NULL。因此,我们需要添加是否为NULL的判断。
3. CComDynamicUnkArray::GetCookie返回的是CComDynamicUnkArray中的Cookie。
此Cookie和CookieMap中存放的Cookie不是一个东西。CComDynamicUnkArray的
Cookie刚好是CookieMap中的键值。因此,为了避免混淆,将CComDynamicUnkArray::GetCookie
返回的Cookie命名为orgCookie。
*/
class CComDynamicUnkArray_GIT : public CComDynamicUnkArray
{
private:
IGlobalInterfaceTable* GIT;
public:
CComDynamicUnkArray_GIT() : CComDynamicUnkArray()
{
GIT = NULL;
::CoInitialize(NULL);
CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IGlobalInterfaceTable),
reinterpret_cast< void** >(&GIT) );
}
~CComDynamicUnkArray_GIT()
{
//clean up the class
clear();
if( GIT != NULL )
{
GIT->Release();
}
::CoUninitialize();
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
//The proxy code use this function to get the interface !
CComPtr<IUnknown> GetAt(int nIndex)
{
IUnknown* pUnknown = CComDynamicUnkArray::GetAt( nIndex );
if(NULL == pUnknown)
{
return NULL;
}
DWORD dwOrgCookie = CComDynamicUnkArray::GetCookie(&pUnknown);
if( dwOrgCookie == 0 )
return NULL;
if( CookieMap.find( dwOrgCookie ) == CookieMap.end() )
{
return (IUnknown*)pUnknown;
}
if( GIT != NULL )
{
CComPtr<IUnknown> ppv;
HRESULT hr = GIT->GetInterfaceFromGlobal(
CookieMap[dwOrgCookie], //Cookie identifying the desired global interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);
if( hr == S_OK )
{
return ppv;
}
//Should never be reached, a ASSERT or exception is possible
}
return (IUnknown*)pUnknown;
}
//clean up the GIT
void clear()
{
CComDynamicUnkArray::clear();
if( GIT != NULL )
{
map< DWORD, DWORD >::iterator iter;
for (iter = CookieMap.begin();
iter != CookieMap.end();
++iter )
{
GIT->RevokeInterfaceFromGlobal(
iter->second //Cookie that was returned from RegisterInterfaceInGlobal
);
}
}
CookieMap.clear();
}
protected:
map< DWORD, DWORD > CookieMap;
};
inline DWORD CComDynamicUnkArray_GIT::Add(IUnknown* pUnk)
{
DWORD Result = CComDynamicUnkArray::Add( pUnk );
HRESULT hr;
DWORD pdwCookie = 0;
if( GIT != NULL )
{
hr = GIT->RegisterInterfaceInGlobal(
pUnk, //Pointer to interface of type riid of object containing global interface
__uuidof(IUnknown), //IID of the interface to be registered
&pdwCookie //Supplies a pointer to the cookie that provides a caller in another apartment access to the interface pointer
);
}
if( hr == S_OK )
{
CookieMap[Result] = pdwCookie;
}
return Result;
}
inline BOOL CComDynamicUnkArray_GIT::Remove(DWORD dwCookie)
{
BOOL Result = CComDynamicUnkArray::Remove( dwCookie );
if( GIT != NULL )
{
if( CookieMap.find( dwCookie ) != CookieMap.end() )
{
GIT->RevokeInterfaceFromGlobal(
CookieMap[dwCookie] //Cookie that was returned from RegisterInterfaceInGlobal
);
CookieMap.erase(dwCookie);
}
}
return Result;
}
UINT WINAPI EventProc(LPVOID pParam)
{
::CoInitialize(NULL);
CEvent* pEvent = reinterpret_cast<CEvent*>(pParam);
while(1)
{
pEvent->OnEvent();
::Sleep(1000);
}
::CoUninitialize();
}
class CEvent
{
public:
CEvent(CSendEvent* pParent) : m_pParent(pParent)
{}
void OnEvent()
{
m_pParent->OnEvent();
}
private:
CSendEvent* m_pParent;
};
UINT WINAPI EventProc(LPVOID pParam)
{
CEvent* pEvent = reinterpret_cast<CEvent*>(pParam);
while(1)
{
//pEvent->OnEvent();
::Sleep(1000);
}
}
// CSendEvent
STDMETHODIMP CSendEvent::Start(void)
{
CEvent* pEvent = new CEvent(this);
HANDLE hThread;
UINT uiThreadId = 0;
//启动线程,使用_beginthreadex
hThread = (HANDLE)_beginthreadex(NULL, // 安全参数
0, // 堆栈
EventProc,// 线程程序
pEvent, // 线程参数
CREATE_SUSPENDED, //创建模式
&uiThreadId); // 线程ID
//继续线程
ResumeThread( hThread );
OnEvent();
return S_OK;
}
void CSendEvent::OnEvent()
{
Fire_OnEvent();
}