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

Bringing DCOM remoting functionality to Windows CE and .NET CF2.0

, 17 Apr 2006
This article shows how to use DCOM on Windows CE 5.0. We will add full DCOM rich error information, and implement a DCOM interface between a Windows XP .NET 2.0 client and Windows CE DCOM server. With this code, it is possible to code .NET remoting alike functionality through DCOM interop.
///////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2005 Werner Willemsens
//
//  Literature:
//     http://www.microsoft.com/msj/0198/activex0198.aspx
//     Inside Distributed COM, Guy & Henry Eddon : Chapter 12 (MEOW:-)
//     Professional DCOM programming, Dr Richard Grimes
//
//  Tooling (Without I could never have done this)
//     Ethereal - Network Protocol Analyzer (Gerald Combes)
//
///////////////////////////////////////////////////////////////////////

#include "channelhook.h"
#include <initguid.h>
#include <assert.h>

STDMETHODIMP Hook::QueryInterface(REFIID riid, void **ppv)
{
    if (riid == IID_IUnknown)
        *ppv = static_cast<IUnknown*>(this);
    else if (riid == IID_IUnknown)
        *ppv = static_cast<IChannelHook*>(this);
    else
        return (*ppv = 0), E_NOINTERFACE;
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) Hook::AddRef(void)
{
    return 2;
}

STDMETHODIMP_(ULONG) Hook::Release(void)
{
    return 1;
}

// called in client prior to making a call
STDMETHODIMP_(void) Hook::ClientGetSize(REFGUID uExtent, REFIID riid, ULONG *pDataSize)
{
    assert(uExtent == IID_IMarshalRichError);
    return;
}

// called in client prior to making a call
STDMETHODIMP_(void) Hook::ClientFillBuffer(REFGUID uExtent, REFIID riid, ULONG *pDataSize, void *pDataBuffer)
{
    assert(uExtent == IID_IMarshalRichError);
    return;
}

// called in client just after a call completes
STDMETHODIMP_(void) Hook::ClientNotify(REFGUID uExtent, REFIID riid, ULONG cbDataSize, void *pDataBuffer, DWORD lDataRep, HRESULT hrFault)
{
    assert(uExtent == IID_IMarshalRichError);
    assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);

	if (cbDataSize)
	{
		GUID	Guid;
		_bstr_t	sDescription;
		_bstr_t	sSource;
		_bstr_t	sHelpFile;
		DWORD	dwHelpContext;

		// First we use a piece of the OBJREF structure, which makes life a bit easier
		OBJREF *objref = (OBJREF*)pDataBuffer;

		unsigned long offset = 0;
		byte *pData = &(objref->u_objref.u_custom.Data);
		offset += 0;

		// First a "0"
		offset += sizeof(unsigned long);

		// HelpContext
		dwHelpContext = *(DWORD*)(pData + offset);
		offset += sizeof(DWORD);

		// GUID
		memcpy(&Guid, pData + offset, sizeof(GUID));
		offset += sizeof(GUID);

		// Source
		if (*(unsigned long*)(pData + offset) == 0xFFFFFFFF)
		{
			offset += sizeof(unsigned long);
			offset += sizeof(unsigned long);

			if (*(unsigned long*)(pData + offset) == 0x00000000L)
			{
				offset += sizeof(unsigned long);
				unsigned int len = *(unsigned long*)(pData + offset);
				offset += sizeof(unsigned long);
				sSource = (TCHAR*)(pData + offset);
				offset += (len + 0)*sizeof(TCHAR);
			}
		}
		else
		{
			offset += sizeof(unsigned long);
		}

		// Description
		if (*(unsigned long*)(pData + offset) == 0xFFFFFFFF)
		{
			offset += sizeof(unsigned long);
			offset += sizeof(unsigned long);

			if (*(unsigned long*)(pData + offset) == 0x00000000L)
			{
				offset += sizeof(unsigned long);
				unsigned int len = *(unsigned long*)(pData + offset);
				offset += sizeof(unsigned long);
				sDescription = (TCHAR*)(pData + offset);
				offset += (len + 0)*sizeof(TCHAR);
			}
		}
		else
		{
			offset += sizeof(unsigned long);
		}

		// HelpFile
		if (*(unsigned long*)(pData + offset) == 0xFFFFFFFF)
		{
			offset += sizeof(unsigned long);
			offset += sizeof(unsigned long);

			if (*(unsigned long*)(pData + offset) == 0x00000000L)
			{
				offset += sizeof(unsigned long);
				unsigned int len = *(unsigned long*)(pData + offset);
				offset += sizeof(unsigned long);
				sHelpFile = (TCHAR*)(pData + offset);
				offset += (len + 0)*sizeof(TCHAR);
			}
		}
		else
		{
			offset += sizeof(unsigned long);
		}

		HRESULT hr = S_OK;

		// Create the Error object
		ICreateErrorInfoPtr iCreateErrorInfo;
		hr = CreateErrorInfo (&iCreateErrorInfo);
	    if (hr == S_OK)  // Recreate the Error object in the current thread context
		{
			hr = iCreateErrorInfo->SetGUID(Guid);
			hr = iCreateErrorInfo->SetDescription(sDescription.GetBSTR());
			hr = iCreateErrorInfo->SetSource(sSource.GetBSTR());
			hr = iCreateErrorInfo->SetHelpFile(sHelpFile.GetBSTR());
			hr = iCreateErrorInfo->SetHelpContext(dwHelpContext);

			IErrorInfoPtr iErrorInfo(iCreateErrorInfo);
			hr = SetErrorInfo(0, iErrorInfo);
		}
	}

    return;
}

// called in server just prior to invoking a call
STDMETHODIMP_(void) Hook::ServerNotify(REFGUID uExtent, REFIID riid, ULONG cbDataSize, void *pDataBuffer, DWORD lDataRep)
{
    assert(uExtent == IID_IMarshalRichError);
    assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
    return;
}

// called in server just after invoking a call
STDMETHODIMP_(void) Hook::ServerGetSize(REFGUID uExtent, REFIID riid, HRESULT hrFault, ULONG *pDataSize)
{
    assert(uExtent == IID_IMarshalRichError);

	IErrorInfoPtr iErrorInfo;
	HRESULT hr = GetErrorInfo(0, &iErrorInfo);
    if (hr == S_OK) // Yep, there is an error, tell the IChannelHook
	{
		// First remember what the error was
		BSTR bstr;
		hr = iErrorInfo->GetGUID(&m_Guid);
		hr = iErrorInfo->GetDescription(&bstr);		m_sDescription = bstr;
		hr = iErrorInfo->GetSource(&bstr);			m_sSource = bstr;
		hr = iErrorInfo->GetHelpFile(&bstr);		m_sHelpFile = bstr;
		hr = iErrorInfo->GetHelpContext(&m_dwHelpContext);

		// Determine the size we need for sending it in an DCERPC message
		int nSource      = sizeof(unsigned long);
		int nDescription = sizeof(unsigned long);
		int nHelpFile    = sizeof(unsigned long);
		if (m_sSource.length() > 0)      nSource      = 4*sizeof(unsigned long) + (m_sSource.length()      + 1)*sizeof(_TCHAR); 
		if (m_sDescription.length() > 0) nDescription = 4*sizeof(unsigned long) + (m_sDescription.length() + 1)*sizeof(_TCHAR);
		if (m_sHelpFile.length() > 0)    nHelpFile    = 4*sizeof(unsigned long) + (m_sHelpFile.length()    + 1)*sizeof(_TCHAR);

		*pDataSize = sizeof(unsigned long) +		// signature
			         sizeof(unsigned long) +		// flags
					 sizeof(GUID) +					// IUnknown
					 sizeof(CLSID) +				// Unmarshalling code for rich error
			         sizeof(unsigned long) +		// Size of extension data
			         sizeof(unsigned long) +		// Size of data
			         sizeof(unsigned long) +		// 0?
			         sizeof(DWORD) +				// HelpContext
					 sizeof(GUID) +					// Guid
					 nSource +						// Source
					 nDescription +					// Description
					 nHelpFile +					// Source
			         sizeof(unsigned long);			// BAADF00D
	} else
	{
		*pDataSize = 0;
	}

    return;
}

// called in server just after invoking a call
STDMETHODIMP_(void) Hook::ServerFillBuffer(REFGUID uExtent, REFIID riid, ULONG *pDataSize, void *pDataBuffer, HRESULT hrFault)
{
	assert(uExtent == IID_IMarshalRichError);

	// Now we are going to copy the data.
	// Note that MS used custom marshalling (flags = 4) for this.

	// First we use a piece of the OBJREF structure, which makes life a bit easier
	OBJREF *objref = (OBJREF*)pDataBuffer;
	objref->signature                     = 0x574F454DL;	// MEOW
	objref->flags                         = 0x00000004L;	// Custom marshalling
	objref->iid                           = IID_IUnknown;
	objref->u_objref.u_custom.clsid       = IID_IUnmarshalRichError;
	objref->u_objref.u_custom.cbExtension = 0x00000000L;	// No extension, I think
//	objref->u_objref.u_custom.size        = size of data;	// see further

	// Next we need to add our custom marshalling code for IErrorInfo,
	// which we gracefully reverse-engineered by sniffing some DCERPC packets
	unsigned long offset = 0;
	byte *pData = &(objref->u_objref.u_custom.Data);
	offset += 0;

	// First a "0"
	*(unsigned long*)(pData + offset) = 0x00000000L;
	offset += sizeof(unsigned long);

	// HelpContext
	*(DWORD*)(pData + offset) = m_dwHelpContext;
	offset += sizeof(DWORD);

	// GUID
	memcpy(pData + offset, &m_Guid, sizeof(GUID));
	offset += sizeof(GUID);

	// Source
	if (m_sSource.length() > 0)
	{
		*(unsigned long*)(pData + offset) = 0xFFFFFFFFL;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sSource.length() + 1;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sSource.length() + 1;
		offset += sizeof(unsigned long);
		memcpy(pData + offset, (TCHAR*)m_sSource, (m_sSource.length() + 1)*sizeof(TCHAR));
		offset += (m_sSource.length() + 1)*sizeof(TCHAR);
	}
	else
	{
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
	}

	// Description
	if (m_sDescription.length() > 0)
	{
		*(unsigned long*)(pData + offset) = 0xFFFFFFFFL;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sDescription.length() + 1;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sDescription.length() + 1;
		offset += sizeof(unsigned long);
		memcpy(pData + offset, (TCHAR*)m_sDescription, (m_sDescription.length() + 1)*sizeof(TCHAR));
		offset += (m_sDescription.length() + 1)*sizeof(TCHAR);
	}
	else
	{
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
	}

	// HelpFile
	if (m_sHelpFile.length() > 0)
	{
		*(unsigned long*)(pData + offset) = 0xFFFFFFFFL;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sHelpFile.length() + 1;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
		*(unsigned long*)(pData + offset) = m_sHelpFile.length() + 1;
		offset += sizeof(unsigned long);
		memcpy(pData + offset, (TCHAR*)m_sHelpFile, (m_sHelpFile.length() + 1)*sizeof(TCHAR));
		offset += (m_sHelpFile.length() + 1)* sizeof(TCHAR);
	}
	else
	{
		*(unsigned long*)(pData + offset) = 0x00000000L;
		offset += sizeof(unsigned long);
	}

	// Now we now the size of the custom data
	objref->u_objref.u_custom.size        = offset;

	// Just to make it look completely Microsoftiesh
	*(unsigned long*)(pData + offset) = 0xBAADF00DL;
	offset += sizeof(unsigned long);

	return;
}

// One time instantiation of the Captain Hook class
static Hook hook;

bool StartChannelHook (void)
{
	// WINOLEAPI CoRegisterChannelHook( REFGUID ExtensionUuid, IChannelHook *pChannelHook );
	HRESULT hr = CoRegisterChannelHook(IID_IMarshalRichError, &hook);
	if (FAILED(hr))
		return false;
	return true;
}

bool StopChannelHook (void)
{
	// WINOLEAPI CoRegisterChannelHook( REFGUID ExtensionUuid, IChannelHook *pChannelHook );
	HRESULT hr = CoRegisterChannelHook(IID_IMarshalRichError, NULL);
	if (FAILED(hr))
		return false;
	return true;
}

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

Werner Willemsens
Team Leader
Belgium Belgium
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140921.1 | Last Updated 17 Apr 2006
Article Copyright 2006 by Werner Willemsens
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid