COM interop - pass a char[] array

haton 21 Reputation points
2020-12-27T17:22:39.823+00:00

I want to call a .NET method through .Invoke with a char[] parameter:

StringBuilder.Insert(int index, char[] value)

I reasoned that from C++, I should pass a safearray of VT_UI2, with variant type VT_ARRAY+VT_UI2, and that would be marshaled to a char[]. But I get an "incorrect parameter" error. Any recommendations?
Thanks

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,527 questions
.NET Runtime
.NET Runtime
.NET: Microsoft Technologies based on the .NET software framework.Runtime: An environment required to run apps that aren't compiled to machine language.
1,119 questions
0 comments No comments
{count} votes

6 answers

Sort by: Most helpful
  1. RLWA32 40,286 Reputation points
    2020-12-27T21:09:25.603+00:00

    I think the .Net COM interop has some kind of a problem with marshaling the SAFEARRAY of VT_UI2 to char[]. You could work around this on the .Net side by using a ushort[] array and converting to string for use with StringBuilder.Insert.


  2. haton 21 Reputation points
    2020-12-28T07:01:47.897+00:00

    Yes, that is what I am observing.
    Is it worth reporting the problem to Microsoft? I guess i am not the first one to stumble upon this.


  3. haton 21 Reputation points
    2020-12-30T10:50:40.267+00:00

    Follow-up: I still try to .Invoke a .NET Framework method which wants a char[] parameter. I tried a sly hack: generate the char[] on the managed side.
    I wrote a small C# class which generates a new char[] in method X() and returns a pointer by calling Marshal.GetIUnknownForObject.
    I call this method X() from the unmanaged side through COM interop, and get the pointer result. To test the result, I cast this pointer to an Object and pass it again to the managed side to methods Y() and Z(). I hoped this object would be received as a char[] array but the results puzzle me:

    • the object isn't accepted by method Y() which wants a char[] array parameter (Incorrect Parameter),
    • it is accepted by method Z() which wants an Object parameter,
    • Console.WriteLine(a.GetType()) prints 'System.Char[]',
    • the parameter can be cast to a char[] array and the contents can be read correctly!

    I haven't reach my goal but maybe I am on to something? I would be super grateful for any advice.

    --C#
    using System; using System.IO; using System.Runtime.InteropServices;
    public class XHelper {
    public IntPtr X() {
    char[] a=new char[2] {'A', 'B'};
    return Marshal.GetIUnknownForObject(a);
    }
    public void Y(char[] a){ // fails with INVALID PARAMETER
    Console.WriteLine(a.GetType());
    Console.WriteLine(((char[])a)[0]);
    }
    public void Z(Object a){ //parameter is accepted as Object
    Console.WriteLine(a.GetType()); //DISPLAYS System.Char[]
    Console.WriteLine(((char[])a)[0]); //DISPLAYS A (correct first character)
    }
    }


  4. haton 21 Reputation points
    2020-12-30T12:34:14.827+00:00

    I don't have control about the system methods I call.
    But I can run some code bits of my own in parallel.

    The unmanaged part looks like:

    //----
    XHelper helper=new XHelper;

    VARIANT v;
    dispidX=...(dispid of X method)
    dispidY=...(dispid of Y method)
    dispidZ=...(dispid of Z method)
    helper->invoke (dispidX, IID_NULL, 0x400, DISPATCH_METHOD, null, &v, null, null);
    //result variant is ant IntPtr, with type VT_UINT

    //prepare Invoke arguments
    VARIANT *args = new VARIANT[1];
    args[0]=v;
    args[0].vt = VT_UNKNOWN; //force to interpret v as an Object

    //invoke Y
    DISPPARAMS dp;
    dp.cArgs=1;
    dp.rgvarg = args;
    helper->invoke (dispidY, IID_NULL, 0x400, DISPATCH_METHOD, &dp, &v, null, null);
    //---

    (This experiment is a first step. To be of any use, the next step would be to create the char[] array on the managed side based on a pointer provided from the unmanaged side).


  5. haton 21 Reputation points
    2021-01-01T13:03:28.6+00:00

    This is an improvement because it lets me stick to my tactics of calling invoke (or invokemember) from the unmanaged side.
    By the way, as you mention simpler ways, is there a way in C# to cast/convert ushort[] to char[] without copying the data?