Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Understanding The COM Single-Threaded Apartment Part 2

, 18 Feb 2005
Learn the fundamental principles of the COM Single-Threaded Apartment Model by code examples.
ccomthread2_src.zip
SimpleCOMObject1
SimpleCOMObject2
Test Programs
VBSTACOMObj
Shared
SimpleCOMObject1.def
SimpleCOMObject1.dsp
SimpleCOMObject1.dsw
SimpleCOMObject1.rgs
SimpleCOMObject1ps.def
SimpleCOMObject1ps.mk
SimpleCOMObject1.tlb
SimpleCOMObject2.def
SimpleCOMObject2.dsp
SimpleCOMObject2.dsw
SimpleCOMObject2.rgs
SimpleCOMObject2ps.def
SimpleCOMObject2ps.mk
SimpleCOMObject2.tlb
VCTests
VBTest
DemonstrateSTAInterThreadMarshalling
VCTest02
VCTest01
VCTest02.dsp
VCTest02.dsw
VBSTACOMObj.tlh
VBSTACOMObj.tli
VCTest01.dsp
VCTest01.dsw
SimpleCOMObject2.tlh
SimpleCOMObject2.tli
FormMain.frm
VBTest.exe
VBTest.vbw
VBTest.vbp
VBSTACOMObj.dll
VBSTACOMObj.exp
VBSTACOMObj.lib
VBSTACOMObj.vbw
VBSTACOMObj.cls
VBSTACOMObj.vbp
// ComThread.h : Declaration of the CComThread

#ifndef __CCOMTHREAD_H_
#define __CCOMTHREAD_H_

#include <windows.h>
#include <vector>
using namespace std;





typedef vector<LPSTREAM> ISTREAM_VECTOR;
typedef vector<LPUNKNOWN> IUNKNOWN_VECTOR;





class CComThread
{
public :
    enum Flags
    {
      FLAG_START_SUSPENDED = 0x00000001
    };

    CComThread() :
	  m_hThread(NULL),
	  m_lpStartAddress(NULL),
	  m_lpParam(NULL),
	  m_Flags((Flags)0),
	  m_dwThreadId(0)
    {
	  InitializeCriticalSection(&m_csStreamVectorAccess);
    }
    
    ~CComThread()
    {
      if (m_hThread)
      {
        CloseHandle (m_hThread);
	    m_hThread = NULL;
      }

	  ClearVectorStream();

	  ClearVectorUnknown();

	  DeleteCriticalSection(&m_csStreamVectorAccess);
    }

	void SetStartAddress (LPTHREAD_START_ROUTINE lpStartAddress)
    {
      m_lpStartAddress = lpStartAddress;
    }

	void SetThreadParam (void* lpParam)
    {
      m_lpParam = lpParam;
    }

	void* GetThreadParam()
    {
      return m_lpParam;
    }

	void SetFlags (Flags FlagsSet)
    {
      m_Flags = FlagsSet;
    }

    long ThreadStart()
    {
      long lRet = 0;

      if (m_hThread)
      {
        CloseHandle(m_hThread);
	    m_hThread = NULL;
      }

      m_hThread = (HANDLE)CreateThread
      (
        (LPSECURITY_ATTRIBUTES)NULL, // SD
        (SIZE_T)0,                       // initial stack size
        (LPTHREAD_START_ROUTINE)(CComThreadStartFunction),    // thread function
        (LPVOID)this,                       // thread argument
        (DWORD)((m_Flags & FLAG_START_SUSPENDED) ? CREATE_SUSPENDED : 0), // creation option
        (LPDWORD)&m_dwThreadId // thread identifier
      );

      return lRet;
    }

    long ThreadSuspend()
    {
      long lRet = 0;

      if (m_hThread)
      {
        lRet = (long)SuspendThread(m_hThread);
      }

      return lRet;
    }

    long ThreadResume()
    {
      long lRet = 0;

      lRet = (long)ResumeThread(m_hThread);

      return lRet;
    }

	long WaitThreadStop()
	{
	  HANDLE		dwChangeHandles[1];
	  BOOL			bContinueLoop = TRUE;
	  DWORD			dwWaitStatus = 0;
	  long			lRet = 0;

	  dwChangeHandles[0] = m_hThread;

      // Msg Loop while waiting for thread to exit.
      while (bContinueLoop) 
      {
	    // Wait for notification.
		dwWaitStatus = ::MsgWaitForMultipleObjectsEx
		(
          (DWORD)1,          // number of handles in array
          dwChangeHandles, // object-handle array
          (DWORD)INFINITE,  // time-out interval
          (DWORD)(QS_ALLINPUT | QS_ALLPOSTMESSAGE),      // input-event type
          (DWORD)(MWMO_INPUTAVAILABLE)          // wait options
        );
 
        switch (dwWaitStatus) 
        { 
          // First wait (thread exit) object has been signalled.
		  case WAIT_OBJECT_0 : 
		  {
            // Flag to indicate stop loop.
		    bContinueLoop = FALSE;
		    break;
		  }
 
          // Windows message has arrived.
          case WAIT_OBJECT_0 + 1: 
		  {
		    MSG msg;

            // Dispatch all windows messages in queue.
		    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		    {
			  TranslateMessage (&msg);
			  DispatchMessage(&msg);
		    }

		    break; 
		  }
 
          default: 
		  {
		    break;
		  }
        } 
      }

	  return lRet;
	}

	long WaitThreadStop2()
	{
	  long lRet = 0;

	  WaitForSingleObject(m_hThread, INFINITE);

	  return lRet;
	}

    long ClearVectorStream()
    {
      long lRet = 0;

	  EnterCriticalSection(&m_csStreamVectorAccess);
 
      // No need to Release() IStream pointers in m_vectorStream. 
      // They have all been Release()'d previously when we called 
      // CoGetInterfaceAndReleaseStream().
      m_vectorStream.clear();

	  LeaveCriticalSection(&m_csStreamVectorAccess);

      return lRet;
    }

    long ClearVectorUnknown()
    {
      IUNKNOWN_VECTOR::iterator	theIterator;
      int						iIndex = 0;
      long						lRet = 0;

      for (theIterator = m_vectorUnknown.begin(); theIterator != m_vectorUnknown.end(); theIterator++)
      {
	    IUnknown* pIUnknown = (*theIterator);

	    if (pIUnknown)
	    {
	      pIUnknown -> Release();
		  pIUnknown = NULL;
	    }
      }

      m_vectorUnknown.clear();

      return lRet;
    }

    // Note that AddUnknownPointer() must be called outside the thread.
	long AddUnknownPointer(LPUNKNOWN& lpUnknown)
	{
	  IStream*		pIStreamTemp = NULL;
      HRESULT		hrTemp = S_OK;
      long			lRet = 0;

	  EnterCriticalSection(&m_csStreamVectorAccess);

	  if (lpUnknown)
	  {
        // Create stream for marshaling lpUnknown to thread.
	    hrTemp = ::CoMarshalInterThreadInterfaceInStream
	    (
	      IID_IUnknown,	// interface ID to marshal
		  lpUnknown,		// ptr to interface to marshal
		  &pIStreamTemp		// output variable
	    );

	    // Place stream in member variable where COM thread will look for it.
	    // No need to call Release() on pStream. They will be Release()'d
	    // when we later call CoGetInterfaceAndReleaseStream().
	    if (pIStreamTemp)
	    {
	      m_vectorStream.push_back(pIStreamTemp);
	    }
	  }

	  LeaveCriticalSection(&m_csStreamVectorAccess);

	  return lRet;
	}

    // Note that UnMarshallInterfaces() must only be called in the thread (managed by this object).
    long UnMarshallInterfaces ()
    {
      ISTREAM_VECTOR::iterator	theIterator;
      int						iIndex = 0;
      HRESULT					hrTemp = S_OK;
      long						lRet = 0;

	  EnterCriticalSection(&m_csStreamVectorAccess);

      // Unmarshal interface pointers
	  for (theIterator = m_vectorStream.begin(); theIterator != m_vectorStream.end(); theIterator++)
      {
        IUnknown*	pIUnknownTemp = NULL;
        IStream*	pIStreamTemp = NULL;

        // Get stream pointer from array where owner has placed it.
        pIStreamTemp = (*theIterator);

        if (pIStreamTemp)
	    {
          // Use stream pointer to create IUnknown that we can call from this thread.
	      hrTemp = ::CoGetInterfaceAndReleaseStream
	      (
	        pIStreamTemp,				// stream containing marshaling info
	        IID_IUnknown,		// interface desired
	        (void**)&pIUnknownTemp	// output variable
	      );

		  // Note that at this time, pIStreamTemp will be Release()'d and will no longer
		  // be valid.

	      // Put resulting IUnknown in IUnknown pointers vector.
	      if (pIUnknownTemp)
	      {
	        m_vectorUnknown.push_back(pIUnknownTemp);
	        pIUnknownTemp -> AddRef();  // Since we have added pIUnknownTemp into a collection, we have an additional reference to it.
	      }
	    }
      }

      // Once all the streams in the stream vector has been unmarshalled,
	  // we no longer need the streams vector (the streams have been Release()'d
	  // anyway and so are no longer valid).
	  ClearVectorStream();

	  LeaveCriticalSection(&m_csStreamVectorAccess);

      return lRet;
    }

    IUNKNOWN_VECTOR& GetUnknownVector()
	{
	  return m_vectorUnknown;
	}

	operator IUNKNOWN_VECTOR&() 
	{
	  return GetUnknownVector();
	}

protected :

    static DWORD WINAPI CComThreadStartFunction(LPVOID lpThreadParameter)
    {
      CComThread* pCComThread = (CComThread*)lpThreadParameter;
      DWORD dwRet = 0;

      CoInitialize(NULL);

	  // At this point in time, the thread managed by CComThread has just started.
	  // We take this opportunity to unmarshall the interfaces stored in m_mapStream.
	  pCComThread -> UnMarshallInterfaces(); 

      dwRet = (pCComThread -> m_lpStartAddress)(/*pCComThread -> m_lpParam*/ pCComThread);

      // Once user thread has completed, we clear the Stream and IUnknown vectors.
	  pCComThread -> ClearVectorStream();

	  pCComThread -> ClearVectorUnknown();

      CoUninitialize();

      return dwRet;
    }

protected :
	HANDLE					m_hThread;
	LPTHREAD_START_ROUTINE	m_lpStartAddress;
	void*					m_lpParam;
	Flags					m_Flags;
	DWORD					m_dwThreadId;
	CRITICAL_SECTION		m_csStreamVectorAccess;

	ISTREAM_VECTOR			m_vectorStream;
	IUNKNOWN_VECTOR			m_vectorUnknown;
};





DWORD ThreadMsgWaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);





#endif  // __CCOMTHREAD_H_




By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Lim Bio Liong
Web Developer
Singapore Singapore
Lim Bio Liong is a Specialist at a leading Software House in Singapore.
 
Bio has been in software development for over 10 years. He specialises in C/C++ programming and Windows software development.
 
Bio has also done device-driver development and enjoys low-level programming. Bio has recently picked up C# programming and has been researching in this area.

| Advertise | Privacy | Mobile
Web04 | 2.8.140905.1 | Last Updated 19 Feb 2005
Article Copyright 2005 by Lim Bio Liong
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid