3

I'm calling the C# methods from a C++ unmanaged code. I have a problem with getting a value from a class instance returned in array.

I've simplified the code a little bit

This is the problematic method.

    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
    public ScOrder[] GetOrders()
    {
        return new ScOrder[] {

            (new ScOrder(1),
            (new ScOrder(2)
        };
    }

This is the IScOrder interface

[ComVisible(true)]
[Guid("B2B134CC-70A6-43CD-9E1E-B3A3D9992C3E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IScOrder
{
    long GetQuantity();
}

And this is the ScOrder implementation

[ComVisible(true)]
[Guid("F739759E-4D00-440E-B0B7-69AAF97FCB6D")]
[ClassInterface(ClassInterfaceType.None)]
public class ScOrder
{
    private long quantity = 0;

    public ScOrder() {}

    public ScOrder(long quantity)
    {
        this.quantity = quantity;
    }

    public long GetQuantity()
    {
        return this.quantity;
    }
}

This is the C++ code, after a help from Zdeslav Vojkovic in my previous request. The problem is described in the comments

  • I'm not using ATL nor MFC.
  • The C++ tlb file is generated via regasm.

COM initialization and calling the GetOrders method works good

IScProxyPtr iPtr;
CoInitialize(NULL);
iPtr.CreateInstance(CLSID_ScProxy);
SAFEARRAY* orders;
iPtr->GetOrders(&orders);
LPUNKNOWN* punks;
HRESULT hr = SafeArrayAccessData(orders, (void**)&punks);
if(SUCCEEDED(hr)) 
{
    long lbound, ubound;
    SafeArrayGetLBound(orders, 1, &lbound);
    SafeArrayGetUBound(orders, 1, &ubound);
    long elements = ubound - lbound + 1;
    for(int i=0;i<elements;i++) 
    {
        LPUNKNOWN punk = punks[i]; //the punk seems valid 
        IScOrderPtr order(punk); //unfortunatelly, "order" now points to {0x00000000}

        //subsequent attempt to get the value will fail
        long quantity = 0;
        HRESULT procCall;
        //GetQuantity will throw an exception
        procCall = order->GetQuantity((long long *)q); 

    }
    SafeArrayUnaccessData(orders);
}
SafeArrayDestroy(orders);

Thanks to Zdeslav, I discovered that I can debug inside the order(punk):

IScOrderPtr order(punk);

So I stepped into order(punk) to see what's happening there. I got inside a "comip.h"

// Constructs a smart-pointer from any IUnknown-based interface pointer.
//
template<typename _InterfaceType> _com_ptr_t(_InterfaceType* p) 
    : m_pInterface(NULL)
{
    HRESULT hr = _QueryInterface(p);

...then I stepped inside the _QueryInterface(p) implementation, also in comip.h

// Performs a QI on pUnknown for the interface type returned
// for this class.  The interface is stored.  If pUnknown is
// NULL, or the QI fails, E_NOINTERFACE is returned and
// _pInterface is set to NULL.
//
template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw()
{
    HRESULT hr;

    // Can't QI NULL
    //
    if (p != NULL) {
        // Query for this interface
        //
        Interface* pInterface;
        hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));

Now the problem here is that the value of "hr" returned is E_NOINTERFACE ... and that's not right.

I'm no C++ or COM expert...please help :)

Community
  • 1
  • 1
Mirek
  • 4,013
  • 2
  • 32
  • 47

1 Answers1

1

Your class ScOrder does not seem to be implementing IScOrder interface on the C# side.

It should have been:

//[ComVisible(true)]
//[Guid("F739759E-4D00-440E-B0B7-69AAF97FCB6D")]
//[ClassInterface(ClassInterfaceType.None)]
public class ScOrder : IScOrder

I commented [...] above not because it is interfering, rather because it does not look necessary: it is IScOrder needs to have COM visibility and should be able to get it on C++ side.

Without inheriting IScOrder your instances do have some interface but the one of your interest IScOrder is indeed not accessible on the pointers.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • 1
    Again, I was proved to be a blind idiot by people in StackOverflow. I'm just going to debug this to see if that works and then accept this as an answer. – Mirek Sep 15 '12 at 11:11
  • For some reason, GetQuantity returns 0 but that's a different problem. Thank you :) – Mirek Sep 15 '12 at 11:20
  • 1
    `E_NOINTERFACE` is a good hint here. You do have some object, and you know it is your order class. But it does not give you the interface you need, so it just has to be an issue around interface visibility. – Roman R. Sep 15 '12 at 11:21
  • `procCall` is expectedly zero (= `S_OK`). `quantity` perhaps stays zero because you provide `&q` as the argument, and not `&quantity` :) – Roman R. Sep 15 '12 at 11:23