Question

I have made a COM wrapper over C# dll and I use this COM component in C++ to invoke the methods in C# dll.

All is working fine except when my C# method returns null through COM to C++. an excpetion is thrown in C++ that says

"Debug Assertion failed!" ..... atlsafe.h Line 235 Expression psaSrc != null.

How do I avoid this error and accept a null value in the return type.

for eg.

CComSafeArray itemEntities = objController1->ListItems(sPath);

When ListItems method returns a null, system should not throw an error. Instead itemEntities should be st to NULL.

Please someone suggest a solution.

Thanks, Gagan

Was it helpful?

Solution

It must be something in the part of your code which you did not show. This works for me:

C# class:

[ComVisible(true)]
[Guid("BE55747F-FEA9-4C1F-A103-32A00B162DF0")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Test
{
    //[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
    public string[] GetStringArray()
    {
        var a = new string[3];
        a[0] = "string0";
        a[1] = null;
        a[2] = "string2";
        return a;
    }

    public string[] GetStringArray2()
    {
        return null;
    }
}

Calling GetStringArray and GetStringArray2 from C++:

SAFEARRAY* pSA = NULL;
testObject->GetStringArray(&pSA);
printf("str0: %ls\n", ((BSTR*)(pSA->pvData))[0]);
printf("ptr1: %x\n", ((BSTR*)(pSA->pvData))[1]);
printf("str2: %ls\n", ((BSTR*)(pSA->pvData))[2]);

SAFEARRAY* pSA2 = NULL;
testObject->GetStringArray2(&pSA2);
printf("pSA2: %x\n", pSA2);

Run:

str0: string0
ptr1: 0
str2: string2
pSA2: 0

I did not have to specify how to marshal the array (the commented out line), because it gets marshaled as SAFEARRAY(VT_BSTR) by default.

EDITED: I guess I see where the problem is. You're using ATL CComSafeArray which doesn't expect a NULL SAFEARRAY by design:

CComSafeArray(_In_ const SAFEARRAY *psaSrc) : m_psa(NULL)
{
    ATLASSERT(psaSrc != NULL);
    HRESULT hRes = CopyFrom(psaSrc);
    if (FAILED(hRes))
        AtlThrow(hRes);
}

You should change your code like this:

CComSafeArray<BSTR> itemEntities;
SAFEARRAY* pItemEntities = objController1->ListItems(sPath);
if (NULL != pItemEntities)
    itemEntities.Attach(pItemEntities);

Or, assign m_psa directly:

CComSafeArray<BSTR> itemEntities
itemEntities.m_psa = objController1->ListItems(sPath);
if (!itemEntities)
{
    // NULL returned
}

OTHER TIPS

The return value in COM is reserved to pass exceptions to different modules. To get a real return value you could add a out parameter to your function and use this as a return value. I thought the C# COM Interop would already do this for you but it seems that you need to do this manually.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top