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.InvalidCastExceptio n was unhandled
Message="Unable to cast COM object of type 'SIMPLE_ATLLib.First_ATLCl ass' 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- 00105AA943 DF}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."
Source="Interop.SIMPLE_ATL Lib"
StackTrace:
at SIMPLE_ATLLib.First_ATLCla ss.AddNumb ers(Int32 Num1, Int32 Num2)
at Test_ATL.Program.Main(Stri ng[] args) in C:\develop\samples\COM_ATL tutorial\COM_ATL_Tutorial_ CS_Source\ Test_ATL.c s:line 29
at System.AppDomain.nExecuteA ssembly(As sembly assembly, String[] args)
at System.AppDomain.ExecuteAs sembly(Str ing assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.Hos tingProces s.HostProc .RunUsersA ssembly()
at System.Threading.ThreadHel per.Thread Start_Cont ext(Object state)
at System.Threading.Execution Context.Ru n(Executio nContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHel per.Thread Start()
here is the source code:
SIMPLE_ATLLib.First_ATLCla ss testClass = new SIMPLE_ATLLib.First_ATLCla ss();
if (testClass == null)
{
Console.WriteLine("ERROR: can't create instance of testClass SIMPLE_ATLLib.First_ATLCla ss()");
}
Console.WriteLine("Getting a long value ..");
Int32 iValue;
iValue=testClass.AddNumber s(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
The C# code compiles fine, however, when I execute it I get the following runtime error:
System.InvalidCastExceptio
Message="Unable to cast COM object of type 'SIMPLE_ATLLib.First_ATLCl
Source="Interop.SIMPLE_ATL
StackTrace:
at SIMPLE_ATLLib.First_ATLCla
at Test_ATL.Program.Main(Stri
at System.AppDomain.nExecuteA
at System.AppDomain.ExecuteAs
at Microsoft.VisualStudio.Hos
at System.Threading.ThreadHel
at System.Threading.Execution
at System.Threading.ThreadHel
here is the source code:
SIMPLE_ATLLib.First_ATLCla
if (testClass == null)
{
Console.WriteLine("ERROR: can't create instance of testClass SIMPLE_ATLLib.First_ATLCla
}
Console.WriteLine("Getting
Int32 iValue;
iValue=testClass.AddNumber
// ================== WORKING C++ code =====================
/**
* This test calls the AddNumbers() method. It adds two numbers. The result
* is returned bu reference.
*/
void test_addNumbers(IFirst_ATL
{
HRESULT hr;
long ReturnValue;
hr = comClass->AddNumbers(5, 7, &ReturnValue);
cout << "=========================
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
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_Prox y(
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(lon g Num1, long Num2, long *ReturnVal)
{
*ReturnVal = Num1 + Num2;
return S_OK;
}
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_Prox
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);
// ==========================
// IMPLEMENTATION of the AddNumbers method
STDMETHODIMP CFirst_ATL::AddNumbers(lon
{
*ReturnVal = Num1 + Num2;
return S_OK;
}
ASKER
// 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<CComSingl eThreadMod el>,
public CComCoClass<CFirst_ATL, &CLSID_First_ATL>,
public IFirst_ATL
{
public:
CFirst_ATL()
{
}
DECLARE_REGISTRY_RESOURCEI D(IDR_FIRS T_ATL)
DECLARE_NOT_AGGREGATABLE(C First_ATL)
DECLARE_PROTECT_FINAL_CONS TRUCT()
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)(/*[i n]*/ 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_
#ifndef __FIRST_ATL_H_
#define __FIRST_ATL_H_
#include "resource.h" // main symbols
//////////////////////////
// CFirst_ATL
class ATL_NO_VTABLE CFirst_ATL :
public CComObjectRootEx<CComSingl
public CComCoClass<CFirst_ATL, &CLSID_First_ATL>,
public IFirst_ATL
{
public:
CFirst_ATL()
{
}
DECLARE_REGISTRY_RESOURCEI
DECLARE_NOT_AGGREGATABLE(C
DECLARE_PROTECT_FINAL_CONS
BEGIN_COM_MAP(CFirst_ATL)
COM_INTERFACE_ENTRY(IFirst
END_COM_MAP()
// IFirst_ATL
public:
STDMETHOD(getBSTR)(/*[in]*
STDMETHOD(AddNumbers)(/*[i
STDMETHOD(getByteArray)(/*
STDMETHOD(getLongValue)(/*
};
#endif //__FIRST_ATL_H_
I see 'IFirst_ATL' defined. Did you try that?
Bob
Bob
ASKER
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_ATLCla ss testClass = new SIMPLE_ATLLib.First_ATLCla ss();
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'
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_ATLCla
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
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
ASKER
solved: [STAThread] fixed it
Sometimes it the easy things.
Bob
Bob
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
Bob