Click here to Skip to main content
15,897,704 members
Articles / Programming Languages / C++

Understanding Custom Marshaling Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (53 votes)
18 Aug 2006CPOL31 min read 209.2K   1.4K   147  
Learn the fundamental principles of COM custom marshaling by code examples.
// VCConsoleClient01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

// Global event object handle that will be signalled when 
// an interface of a custom marshaled object has been
// marshaled into a stream. This will signal a receiving 
// thread that unmarshaling can now be performed.
HANDLE g_hInterfaceMarshaled = NULL;

// ThreadFunc_CustomMarshaledObject() is the entry point function
// of a thread that will create an immutable object, create a
// marshaled data packet for it and pass it to the creator of this
// thread.
//
// This thread will then remain alive until a WM_QUIT message
// is posted to it. The fact that this thread remains alive
// will enable the imutable object to also remain alive.
DWORD WINAPI ThreadFunc_CustomMarshaledObject(LPVOID lpvParameter)
{
  MSG			msg;
  long			lLong = 0;
  LPSTREAM*		ppStreamReceiver = (LPSTREAM*)lpvParameter;

  // This thread will be thread of an STA.
  ::CoInitialize(NULL);
    
  IImmutableObjectFactoryPtr	spIImmutableObjectFactory = NULL;
  IImmutablePtr					spIImmutable = NULL;
  IUnknown*						pIUnknown = NULL;
  
  // Initialize the receiver.
  *ppStreamReceiver = NULL;
    
  // Create an instance of the COM object which has ProgID 
  // "BasicSample01InterfacesImpl.ImmutableObjectFactoryImpl" and manage it
  // via its IImmutableObjectFactory interface.
  // Now, because the resultant object (spIImmutableObjectFactory) is an STA object,
  // and this thread is an STA thread, spIImmutableObjectFactory will live in the 
  // same STA as this thread.
  _CoCreateInstance("BasicSample01InterfacesImpl.ImmutableObjectFactoryImpl", spIImmutableObjectFactory);
    
  // Get the immutable object from spIImmutableObjectFactory.
  // Now because the immutable object is also an STA object, 
  // it will live in the same STA as this thread.
  spIImmutableObjectFactory -> CreateObject(101, (IImmutable**)&spIImmutable);

  // QI the original interface pointer for its IUnknown interface.
  spIImmutable -> QueryInterface (IID_IUnknown, (void**)&pIUnknown);

  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,
      ppStreamReceiver
    );
  
    if (*ppStreamReceiver)
    {
	  LARGE_INTEGER li = { 0 };

      // We use the new stream object to store the marshal data 
      // packet of the immutable object.
      //                                     
      // 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.
      //
	  // When CoMarshalInterface() is called on spIImmutable,
	  // COM will attempt to get the CLSID of the proxy for the 
	  // immutable object and then to get the marshaled data packet 
	  // from it.
	  //
	  // The following sequence of function calls will take place :      
      //
      // CImmutableImpl::GetUnmarshalClass()
      // CImmutableImpl::GetMarshalSizeMax()
      // CImmutableImpl::MarshalInterface()
      // 
      ::CoMarshalInterface
      (
        *ppStreamReceiver,
        __uuidof(IImmutablePtr),
        (IUnknown*)pIUnknown,
        MSHCTX_INPROC,
        NULL,
        MSHLFLAGS_NORMAL
      );

      // We will need to reset the stream to the beginning.
      // Otherwise a later call to the CoUnmarshalInterface() 
      // API will fail with error STG_E_READFAULT.
	  (*ppStreamReceiver) -> Seek(li, STREAM_SEEK_SET, NULL);
    }

	pIUnknown -> Release();
	pIUnknown = NULL;

    // Signal the situation that the immutable object
    // has been marshaled by setting the g_hInterfaceMarshaled
    // event.
    if (g_hInterfaceMarshaled)
    {
      SetEvent(g_hInterfaceMarshaled);
    }
  }

  // Dispatch all windows messages in queue.
  // This windows message loop will not
  // break until the WM_QUIT message is
  // posted to this thread.
  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage (&msg);
    DispatchMessage(&msg);
  }
  
  ::CoUninitialize();
  
  return 0;
}





// ExamineStream() will take a stream object and cast it
// to a MarshaledDataPacket structure for examination.
void ExamineStream(LPSTREAM& lpStream)
{
  LPBYTE  lpbyStreamData = NULL;
  STATSTG statstg;
    
  memset (&statstg, 0, sizeof(statstg));

  lpStream -> Stat
  (
    (STATSTG*)&statstg,
    STATFLAG_DEFAULT
  );
    
  lpbyStreamData = new BYTE[(size_t)(statstg.cbSize.LowPart)];
  memset (lpbyStreamData, 0, (size_t)(statstg.cbSize.LowPart));
  if (lpbyStreamData)
  {
    MarshalDataPacket marshal_data_packet;
    
    memset (&marshal_data_packet, 0, sizeof(marshal_data_packet));
    
    lpStream -> Read
    (
      (void*)lpbyStreamData,
      statstg.cbSize.LowPart,
      NULL
    );
    
    memcpy((void*)&marshal_data_packet, (void*)lpbyStreamData, sizeof(marshal_data_packet));
      
    delete [] lpbyStreamData;
    lpbyStreamData = NULL;
  }
    
  LARGE_INTEGER li;
    
  memset (&li, 0, sizeof(li));
  lpStream -> Seek
  (
    li,
    STREAM_SEEK_SET ,
    NULL
  );
}





// Demonstrate_Cross_Apartment_Call_Via_Stream() will show 
// a complete rendition of importing an interface pointer
// from another apartment.
void Demonstrate_Cross_Apartment_Call_Via_Stream()
{
  HANDLE					hThread = NULL;
  DWORD						dwThreadId = 0;
  // lpStream is an uninitialized pointer to a stream.
  LPSTREAM					lpStream = NULL;
  long						lLongValue = 0;
  // spIImmutable will become a proxy to the immutable object.
  IImmutablePtr				spIImmutable = NULL;
  
  // We create a thread which is started by the function
  // ThreadFunc_CustomMarshaledObject(). We also pass the
  // uninitialized IStream pointer to this thread function
  // as a parameter.
  hThread = CreateThread
  (
    (LPSECURITY_ATTRIBUTES)NULL,
    (SIZE_T)0,
    (LPTHREAD_START_ROUTINE)ThreadFunc_CustomMarshaledObject,
    (LPVOID)(&lpStream),
    (DWORD)0,
    (LPDWORD)&dwThreadId
  );  

  // We wait for the thread to initialize.
  ThreadMsgWaitForSingleObject(g_hInterfaceMarshaled, INFINITE);

  ResetEvent(g_hInterfaceMarshaled);
  
  // By the time the thread is initialized, lpStream will
  // be initialized to point to a stream object that contains
  // a marshaled data packet.
  if (lpStream)
  {
    // We can examine this stream.
    ExamineStream(lpStream);
    
    // Construct a proxy to the IImmutable interface from the stream.
    // 
    // At this point in time, the Marshal Data Packet is contained 
    // inside the stream. All COM needs is a new Proxy object to which 
    // it passes the Marshal Data Packet (in the UnmarshalInterface()
    // method call).
    //     
    // When CoUnmarshalInterface() is called with our marshaled
    // data packet stream, COM will create a proxy to the immutable
    // object created inside the ThreadFunc_CustomMarshaledObject
    // thread.
    //
    // COM will then pass the marshaled data packet stream to the
    // proxy in order to initialize it.
    // 
    // The following sequence of function calls will take place : 
    //
    // CImmutableImpl()	(part of proxy construction)
    // CImmutableImpl::FinalConstruct() (part of proxy construction)
    // CImmutableImpl::UnmarshalInterface()
    //     
    ::CoUnmarshalInterface
    (
      lpStream,
      __uuidof(IImmutablePtr),
      (void**)&spIImmutable
    );
    
    lpStream -> Release();
    lpStream = NULL;
  }

  // At this time, spIImmutable has been initialized to be a proxy
  // of the immutable object.
  if (spIImmutable)
  {   
    spIImmutable -> get_LongValue(&lLongValue);        
  }

  PostThreadMessage(dwThreadId, WM_QUIT, 0, 0);

  ThreadMsgWaitForSingleObject(hThread, INFINITE);

  CloseHandle (hThread);

  hThread = NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
    ::CoInitialize(NULL);
    
    g_hInterfaceMarshaled = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    Demonstrate_Cross_Apartment_Call_Via_Stream();
    
    if (g_hInterfaceMarshaled)
    {
      CloseHandle(g_hInterfaceMarshaled);
	  g_hInterfaceMarshaled = NULL;
    }

    ::CoUninitialize();
    
	return 0;
}

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