Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2675
  • Last Modified:

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
 



0
MikeJ2001
Asked:
MikeJ2001
  • 4
  • 4
1 Solution
 
Bob LearnedCommented:
Is there a definition for SIMPLE_ATLLib.First_ATL also?

Bob
0
 
MikeJ2001Author Commented:
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;
}
0
 
MikeJ2001Author Commented:
// 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_
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
Bob LearnedCommented:
I see 'IFirst_ATL' defined.  Did you try that?

Bob
0
 
MikeJ2001Author Commented:
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'
0
 
Bob LearnedCommented:
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
0
 
MikeJ2001Author Commented:
solved: [STAThread] fixed it
0
 
Bob LearnedCommented:
Sometimes it the easy things.

Bob
0
 
DarthModCommented:
PAQed with points refunded (400)

DarthMod
Community Support Moderator
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 4
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now