3

I need to access C# methods in a COM dll via COM-like interface. One of the method requires an array of strings to be passed as input.

Am creating a SAFEARRAY and passing it to the COM Interop. However, this does not seem to be working, as I see an exception in the interop layer. (System.Runtime.InteropServices.SafeArrayTypeMismatchException).

Obviously, there seems to be a difference in type being expected.

Pasting the code here:

C# method to be invoked:

public long DoIt3(int nFiles, string[] fileNames);

C++ code invoking the same:

int _tmain()
{
TCHAR *fileNames[128] = { TEXT("C:\\Program Files\\IBM\\RTC.NET"),
                          TEXT("C:\\KIRAN\\Work\\RFT"), TEXT(".\\bin\\Debug") };

SAFEARRAY *pSA = CreateSafeStringArray(3, fileNames);

_tprintf(TEXT("%d"), pIManaged->DoIt3(3, pSA));

SafeArrayDestroy(pSA);
}

static SAFEARRAY *CreateSafeStringArray(long nElements, TCHAR *elements[])
{
SAFEARRAYBOUND saBound[1];

saBound[0].cElements = nElements;
saBound[0].lLbound = 0;

SAFEARRAY *pSA = SafeArrayCreate(VT_VARIANT, 1, saBound);

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix++)
{
    VARIANT v;

    VariantInit(&v);

    v.vt = VT_BSTR;
    v.bstrVal = elements[ix];

    long rgIndicies[1];

    rgIndicies[0] = ix + saBound[0].lLbound;

    HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, &v);

    _tprintf(TEXT("%d"), hr);

    VariantClear(&v);
}

return pSA;
}

Any ideas/suggestions are welcome.

4
  • How are you generating the Runtime Callible Wrapper for the ComObject is the c# code ? Commented Nov 10, 2011 at 20:43
  • 2
    Pretty unclear how you are invoking the C# method. But you created an object[], not a string[]. Pass VT_BSTR to SafeArrayCreate() and adjust the rest of the code accordingly. Commented Nov 10, 2011 at 21:37
  • Not sure about C++, but to get a VBA client to call a C# CCW, the only way I got it working was to declare the string array argument as ref: public long DoIt3(int nFiles, ref string[] fileNames); Commented Nov 10, 2011 at 22:19
  • 2
    @Joe That was because VBA clients always pass variant parameters as VT_BYREF Commented Nov 10, 2011 at 23:05

2 Answers 2

2

I figured it out! The following code works:

static SAFEARRAY *CreateSafeStringArray(long nElements, TCHAR *elements[])
{
SAFEARRAYBOUND saBound[1];

saBound[0].cElements = nElements;
saBound[0].lLbound = 0;

SAFEARRAY *pSA = SafeArrayCreate(VT_BSTR, 1, saBound);

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix++)
{
    BSTR pData = SysAllocString(elements[ix]);

    long rgIndicies[1];

    rgIndicies[0] = saBound[0].lLbound + ix;

    HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, pData);

    _tprintf(TEXT("%d"), hr);
}

return pSA;
}

Thanks for all your suggestions!

Sign up to request clarification or add additional context in comments.

Comments

1

In the case of an array of BSTR strings, you can set the BSTR values directly on your array, and also you need to allocate memory for your BSTR elements, you could use ATL/MFC CString for that:

...
psa = SafeArrayCreate( VT_BSTR, 1, saBound);
HRESULT hr = SafeArrayLock( psa );
//TODO: test for hr success

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix++)
{
    long rgIndicies[1];
    rgIndicies[0] = ix + saBound[0].lLbound;
    CString tempstr(elements[ix]);

    ((BSTR*)psa->pvData)[ix] = tempstr.AllocSysString();
    _tprintf(TEXT("%d"), hr);
}

hr = SafeArrayUnlock( psa );
//TODO: test for hr success
...

1 Comment

Thanks, yms! Will try out. But, this approach appears like achieving it through manipulating the internal fields. Is there a standard way of doing it -- may be, the arguments am passing to SafeArrayPutElement() is wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.