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

Understanding Custom Marshaling Part 1

, 18 Aug 2006 CPOL
Learn the fundamental principles of COM custom marshaling by code examples.
custommarshaling01_src.zip
utilities
BasicSample01
Interfaces
Implementations
Clients
BasicSample01Interfaces
BasicSample01Interfaces.aps
BasicSample01Interfaces.def
BasicSample01Interfaces.ncb
BasicSample01Interfaces.rgs
BasicSample01Interfacesps.def
Immutable.rgs
ImmutableObjectFactory.rgs
BasicSample01Interfaces.vcproj.vspscc
BasicSample01InterfacesPS.vcproj.vspscc
BasicSample01InterfacesImpl
BasicSample01InterfacesImpl.aps
BasicSample01InterfacesImpl.def
BasicSample01InterfacesImpl.rgs
BasicSample01InterfacesImplps.def
ImmutableImpl.rgs
ImmutableObjectFactoryImpl.rgs
BasicSample01InterfacesImpl.ncb
BasicSample01InterfacesImpl.vcproj.vspscc
BasicSample01InterfacesImplPS.vcproj.vspscc
VCConsoleClient01
VCConsoleClient03
VCConsoleClient02
VCConsoleClient01.ncb
VCConsoleClient03.ncb
VCConsoleClient02.ncb
// 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)

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 | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 18 Aug 2006
Article Copyright 2006 by Lim Bio Liong
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid