//
you're reading...
Programming Issues/Tips

Determining the Type of a Managed Object From Unmanaged Code.

1. Introduction.

1.1 Suppose in unmanaged code, we are passed an IDispatch interface pointer to a managed object.

1.2 How can we know the type of this managed object ?

1.3 This blog demonstrates a technique to determine this by presenting a helper function in the next section.

1.4 In section 3, I will demonstrate the helper function in action via some sample test code.

2. The Helper Function.

2.1 The function is listed below :

#include
#include
#import "mscorlib.tlb" raw_interfaces_only no_implementation

void CheckTypeOfManagedObject(/*[in]*/ IDispatch* lpDispatch, /*[out]*/ std::string& strTypeReceiver)
{
	if (lpDispatch == NULL)
	{
	  return;
	}

	DISPPARAMS dispparms;
	VARIANTARG varReturn;
	HRESULT hr;

	VariantInit(&varReturn);

	memset(&dispparms, 0, sizeof(DISPPARAMS));
	dispparms.cArgs = 0;

	// Obtain the ID of the GetType() method.
	OLECHAR FAR* rgszNames = L"GetType";
	DISPID dispid = 0;
	EXCEPINFO excepinfo;

	hr = lpDispatch -> GetIDsOfNames
	(
	  IID_NULL,
	  &rgszNames,
	  1,
	  LOCALE_SYSTEM_DEFAULT,
	  &dispid
	);

	if (!SUCCEEDED(hr))
	{
		return;
	}

         // Call GetType().
	UINT nErrArg;
	hr = lpDispatch -> Invoke
         (
           dispid,
           IID_NULL,
           LOCALE_SYSTEM_DEFAULT,
           DISPATCH_METHOD,
           &dispparms,
           &varReturn,
           &excepinfo,
           &nErrArg
         );

	if (!SUCCEEDED(hr))
	{
	  return;
	}	

	// The returned VARIANT should contain an IUnknown pointer
	// to a managed object of type "Type", i.e. a Type object.
	if (V_VT(&varReturn) != VT_UNKNOWN)
	{
	  // If not return immediately.
	  VariantClear(&varReturn);
	  return;
	}

	// If so, get hold of the IUnknown interface pointer
	// of the Type object.
	IUnknownPtr spIUnknownForManagedType = V_UNKNOWN(&varReturn);
	VariantClear(&varReturn);

	// Using the IUnknown interface pointer, QI() for the _Type
	// interface which is the COM-visible interface of the
	// managed Type object.
	mscorlib::_TypePtr sp_Type = NULL;

	hr = spIUnknownForManagedType -> QueryInterface(&sp_Type);
	if (!SUCCEEDED(hr))
	{
	  return;
	}

	// Now, get the string description of the Type object.
	// This will reveal the name of the type of the managed
	// object whose IDispatch pointer was passed to this funtion.
	BSTR bstrToString = NULL;

	sp_Type-> get_ToString(&bstrToString);

	if (bstrToString)
	{
	  strTypeReceiver = (char*)_bstr_t(bstrToString, true);
	  ::SysFreeString(bstrToString);
	  bstrToString = NULL;
	}
}

2.2 The basic premise behind CheckTypeOfManagedObject() is that each managed object is derived from “System.Object” :

[SerializableAttribute]
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
public class Object

Each managed object, when marked as COM-visible, will automatically expose public methods of “System.Object” type. This includes the “GetType()” method.

2.3 “GetType()” returns an instance of the managed “Type” class :

[SerializableAttribute]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[ComVisibleAttribute(true)]
public abstract class Type : MemberInfo, _Type, IReflect

2.4 We make use of the “_Type” interface which is COM-visible :

[CLSCompliantAttribute(false)]
[GuidAttribute("BCA8B44D-AAD6-3A86-8AB7-03349F4F2DA2")]
[ComVisibleAttribute(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface _Type

2.5 The _Type method which is useful to us for obtaining the managed type of a COM-imported managed object is “ToString()”.

3. Example Usage.

3.1 To demonstrate an effective use of the CheckTypeOfManagedObject() function, I created a DLL which exports an API named GetManagedObjectType() with the following signature :

const char* __stdcall GetManagedObjectType(/*[in]*/ IDispatch* pIDispatch);

3.2 This API internally uses the CheckTypeOfManagedObject() function to determine the type of the managed object as a string. The following is an implementation :

const char* __stdcall GetManagedObjectType(/*[in]*/ IDispatch* pIDispatch)
{
  std::string strTypeReceiver;

  CheckTypeOfManagedObject(pIDispatch, strTypeReceiver);

  if (strTypeReceiver.empty())
  {
    return NULL;
  }
  else
  {
    char* lpszRet = (char*)::CoTaskMemAlloc(strTypeReceiver.length() + 1);

    strcpy(lpszRet, strTypeReceiver.c_str());

    return lpszRet;
  }
}

3.3 The following is a sample C# code for a console program that demonstrates a use of the GetManagedObjectType() API :

[DllImport(@"TestDLL.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetManagedObjectType([In] IntPtr pIDispatch);

static void DisplayObjectType()
{
  string str = "some string";
  IntPtr pDispatch = Marshal.GetIDispatchForObject(str);
  Console.WriteLine("{0:S}", GetManagedObjectType(pDispatch));
}

static void Main(string[] args)
{
  DisplayObjectType();
  Console.ReadKey();
}

3.4 When the above C# program is run, the following output will be displayed on the console window :

System.String

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: