#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <stdio.h>
#import "..\..\..\..\VBSTACOMObj\VBSTACOMObj.dll"
using namespace VBSTACOMObj;
#include "..\..\..\..\Shared\ComThread.h"
/* Simple function that displays the current thread ID. */
void DisplayCurrentThreadId()
{
TCHAR szMessage[256];
sprintf (szMessage, "Thread ID : 0x%X", GetCurrentThreadId());
::MessageBox(NULL, szMessage, "TestMethod1()", MB_OK);
}
/* LowLevelInProcMarshalInterface() uses low-level APIs */
/* CreateStreamOnHGlobal(), CoMarshalInterface(), and IStream */
/* methods to perform interface pointer marshalling across */
/* apartments in a process. */
/* */
template <typename T>
LPSTREAM LowLevelInProcMarshalInterface(T* pInterface, REFIID riid)
{
IUnknown* pIUnknown = NULL;
IStream* pIStreamRet = NULL;
/* QI the original interface pointer for its IUnknown interface. */
pInterface -> QueryInterface (IID_IUnknown, (void**)&pIUnknown);
/* Once we get the IUnknown pointer, we serialize it into a stream of bytes. */
if (pIUnknown)
{
/* Create a Stream Object which will reside in global memory. */
/* We set the first parameter to NULL, hence indicating that */
/* we want CreateStreamOnHGlobal() to internally allocate */
/* a new shared memory block of size zero. */
/* The second parameter is set to TRUE so that when the returned*/
/* stream object is Release()'d, the global memory will also be */
/* freed. */
::CreateStreamOnHGlobal
(
0,
TRUE,
&pIStreamRet
);
if (pIStreamRet)
{
LARGE_INTEGER li = { 0 };
/* We use the new stream object to store the marshalling data */
/* of spISimpleCOMObject2. */
/* The use of MSHCTX_INPROC indicates that the unmarshaling */
/* of the data in the stream will be done in another apartment*/
/* in the same process. */
::CoMarshalInterface
(
pIStreamRet,
riid,
(IUnknown*)pIUnknown,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL
);
/* Always reset the stream to the beginning.*/
pIStreamRet -> Seek(li, STREAM_SEEK_SET, NULL);
}
pIUnknown -> Release();
pIUnknown = NULL;
}
return pIStreamRet;
}
/* LowLevelInProcUnmarshalInterface() uses low-level APIs */
/* CoUnmarshalInterface(), CoReleaseMarshalData(), and */
/* IStream methods to perform interface pointer unmarshalling */
/* for an apartment in a process. */
/* */
template <typename T>
void LowLevelInProcUnmarshalInterface(LPSTREAM pIStream, REFIID riid, T** ppInterfaceReceiver)
{
/* Deserialize the byte contents of the IStream object */
/* into an actual interface pointer to be used only */
/* within this thread. */
/* */
/* The interface pointer will not be a direct pointer. */
/* It will be a proxy to the original pointer. */
if (pIStream)
{
LARGE_INTEGER li = { 0 };
/* Make sure stream pointer is at the beginning of the stream. */
pIStream -> Seek(li, STREAM_SEEK_SET, NULL);
if
(
::CoUnmarshalInterface
(
pIStream, //Pointer to the stream
riid, //Reference to the identifier of the interface
(void **)ppInterfaceReceiver //Address of output variable that receives the
// interface pointer requested in riid
) != S_OK
)
{
/* Since unmarshalling has failed, we call */
/* CoReleaseMarshalData() to destroys the */
/* previously marshaled data packet contained*/
/* in pIStream. */
::CoReleaseMarshalData(pIStream);
}
/* When pIStream is Release()'d the underlying global memory*/
/* used to store the bytes of the stream is also freed. */
pIStream -> Release();
pIStream = NULL;
}
}
/* This thread function obtains a pointer to */
/* a _ClassVBSTACOMObj interface via a stream */
/* object which contains apartment-independent */
/* serialized bytes of the interface pointer. */
/* */
/* This set of bytes can be de-serialized into */
/* a proxy to the interface pointer. */
/* This de-serialization (or unmarshalling) */
/* process is performed by our own */
/* LowLevelInProcUnmarshalInterface() function.*/
/* */
DWORD WINAPI ThreadFunc_MarshalUsingLowLevelAPI(LPVOID lpvParameter)
{
/* The IStream object may be passed from one thread */
/* to another. It is thread-independent. */
LPSTREAM pIStream = (LPSTREAM)lpvParameter;
_ClassVBSTACOMObj* p_ClassVBSTACOMObj = NULL;
/* This thread enters an STA.*/
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/* Note the id of this thread. */
/* Let's say this is thread_id_2.*/
DisplayCurrentThreadId();
/* Deserialize the byte contents of the IStream object */
/* into an actual interface pointer to be used only */
/* within this thread. */
/* */
/* The interface pointer will not be a direct pointer. */
/* It will be a proxy to the original pointer. */
if (pIStream)
{
/* pIStream will be Release()'d inside LowLevelInProcUnmarshalInterface(). */
LowLevelInProcUnmarshalInterface<_ClassVBSTACOMObj>(pIStream, __uuidof(_ClassVBSTACOMObjPtr), &p_ClassVBSTACOMObj);
}
if (p_ClassVBSTACOMObj)
{
/* Call the TestMethod1() using the proxy. */
/* You will note that the thread id will */
/* not be thread_id_2. It will be threadid_1 */
/* which is the id of the STA thread in which*/
/* the object was originally created. */
p_ClassVBSTACOMObj -> TestMethod1();
/* You may be surprised that the return value */
/* of Release() is actually zero, showing that */
/* it is the proxy (not the original interface) */
/* that is Release()'d.*/
p_ClassVBSTACOMObj -> Release();
p_ClassVBSTACOMObj = NULL;
}
::CoUninitialize();
return 0;
}
/* This function demonstrates a proper way of marshalling an interface */
/* pointer from one apartment to another. */
/* */
/* The method used here involves a stream of bytes that stores the thread */
/* and apartment independent serialized bytes of an interface pointer. */
/* This stream of bytes (headed by an IStream interface pointer) is then */
/* passed to a thread of a destination apartment distinct from the original*/
/* interface pointer's apartment. */
/* */
/* The destination thread then deserializes the bytes into a proxy to the */
/* original interface pointer. */
/* */
/* This demonstration uses low-level APIs to achieve its objectives as */
/* coded in the functions LowLevelInProcMarshalInterface() and */
/* LowLevelInProcUnmarshalInterface(). */
/* */
void DemonstrateInterThreadMarshallingUsingLowLevelAPI(_ClassVBSTACOMObjPtr& sp_ClassVBSTACOMObj)
{
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
IStream* pIStream = NULL;
/* Prepare the serialization bytes of sp_ClassVBSTACOMObj */
/* and store it inside a stream object handled by pIStream. */
pIStream = LowLevelInProcMarshalInterface<_ClassVBSTACOMObj>(sp_ClassVBSTACOMObj, __uuidof(_ClassVBSTACOMObjPtr));
if (pIStream)
{
/* Demonstrate the use of a stream of bytes to marshal an interface */
/* pointer from one thread to another. */
hThread = CreateThread
(
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)ThreadFunc_MarshalUsingLowLevelAPI,
(LPVOID)pIStream,
(DWORD)0,
(LPDWORD)&dwThreadId
);
ThreadMsgWaitForSingleObject(hThread, INFINITE);
CloseHandle (hThread);
hThread = NULL;
/* Note : do not call pIStream -> Release(). */
/* This is done in the receiving thread when */
/* it calls LowLevelInProcUnmarshalInterface().*/
}
}
/* This thread function obtains a pointer to */
/* an _ClassVBSTACOMObj interface via a stream*/
/* which contains an apartment-independent */
/* serialized bytes of an interface pointer. */
/* */
/* This set of bytes can be de-serialized into*/
/* a proxy to the original interface pointer. */
/* */
DWORD WINAPI ThreadFunc_MarshalUsingIStream(LPVOID lpvParameter)
{
/* The IStream object may be passed from one thread */
/* to another. It is thread-independent. */
LPSTREAM pIStream = (LPSTREAM)lpvParameter;
_ClassVBSTACOMObj* p_ClassVBSTACOMObj = NULL;
/* This thread enters an STA.*/
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/* Note the id of this thread. */
/* Let's say this is thread_id_3.*/
DisplayCurrentThreadId();
/* Deserialize the byte contents of the IStream object */
/* into an actual interface pointer to be used only */
/* within this thread. */
/* */
/* The interface pointer will not be a direct pointer. */
/* It will be a proxy to the original pointer. */
if (pIStream)
{
::CoGetInterfaceAndReleaseStream
(
pIStream,
__uuidof(_ClassVBSTACOMObj),
(void**)&p_ClassVBSTACOMObj
);
}
if (p_ClassVBSTACOMObj)
{
/* Call the TestMethod1() using the proxy. */
/* You will note that the thread id will */
/* not be thread_id_3. It will be thread_id_1 */
/* (main()'s thread id) which is the id */
/* of the STA thread in which the object */
/* was originally created. */
p_ClassVBSTACOMObj -> TestMethod1();
/* You may be surprised that the return value */
/* of Release() is actually zero, showing that */
/* it is the proxy that is Release()'d. */
p_ClassVBSTACOMObj -> Release();
p_ClassVBSTACOMObj = NULL;
}
::CoUninitialize();
return 0;
}
/* This function demonstrates a proper way of marshalling an interface */
/* pointer from one apartment to another. */
/* */
/* The method used here involves a stream of bytes that stores the thread */
/* and apartment-independent serialized bytes of an interface pointer. */
/* This stream of bytes (headed by an IStream interface pointer) is then */
/* passed to a destination thread of an apartment distinct from the original*/
/* interface pointer's own apartment. */
/* */
/* The destination thread then deserializes the bytes into a proxy to the */
/* original interface pointer. */
/* */
void DemonstrateInterThreadMarshallingUsingIStream(_ClassVBSTACOMObjPtr& sp_ClassVBSTACOMObj)
{
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
IUnknown* pIUnknown = NULL;
IStream* pIStream = NULL;
/* QI the original interface pointer for its IUnknown interface. */
sp_ClassVBSTACOMObj -> QueryInterface (IID_IUnknown, (void**)&pIUnknown);
/* Once we get the IUnknown pointer, we serialize it into a stream of bytes. */
if (pIUnknown)
{
::CoMarshalInterThreadInterfaceInStream
(
__uuidof(_ClassVBSTACOMObj),
pIUnknown,
&pIStream
);
pIUnknown -> Release();
pIUnknown = NULL;
}
if (pIStream)
{
/* Demonstrate the use of a stream of bytes to marshal an interface */
/* pointer from one thread to another. */
hThread = CreateThread
(
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)ThreadFunc_MarshalUsingIStream,
(LPVOID)pIStream,
(DWORD)0,
(LPDWORD)&dwThreadId
);
ThreadMsgWaitForSingleObject(hThread, INFINITE);
CloseHandle (hThread);
hThread = NULL;
/* Note : do not call pIStream -> Release(). */
/* This is done when receiving thread calls */
/* CoGetInterfaceAndReleaseStream(). */
}
}
/* This is a thread start function that demonstrates */
/* the use of a Global Interface Table to transfer */
/* interface pointers from one thread to another. */
DWORD WINAPI ThreadFunc_MarshalUsingGIT(LPVOID lpvParameter)
{
/* The cookie of the interface registered in the GIT is passed by */
/* the thread parameter. */
DWORD dwCookie = (DWORD)lpvParameter;
_ClassVBSTACOMObj* p_ClassVBSTACOMObj = NULL;
IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
/* Make this thread an STA thread. */
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/* There is a single instance of the global interface */
/* table per process. */
/* Hence all calls in a process to create it will */
/* return the same instance. */
CoCreateInstance
(
CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pIGlobalInterfaceTable
);
if (pIGlobalInterfaceTable)
{
/* Display the id of this thread. Let's say this is thread_id_4. */
DisplayCurrentThreadId();
/* Retrieve the interface pointer from the GIT. */
/* What is returned is actually a proxy to the */
/* original interface pointer created in the */
/* main() function. */
pIGlobalInterfaceTable -> GetInterfaceFromGlobal
(
dwCookie,
__uuidof(_ClassVBSTACOMObj),
(void**)&p_ClassVBSTACOMObj
);
if (p_ClassVBSTACOMObj)
{
/* Display the id of the thread which executes */
/* TestMethod1(). This should be thread_id_1. */
/* That is, it is the id of main()'s thread. */
p_ClassVBSTACOMObj -> TestMethod1();
/* Release() the proxy interface pointer. */
/* The current ref count of the proxy is returned.*/
/* This ref count may not tally with that of the */
/* original interface pointer. */
p_ClassVBSTACOMObj -> Release();
p_ClassVBSTACOMObj = NULL;
}
pIGlobalInterfaceTable -> Release();
pIGlobalInterfaceTable = NULL;
}
::CoUninitialize();
return 0;
}
/* This function demonstrates a proper way of marshalling an interface */
/* pointer from one apartment to another. */
/* */
/* The method used here involves the Global Interface Table (GIT) which */
/* is used to store interface pointers from various apartments in a process*/
/* -wide repository. */
/* */
/* A thread of a destination apartment can obtain an interface pointer from*/
/* this table. */
/* */
/* What the destination thread receives is actually a proxy to the original*/
/* interface pointer. */
/* */
void DemonstrateInterThreadMarshallingUsingGIT(_ClassVBSTACOMObjPtr& sp_ClassVBSTACOMObj)
{
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
IUnknown* pIUnknown = NULL;
IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
DWORD dwCookie = 0;
/* There is a single instance of the global interface table per process. */
/* Hence all calls in a process to create it will return the same instance. */
/* We can get an interface pointer to this GIT here in this function. Later,*/
/* another thread is able to retrieve the same GIT interface pointer via */
/* another call to ::CoCreateInstance(). */
::CoCreateInstance
(
CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pIGlobalInterfaceTable
);
if (pIGlobalInterfaceTable)
{
/* QI the original interface pointer for its IUnknown interface. */
sp_ClassVBSTACOMObj -> QueryInterface (IID_IUnknown, (void**)&pIUnknown);
if (pIUnknown)
{
/* Register this interface pointer in GIT. */
/* A cookie, identifying the interface pointer*/
/* is returned. */
/* No need to call pIUnknown -> AddRef(). */
/* Another thread can retrieve the pIUnknown */
/* using the cookie. */
pIGlobalInterfaceTable -> RegisterInterfaceInGlobal
(
pIUnknown,
__uuidof(_ClassVBSTACOMObj),
&dwCookie
);
pIUnknown -> Release();
pIUnknown = NULL;
}
}
if (dwCookie)
{
/* Demonstrate the use of GIT to marshal an interface */
/* pointer from one thread to another. */
/* The cookie of the interface pointer is passed as a */
/* parameter to the thread function. */
hThread = CreateThread
(
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)ThreadFunc_MarshalUsingGIT,
(LPVOID)dwCookie,
(DWORD)0,
(LPDWORD)&dwThreadId
);
/* Get this thread to wait until the new thread ends.*/
/* In the meantime, we must continue to process */
/* Windows messages. */
ThreadMsgWaitForSingleObject(hThread, INFINITE);
CloseHandle (hThread);
hThread = NULL;
pIGlobalInterfaceTable -> RevokeInterfaceFromGlobal(dwCookie);
dwCookie = 0;
}
if (pIGlobalInterfaceTable)
{
pIGlobalInterfaceTable -> Release();
pIGlobalInterfaceTable = NULL;
}
}
/* This thread function receives an interface pointer DIRECTLY from */
/* another thread. What we have is not a proxy but a DIRECT pointer */
/* to the original interface pointer. */
DWORD WINAPI ThreadFunc_DangerousTransferOfInterfacePointers(LPVOID lpvParameter)
{
/* Make this an STA thread. */
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/* Display the id of this thread. */
/* Let's say this is thread_id_5. */
DisplayCurrentThreadId();
if (1)
{
/* Directly cast the thread parameter into */
/* a _ClassVBSTACOMObj interface pointer. */
_ClassVBSTACOMObj* p_ClassVBSTACOMObj = (_ClassVBSTACOMObj*)lpvParameter;
/* A crash will occur here when TestMethod1() executes. */
p_ClassVBSTACOMObj -> TestMethod1();
/* Calling Release() will affect the original object's */
/* reference count. */
p_ClassVBSTACOMObj -> Release();
p_ClassVBSTACOMObj = NULL;
}
::CoUninitialize();
return 0;
}
/* This function demonstrates an illegal and dangerous method of transferring */
/* an interface pointer from one apartment to another. */
/* This method will work for an ATL generated COM DLL Server AND a client app */
/* deliberately developed to avoid threading problems. */
/* But for a COM Server created via Visual Basic, a crash will occur when a */
/* method of the interface is invoked. */
void DemonstrateDangerousTransferOfInterfacePointers(_ClassVBSTACOMObjPtr& sp_ClassVBSTACOMObj)
{
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
/* We are going to DIRECTLY pass an _ClassVBSTACOMObj interface */
/* pointer to a thread via thread parameter. */
/* We need to AddRef() the interface pointer before passing it */
/* to a client thread. The client thread must later Release() it.*/
sp_ClassVBSTACOMObj -> AddRef();
hThread = CreateThread
(
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)ThreadFunc_DangerousTransferOfInterfacePointers,
(LPVOID)((_ClassVBSTACOMObj*)sp_ClassVBSTACOMObj),
(DWORD)0,
(LPDWORD)&dwThreadId
);
ThreadMsgWaitForSingleObject(hThread, INFINITE);
CloseHandle (hThread);
hThread = NULL;
}
/* This program aims to demonstrate the transfer of an interface pointer
across apartments. There are 4 methods shown :
1. By way of IStream using low-level APIs.
2. By way of IStream using high-level APIs.
3. By way of the Global Interface Table (GIT).
4. By an incorrect and dangerous way.
*/
int main()
{
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/* Display the current thread id.*/
/* Let's say this is thread_id_1.*/
DisplayCurrentThreadId();
if (1)
{
_ClassVBSTACOMObjPtr sp_ClassVBSTACOMObj;
/* Instantiate coclass ClassVBSTACOMObj */
/* and get its _ClassVBSTACOMObj interface. */
sp_ClassVBSTACOMObj.CreateInstance(__uuidof(ClassVBSTACOMObj));
/* Call its TestMethod1() method. Note the thread id. */
/* This should be thread_id_1. */
sp_ClassVBSTACOMObj -> TestMethod1();
DemonstrateInterThreadMarshallingUsingLowLevelAPI(sp_ClassVBSTACOMObj);
DemonstrateInterThreadMarshallingUsingIStream(sp_ClassVBSTACOMObj);
DemonstrateInterThreadMarshallingUsingGIT(sp_ClassVBSTACOMObj);
DemonstrateDangerousTransferOfInterfacePointers(sp_ClassVBSTACOMObj);
/* Call its TestMethod1() method again. */
/* The thread id displayed is still thread_id_1. */
sp_ClassVBSTACOMObj -> TestMethod1();
}
::CoUninitialize();
return 0;
}