Link to home
Create AccountLog in
Avatar of MikeJ2001
MikeJ2001

asked on

Calling ATL COM method from C# fails with HRESULT: 0x80004002 (E_NOINTERFACE)

I am currently trying to port some C++ sample code for an ATL COM Component to C#. The component was developed in C++. It compiles fine with VC6 and VC8 and the console-based sample that calls the COM is also working fine.

The C# code compiles fine, however, when I execute it I get the following runtime error:

System.InvalidCastException was unhandled
Message="Unable to cast COM object of type 'SIMPLE_ATLLib.First_ATLClass' to interface type 'SIMPLE_ATLLib.IFirst_ATL'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{C8F6E230-2672-11D3-A8A8-00105AA943DF}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."
  Source="Interop.SIMPLE_ATLLib"
  StackTrace:
       at SIMPLE_ATLLib.First_ATLClass.AddNumbers(Int32 Num1, Int32 Num2)
       at Test_ATL.Program.Main(String[] args) in C:\develop\samples\COM_ATL tutorial\COM_ATL_Tutorial_CS_Source\Test_ATL.cs:line 29
       at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

here is the source code:
  SIMPLE_ATLLib.First_ATLClass testClass = new SIMPLE_ATLLib.First_ATLClass();
  if (testClass == null)
   {
      Console.WriteLine("ERROR: can't create instance of testClass SIMPLE_ATLLib.First_ATLClass()");
   }
   Console.WriteLine("Getting a long value ..");
   Int32 iValue;
   iValue=testClass.AddNumbers(1, 2); // this is the part that I can't get to work


// ================== WORKING C++ code =====================
/**
 * This test calls the AddNumbers() method. It adds two numbers. The result
 * is returned bu reference.
*/
void test_addNumbers(IFirst_ATL *comClass)
{
  HRESULT hr;
  long ReturnValue;
  hr = comClass->AddNumbers(5, 7, &ReturnValue);
  cout << "================================================================" << endl;
  cout << "calculating 5 + 7 ... ";
  if(SUCCEEDED(hr))
  {
    cout << "result is: " << dec << ReturnValue << endl;
  }
  else
  {
    cout << "ERROR " << hr << endl;
  }
}

NOTE:
the sample code and ATL COM was derived from "A Simple COM tutorial using ATL" by  C. Lung which contains the COM sources and VB and VC sample code. Complete original sources are available at: http://www.codeguru.com/Cpp/COM-Tech/atl/tutorials/article.php/c17
 



Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Is there a definition for SIMPLE_ATLLib.First_ATL also?

Bob
Avatar of MikeJ2001
MikeJ2001

ASKER

Hi Bob,
Thanks for looking into this.
Mike

// ============= from Simple_ATL.h ====================
// excerpt from the Simple_ATL.h file created by the VC compiler.
 
virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE AddNumbers(
            /* [in] */ long Num1,
            /* [in] */ long Num2,
            /* [retval][out] */ long *ReturnVal) = 0;
       
/* [helpstring] */ HRESULT STDMETHODCALLTYPE IFirst_ATL_AddNumbers_Proxy(
    IFirst_ATL * This,
    /* [in] */ long Num1,
    /* [in] */ long Num2,
    /* [retval][out] */ long *ReturnVal);

void __RPC_STUB IFirst_ATL_AddNumbers_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

// ========================== from First_ATL.cpp ==============
// IMPLEMENTATION of the AddNumbers method
STDMETHODIMP CFirst_ATL::AddNumbers(long Num1, long Num2, long *ReturnVal)
{
      *ReturnVal = Num1 + Num2;
      return S_OK;
}
// First_ATL.h : Declaration of the CFirst_ATL

#ifndef __FIRST_ATL_H_
#define __FIRST_ATL_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CFirst_ATL
class ATL_NO_VTABLE CFirst_ATL :
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CFirst_ATL, &CLSID_First_ATL>,
      public IFirst_ATL
{
public:
      CFirst_ATL()
      {
      }

DECLARE_REGISTRY_RESOURCEID(IDR_FIRST_ATL)
DECLARE_NOT_AGGREGATABLE(CFirst_ATL)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CFirst_ATL)
      COM_INTERFACE_ENTRY(IFirst_ATL)
END_COM_MAP()

// IFirst_ATL
public:
      STDMETHOD(getBSTR)(/*[in]*/ BSTR input, /*[in]*/ long offset, /*[out, retval]*/ BSTR *output);
      STDMETHOD(AddNumbers)(/*[in]*/ long Num1, /*[in]*/ long Num2, /*[out, retval]*/ long *ReturnVal);
        STDMETHOD(getByteArray)(/*[in, out]*/long *length, /*[out, retval]*/ unsigned char* byteArray);
      STDMETHOD(getLongValue)(/*[out, retval]*/ long *longValue);
};

#endif //__FIRST_ATL_H_
I see 'IFirst_ATL' defined.  Did you try that?

Bob
Yes, working with IFirst_ATL worked fine C++: an IFirst_ATL *comClass gives me access to all methods within the FirstATl class.
  IFirst_ATL      *IFirstATL;
  hr = CoInitialize(0);
  // Now we will intilize COM
  // Use the SUCCEDED macro and see if we can get a pointer to
  // the interface
  if(SUCCEEDED(hr))
  {
    hr = CoCreateInstance( CLSID_First_ATL, NULL, CLSCTX_INPROC_SERVER,
      IID_IFirst_ATL, (void**) &IFirstATL);
// If we succeeded then call the AddNumbers method, if it failed
      // then display an appropriate message to the user.
      if(SUCCEEDED(hr))
      {
        test_addNumbers(IFirstATL);

I tried that in C#, but there, I can only create an instance of the First_ATL class the following way:
SIMPLE_ATLLib.First_ATLClass testClass = new SIMPLE_ATLLib.First_ATLClass();

changing this to
SIMPLE_ATLLib.IFirst_ATL testClass = new SIMPLE_ATLLib.IFirst_ATL();

leads to the following error message during build:
Cannot create an instance of the abstract class or interface 'SIMPLE_ATLLib.IFirst_ATL'
I have to say that working with COM/C++ libraries can really be a pain in the ass :(

I have been reading articles like this, that talk about creating a custom wrapper class in managed C++.NET, and calling it from C#.  

Implementing a Custom Runtime Callable Wrapper
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmex/html/vcgrfImplementingCustomRuntimeCallableWrapper.asp


I realize, though, that this only adds another layer of complexity.

Bob
solved: [STAThread] fixed it
Sometimes it the easy things.

Bob
ASKER CERTIFIED SOLUTION
Avatar of DarthMod
DarthMod
Flag of United States of America image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer