Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / ATL

Understanding The COM Single-Threaded Apartment Part 2

Rate me:
Please Sign up or sign in to vote.
4.90/5 (57 votes)
18 Feb 2005CPOL71 min read 256.3K   2.2K   169  
Learn the fundamental principles of the COM Single-Threaded Apartment Model by code examples.
// 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)


Written By
Systems Engineer NEC
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.

Comments and Discussions