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

Misunderstanding IntPtr and System.Object.

1. Introduction.

1.1 Some time ago, someone from the MSDN forum asked for advise on a problem that he was facing.

1.2 He had created a COM interface that serves as a notifier of events. A receiver of the events must implement a notification method and then connect with the notification interface via a method that takes the following form :

HRESULT RegisterNotification([in] IDispatch* lpDispatch);

An interesting part of his design of the notification process is that the event receiver implement only the IDispatch interface and not some specific interface. We shall discuss a little about this in the next section (see point 2.3).

1.3 His client application is written in C# and the notification receiver class is a C# class.

1.4 In his C# client application, he creates an instance of the COM notification object and an instance of the notification receiver C# class.

1.5 Then, noting that the RegisterNotification() method takes a pointer to an IDispatch interface, he used Marshal.GetIDispatchForObject() to obtain the IDispatch interface from the notification receiver object.

1.6 He then passed the IDispatch interface pointer (an IntPtr) to the RegisterNotification() method. Everything went well during compilation. Everything went well at runtime. However, the client application could not receive any event.

1.7 The forum member then asked whether the problem was due to his not using the IConnectionPoint protocol as well as the .NET events/delegates technology for connecting with COM events. He also wondered if, in not using the IConnectionPoint protocol, he should at least define a full COM interface for receiving events.

1.8 The answer to his questions in point 1.7 are all negative. They are not the cause of the problem. The technique that he used to fire events to clients, i.e. the event interface being passed by a method (RegisterNotification()) as well as the use of the IDispatch interface for event notification, are perfectly fine. There is no compulsion to use the IConnectionPoint interface.

1.9 This blog discusses the advantage of using the IDispatch interface for event firing as well as determining the real problem that hindered events from being fired to the client. As it turned out, the answer to the event failure is ridiculously simple but deceptively subtle. Please read on.

2. Test Interfaces and Implementations.

2.1 In order to demonstrate the problem, I have developed my own simple test interfaces. Listed below is the IDL definition for INotificationServer which is to be implemented by a coclass that serves as the event notifier :

[
	object,
	uuid(060F5022-78A9-42FD-AC7F-45186D53D489),
	dual,
	nonextensible,
	helpstring("INotificationServer Interface"),
	pointer_default(unique)
]
interface INotificationServer : IDispatch
{
	[id(1), helpstring("method RegisterNotification")] HRESULT RegisterNotification([in] IDispatch* lpDispatch);
	[id(2), helpstring("method TestNofity")] HRESULT TestNofity([in] LONG value);
};

2.2 The interface contains the RegisterNotification() method which we have met in the last section. Its purpose is to be given an IDispatch interface pointer that points to the object which is to be notified of events. The interface also contains a second method named “TestNotify()” the purpose of which is to perform an immediate firing of an event to the receiver.

2.3 Listed below is the signature of the expected event method :

HRESULT Notify([in] LONG value);

The event receiver is expected to implement the IDispatch interface and to contain the above “Notify()” method. Note that this method need not be tied to any particular DISPID. This is actually quite an elegant solution because the event receiver need not be bound to any particular interface. Over time, other events may be added without breaking any interface. This is the essence of late-binding.

2.4 Since the client application is written in C#, the event receiver must be a C# class that is COM-visible and implements IDispatch. We shall discuss more on the event receiver class later (see point 3.3).

2.5 I have implemented the INotificationServer in ATL :

class ATL_NO_VTABLE CNotificationServer :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CNotificationServer, &CLSID_NotificationServer>,
	public IDispatchImpl<INotificationServer, &IID_INotificationServer, &LIBID_TestCOMObjects01Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CNotificationServer() :
		m_spIDispatch(NULL)
	{
	}

	~CNotificationServer()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_NOTIFICATIONSERVER)

BEGIN_COM_MAP(CNotificationServer)
	COM_INTERFACE_ENTRY(INotificationServer)
	COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

public:

	STDMETHOD(RegisterNotification)(IDispatch* lpDispatch);
	STDMETHOD(TestNofity)(LONG value);

protected :
	IDispatchPtr	m_spIDispatch;
};

2.6 The implementation for the RegisterNotification() method is listed below :

STDMETHODIMP CNotificationServer::RegisterNotification(IDispatch* lpDispatch)
{
	// TODO: Add your implementation code here
	if (lpDispatch == NULL)
	{
		return E_POINTER;
	}

	if (m_spIDispatch)
	{
		// If m_spIDispatch is not null,
		// release it first.
		m_spIDispatch = NULL;
	}

	lpDispatch -> QueryInterface(IID_IDispatch, (void**)(&m_spIDispatch));

	return S_OK;
}

2.7 The implementation for the TestNofity() method is listed below :

STDMETHODIMP CNotificationServer::TestNofity(LONG value)
{
	// TODO: Add your implementation code here
	if (m_spIDispatch == NULL)
	{
		return E_FAIL;
	}

	DISPPARAMS dispparms;
	VARIANTARG varArg;
	VARIANTARG varReturn;
	HRESULT hr;

	VariantInit(&varReturn);
	VariantInit(&varArg);
	V_VT(&varArg) = VT_I4;
	V_I4(&varArg) = value;	

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

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

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

	if (SUCCEEDED(hr))
	{
          // Finally making the call
	  UINT nErrArg;
	  hr = m_spIDispatch -> Invoke
          (
            dispid,
            IID_NULL,
            LOCALE_SYSTEM_DEFAULT,
            DISPATCH_METHOD,
            &dispparms,
            &varReturn,
            &excepinfo,
            &nErrArg);
	}

	return hr;
}

As can be seen in its implementation, CNotificationServer::TestNofity() uses pure late binding (IDispatch::Invoke()) to invoke the client’s Notify() method.

2.8 The ATL project is then complied into TestCOMObjects01.dll.

3. The Client Code.

3.1 In this section, we step through the client code starting from object instantiation through event connection through test notification. The C# client application is listed below in full. Note that it is a modification of the actual one provided by the forum member. It has been modified for simplification :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using interop.TestCOMObjects01;

namespace CSConsoleClient01
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class MyNotificationClient
    {
        public void Notify(int value)
        {
            Console.WriteLine("Notify. Value == {0:D}.", value);
        }
    }

    class Program
    {
        static void TestNotifyByIDispatch()
        {
            NotificationServerClass notification_server = new NotificationServerClass();
            MyNotificationClient notification_client = new MyNotificationClient();

            IntPtr pDispatch = Marshal.GetIDispatchForObject(notification_client);

            notification_server.RegisterNotification(pDispatch);

            notification_server.TestNofity(100);
        }

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

3.2 For the client application, I have manually generated an interop assembly “interop.TestCOMObjects01.dll” using TLBIMP.EXE. This interop assembly is then referenced by the C# client application (hence the “using interop.TestCOMObjects01” statement).

3.3 Note the “MyNotificationClient” class. It is marked with the “ComVisibleAttribute” with value “true”. It is also marked with the “ClassInterfaceAttribute” indicating that it implements the IDispatch interface. Its public “Notify()” method matches the signature of the expected COM event method. The implementation of “Notify()” simply displays “value” on the console window.

3.4 We now examine the method that makes the connection between the INotificationServer COM server and the MyNotificationClient event receiver :

static void TestNotifyByIDispatch()
{
  NotificationServerClass notification_server = new NotificationServerClass();
  MyNotificationClient notification_client = new MyNotificationClient();

  IntPtr pDispatch = Marshal.GetIDispatchForObject(notification_client);

  notification_server.RegisterNotification(pDispatch);

  notification_server.TestNofity(100);
}

3.5 The code instantiates the INotificationServer COM server by using the TLBIMP.EXE generated “NotificationServerClass”. An instance of “MyNotificationClient” (i.e. “notification_client”) is then created.

3.6 Next, an attempt is made to call the RegisterNotification() method by using the IDispatch pointer extracted from “notification_client” :

IntPtr pDispatch = Marshal.GetIDispatchForObject(notification_client);

3.7 The Marshal.GetIDispatchForObject() method returns a pointer to the actual COM IDispatch implemented by the CLR for the “notification_client” object. This IDispatch interface is returned as an IntPtr. This IntPtr is then passed directly as the parameter to the RegisterNotification() method :

notification_server.RegisterNotification(pDispatch);

3.8 The Notification Server’s “TestNofity()” method is then called with the expectation that MyNotificationClient.Notify() be invoked. However, not only did the MyNotificationClient.Notify() method not get called, an exception was raised with the following message :

An unhandled exception of type ‘System.Runtime.InteropServices.COMException’ occurred in CSConsoleClient01.exe

Additional information: Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

3.9 The next section steps through the server code and track down the specific point where the event firing mechanism broke down and the exception thrown.

4. Server Code.

4.1 Let’s observe the CNotificationServer::RegisterNotification() method which serves as the connection point between the notification server and the client :

STDMETHODIMP CNotificationServer::RegisterNotification(IDispatch* lpDispatch)
{
	// TODO: Add your implementation code here
	if (lpDispatch == NULL)
	{
		return E_POINTER;
	}

	if (m_spIDispatch)
	{
		// If m_spIDispatch is not null,
		// release it first.
		m_spIDispatch = NULL;
	}

	lpDispatch -> QueryInterface(IID_IDispatch, (void**)(&m_spIDispatch));

	return S_OK;
}

The client calls this method in the TestNotifyByIDispatch() method (see point 3.4). The most important part of this method is the call to QueryInterface() the incoming interface pointer for its IDispatch interface. This naturally succeeded. We conclude for now that this method is bug-free.

4.2 We next observe the CNotificationServer::TestNofity() method :

STDMETHODIMP CNotificationServer::TestNofity(LONG value)
{
	// TODO: Add your implementation code here
	if (m_spIDispatch == NULL)
	{
		return E_FAIL;
	}

	DISPPARAMS dispparms;
	VARIANTARG varArg;
	VARIANTARG varReturn;
	HRESULT hr;

	VariantInit(&varReturn);
	VariantInit(&varArg);
	V_VT(&varArg) = VT_I4;
	V_I4(&varArg) = value;	

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

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

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

	if (SUCCEEDED(hr))
	{
          // Finally making the call
	  UINT nErrArg;
	  hr = m_spIDispatch -> Invoke
          (
            dispid,
            IID_NULL,
            LOCALE_SYSTEM_DEFAULT,
            DISPATCH_METHOD,
            &dispparms,
            &varReturn,
            &excepinfo,
            &nErrArg);
	}

	return hr;
}

It is a typical late-bind call to a method of an object that implements the IDispatch interface.

4.3 As we step through the code, we note that the trouble started at the code to obtain the DISPID of the Notify() method :

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

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

Here the return value from the IDispatch::GetIDsOfNames() is 0x80020006 which indicates “Unknown name”. The HRESULT value was then returned to the caller and the IDispatch::Invoke() method was not called. This explains why the exception (described in point 3.8) was thrown.

4.4 The next section analyses and seeks the reason for the exception.

5. Analysis of the Exception.

5.1 Why did the call to GetIDsOfNames() fail ? The following are facts :

  • The MyNotificationClient class has already been marked with the ComVisibleAttribute hence it can be accessed in unmanaged COM code.
  • The MyNotificationClient class has already been marked with the ClassInterfaceAttribute indicating that it exposes the COM IDispatch interface hence it is no surprise that the call to RegisterNotification() succeeded.
  • Furthermore, the call to Marshal.GetIDispatchForObject() did return a non-NULL IntPtr value.
  • MyNotificationClient.Notify() method has already been declared as public in scope hence it should be discovered via GetIDsOfNames().

5.2 As it turned out, the trouble was with the call to the RegisterNotification() method. More specifically, the parameter that was passed to it :

IntPtr pDispatch = Marshal.GetIDispatchForObject(notification_client);

notification_server.RegisterNotification(pDispatch);

Note the highlighted code “pDispatch”.

5.3 To understand the problem, observe the way the RegisterNotification() method is declared after being processed by the type library importer :

[Guid("DAF8AA92-BAB9-44D4-884C-E66F3453EEB4")]
[TypeLibType(2)]
[ClassInterface(0)]
public class NotificationServerClass : INotificationServer, NotificationServer
{
        public NotificationServerClass();

        [DispId(1)]
        public virtual void RegisterNotification(object lpDispatch);
        [DispId(2)]
        public virtual void TestNofity(int value);
}

Here, the parameter to RegisterNotification() is actually of type “System.Object”. However, the actual parameter that was passed to RegisterNotification() was of type “IntPtr”.

5.4 Does this make a difference ? On initial inspection, all seems fine. We did not seem to have made any mistakes by passing an “IntPtr” variable to a method that expects an “System.Object”.

5.5 But we did make a mistake. We have passed “pDispatch” (an IntPtr type) to a method that expects a parameter of “System.Object” type. Hence It will not be the IDispatch pointer of “notification_client” that gets passed to RegisterNotification(). Rather, it is the IDispatch pointer of “pDispatch” (i.e. the object behind the IntPtr “pDispatch”) that gets passed to RegisterNotification().

5.6 The mistake is truly deceptively subtle. We had mistakenly assumed that an IDispatch IntPtr is somehow equivalent to an object, forgetting the fact that an IntPtr can itself be boxed as an object. And a managed object intrinsically implements IDispatch (and IUnknown).

5.7 To convince ourselves of this, the next section provides a special function that determines the type of an managed object.

6. Determining the Type of a Managed Object.

6.1 For this, I wrote a special global function that takes an IDispatch interface pointer (that points to a managed object) and determines its managed type. It is listed below :

#include <string>
#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;
	}
}

6.2 To use CheckTypeOfManagedObject(), you need to #import “mscorlib.tlb” as shown in the code above.

6.3 We can run CheckTypeOfManagedObject() against the IDispatch pointer passed to RegisterNotification() by enhancing RegisterNotification() as follows :

STDMETHODIMP CNotificationServer::RegisterNotification(IDispatch* lpDispatch)
{
	// TODO: Add your implementation code here
	if (lpDispatch == NULL)
	{
		return E_POINTER;
	}

	if (m_spIDispatch)
	{
		// If m_spIDispatch is not null,
		// release it first.
		m_spIDispatch = NULL;
	}

         std::string strManagedType;
         CheckTypeOfManagedObject(lpDispatch, strManagedType);

	lpDispatch -> QueryInterface(IID_IDispatch, (void**)(&m_spIDispatch));

	return S_OK;
}

We will see that the returned string for “strManagedType” is “System.IntPtr”.

7. Solution.

7.1 The solution to the problem is remarkably simple indeed. When we call RegisterNotification() in the client code, we pass as parameter the instance of the MyNotificationClient class itself :

static void TestNotifyByIDispatch_Corrected()
{
  NotificationServerClass notification_server = new NotificationServerClass();
  MyNotificationClient notification_client = new MyNotificationClient();

  notification_server.RegisterNotification(notification_client);

  notification_server.TestNofity(100);
}

7.2 This time, when the CNotificationServer::RegisterNotification() is run, CheckTypeOfManagedObject() will return “CSConsoleClient01.MyNotificationClient”.

7.3 This time, it is the IDispatch interface pointer of the MyNotificationClient object “notification_client” that gets passed to RegisterNotification().

8. Alternative Solution.

8.1 Although the solution in section 7 is sufficient, I shall provide an alternative solution that will work for the original TestNotifyByIDispatch() C# code.

8.2 For this I have defined a new method for the INotificationServer interface :

[id(3), helpstring("method RegisterNotification2")] HRESULT RegisterNotification2([in] IDispatch* lpDispatch);

The implementation for RegisterNotification2() can be identical to RegisterNotification() :

STDMETHODIMP CNotificationServer::RegisterNotification2(IDispatch* lpDispatch)
{
	// TODO: Add your implementation code here
	if (lpDispatch == NULL)
	{
		return E_POINTER;
	}

	if (m_spIDispatch)
	{
		// If m_spIDispatch is not null,
		// release it first.
		m_spIDispatch = NULL;
	}

	std::string strManagedType;
	CheckTypeOfManagedObject(lpDispatch, strManagedType);	

	lpDispatch -> QueryInterface(IID_IDispatch, (void**)(&m_spIDispatch));

	return S_OK;
}

8.3 Then, the interop assembly for TestCOMObjects01.dll, i.e. interop.TestCOMObjects01.dll is modified using ILDASM.EXE and ILASM.EXE. The following describes this process :

  • Run ILDASM.EXE against interop.TestCOMObjects01.dll to produce a disassembled code in text. The following is a sample command line :
ILDASM /TEXT interop.TestCOMObjects01.dll /out:interop.TestCOMObjects01.il
  • In the output “interop.TestCOMObjects01.il” file search for all occurrences of “RegisterNotification2” and then modify the parameter type for this method from “object  marshal( idispatch )” to “native int” :
.method public hidebysig newslot abstract virtual
  // instance void  RegisterNotification2([in] object  marshal( idispatch ) lpDispatch) runtime managed internalcall
  instance void  RegisterNotification2([in] native int lpDispatch) runtime managed internalcall
{
  .custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 04 00 00 00 00 00 )
} // end of method INotificationServer::RegisterNotification2
...
...
...
.method public hidebysig newslot virtual
  // instance void  RegisterNotification2([in] object  marshal( idispatch ) lpDispatch) runtime managed internalcall
  instance void  RegisterNotification2([in] native int lpDispatch) runtime managed internalcall
{
  .custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 04 00 00 00 00 00 )
  .override interop.TestCOMObjects01.INotificationServer::RegisterNotification2
} // end of method NotificationServerClass::RegisterNotification2
  • Thereafter, re-assemble the modified .IL file to produce a new “interop.TestCOMObjects01.dll” using the following command line :
ILASM /DLL interop.TestCOMObjects01.il /out:interop.TestCOMObjects01.dll

8.4 Thereafter, in the C# client project, reference the new interop assembly. You will see the RegisterNotification2() method listed in the Object Browser as :

void RegisterNotification2(System.IntPtr lpDispatch);

Note that the parameter type is IntPtr. We can now use the output of Marshal.GetIDispatchForObject() as parameter to RegisterNotification2() :

static void TestNotifyByIntPtr()
{
  NotificationServerClass notification_server = new NotificationServerClass();
  MyNotificationClient notification_client = new MyNotificationClient();

  IntPtr pDispatch = Marshal.GetIDispatchForObject(notification_client);

  notification_server.RegisterNotification2(pDispatch);

  notification_server.TestNofity(100);
}

9. In Conclusion.

9.1 I hope the reader has benefitted from this rather long discussion of a small bug with large consequences.

9.2 Always remember that all managed types are derived from System.Object. Hence all managed objects can be cast to the System.Object type. This includes the IntPtr type.

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

5 thoughts on “Misunderstanding IntPtr and System.Object.

  1. Hi, I tried to do the same with out-of-proc COM server, I mean invoking methods exposed by managed object using late-binding. The problem is that GetIDsOfNames returns 0x80070005, and System.UnauthorizedAccess exception as raised somewhere in CLR after the call to GetIDsOfNames from unmanaged function. Could you please tell me what the problem is? Is it possible to access methods of managed object passed to unmanaged function in case of out-of-proc COM-server?
    Here is a code:

    in com object declaration I have
    protected:
    IDispatchPtr m_spIDispatch;

    public:
    STDMETHOD(RegisterObject )(/*[in]*/IDispatch *spObject);
    STDMETHOD(ConfigureObject)(/*[in]*/BSTR *str);

    RegisterObject implementation:
    STDMETHODIMP CCOMClass::RegisterObject(IDispatch* lpDispatch)
    {
    CBSTR errmsg;
    HRESULT hr=0;
    if (lpDispatch == NULL)
    {
    return E_POINTER;
    }

    if (m_spIDispatch)
    {
    // If m_spIDispatch is not null,
    // release it first.
    m_spIDispatch = NULL;
    }
    try{
    hr=lpDispatch -> QueryInterface(IID_IDispatch, (void**)(&m_spIDispatch));
    }
    catch (_com_error& e)
    {
    // never comes here QueryInterface returns S_OK
    }
    return S_OK;
    }

    ConfigureObject implementation:
    STDMETHODIMP CCOMClass::ConfigureObject(BSTR *str)
    {

    if (m_spIDispatch == NULL)
    {
    return E_FAIL;
    }

    HRESULT hr;

    OLECHAR FAR* rgszNames = L”Notify”;
    DISPID dispid = 0;

    hr = m_spIDispatch -> GetIDsOfNames
    (
    IID_NULL,
    &rgszNames,
    1,
    LOCALE_SYSTEM_DEFAULT,
    &dispid
    );
    // hr is 0x80070005
    }

    C# client code:
    namespace ConsoleApplication1
    {

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class MyNotificationClient
    {

    public void Notify(int value)
    {
    Console.WriteLine(“Notify. Value == {0:D}.”, value);
    }
    }

    class Program
    {
    static void TestNotifyByIDispatch()
    {
    Console.WriteLine(Thread.CurrentThread.ApartmentState);
    string str = “str”;
    COMClass comObj = new COMClass ();
    MyNotificationClient managedObj = new MyNotificationClient();
    comObj .RegisterObject(managedObj);
    comObj .ConfigureObject(ref str); // here .NET program terminates. The appearing error message is about // System.UnauthorizedAccess error

    }
    static void CalledOnSTAThread()
    {
    TestNotifyByIDispatch();
    Console.ReadKey();
    }

    static void Main(string[] args)
    {
    Thread t = new Thread(new ThreadStart(CalledOnSTAThread));
    t.ApartmentState = ApartmentState.STA;
    t.Start();

    }
    }
    }

    When I tried to run the program from default (MTA) thread it failed also.
    Actually, I’m doing exactly the same as in the example in this article, except that COM serever is out-of-proc.
    This problem becomes critical for development of my project and I’m completely lost. Could you please help to figure out the issue? thank you in advance.

    Posted by Anastasia | December 9, 2011, 10:50 am
    • Hello Anastasia,

      1. >> The problem is that GetIDsOfNames returns 0×80070005, and System.UnauthorizedAccess exception as raised somewhere in CLR after the call to GetIDsOfNames from unmanaged function.

      1.1 This is unusual. 0×80070005 (Access is Denied) is not listed as a standard return value from IDispatch::GetIDsOfNames().

      1.2 What OS platform are you running your application on ?

      2. >> Is it possible to access methods of managed object passed to unmanaged function in case of out-of-proc COM-server?

      2.1 There is no problem here. The fact that the COM server is an out-of-proc server is transparent to the managed application.

      2.2 I created a COM out-of-proc server and tried to recreate your problem but everything went well.

      2.3 In fact, I could successfully invoke the Notify() method of the instance of the MyNotificationClient class (i.e. managedObj) from inside CCOMClass::ConfigureObject()..

      3. Have you tried using an in-proc-server (DLL) ?

      – Bio.

      Posted by Lim Bio Liong | December 13, 2011, 3:23 am
  2. Hi, Bio,

    Thanks a lot for your reply.

    1. >>What OS platform are you running your application on ?
    I have tried this in Windows Server 2003, 2008, using .NET framework 2.0 and 3.0

    I thought it could be due to some .NET framework security policy peculiarities, but trying different settings there did not help.

    2. >> Have you tried using an in-proc-server (DLL) ?
    I have not tried this. If even this worked this would not be an option for my project.

    Now I’m going to try running the application on Win XP machine and in case of success will make a start from OS configuration..

    Could you tell me please what OS is used in your case?

    Thanks,
    Anastasia.

    Posted by Anastasia | December 13, 2011, 9:45 pm
  3. Hi,

    I’ve just got it worked by adjusting settings in Local Security Policy and DCOM config.

    But I think it would be more effective in my case to query for interface of the managed object and invoke it’s methods just like you did in CheckTypeOfManagedObject func:

    mscorlib::_TypePtr sp_Type = NULL;
    hr = spIUnknownForManagedType -> QueryInterface(&sp_Type);

    Unfortunately did not manage to to this and got hr=80004002 E_NOINTERFACE upon QueryInterface call.

    Here is declaration of Interface and class (implemented in C++\CLI):
    [ComVisible(true)]
    [Guid(“2028ADC0-F9AD-3EB4-A93B-78E3CEBCD456”)]
    public interface class ICRReport
    {

    }
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType::None)]
    public ref class CRReport:ICRReport
    {

    }

    I have build dll assembly, created type library from it and imported it to the server project

    in c# client instance of CRReport is created and passed as a parameter to the server function:
    Report reportObj = new Report(); \\ unmanaged object
    CRReport CRReportObj = new CRReport(); \\managed object
    reportObj.RegisterReport(CRReportObj);

    Here is server function code:
    STDMETHODIMP CReport::RegisterReport(IDispatch* lpDispatch)
    {
    ICRReportPtr spICRReport=NULL;
    IUnknownPtr spIUnknown=NULL;
    hr=lpDispatch-> QueryInterface(IID_IUnknown, (void**)(&spIUnknown)); \\OK
    hr=spIUnknown -> QueryInterface(&spICRReport); \\ hr=80004002 (E_NOINTERFACE);

    …….
    }

    Could you please tell me what I am missing here and how to make this work?

    Posted by Anastasia | December 20, 2011, 12:51 pm

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: