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,538 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,125 questions
0 comments No comments
{count} votes

6 answers

Sort by: Most helpful
  1. Bio Liong Lim 1 Reputation point
    2021-05-08T07:43:03.407+00:00

    Hello haton-9060,

    I think the problem is with that the interop marshaler is not able to convert the array of ushorts to an array of char. This is possibly because there is no direct mapping from a VARIANT VT_UI2 to a Managed Char Type.

    I have tried my hand at your code by writing a Custom Marshaler (named CharArrayMarshaler) and then specifying that the "a" parameter be marshaled with my Custom Marshaler as follows :

    public void Y ([In][MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CharArrayMarshaler))] char[] a)

    but CharArrayMarshaler is not even invoked.

    I have not investigated why the Custom Marshaler is not invoked. But I suspect that this may have something to do with the call being made through IDispatch Invoke(). I'm not sure at this time.

    I then implementing the IDispatch interface for XHelper via ICustomQueryInterface. This essentially makes XHelper bypass the Interop Marshaler and allows it to handle Invoke() calls on its own.

    The XHelper code that I wrote goes something like the following :

        [ComVisible(true)]
        [Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
        [ProgId("CSDLLServer.XHelper")]
        [ClassInterface(ClassInterfaceType.AutoDispatch)]
        public class XHelper : ICustomQueryInterface, IDispatch
        {
            ...
        }
    

    My Invoke() method for Y() is as follows :

            public void Invoke
            (
                int dispId,
                ref Guid riid,
                int lcid,
                ushort wFlags,
                ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
                out object result,
                IntPtr pExcepInfo,
                IntPtr puArgErr
            )
    {
       ...
    
                    // 2 is the dispid of Y()
                    case 2:
                        {
                            int i = 0;
                            result = null;
    
                            // We create an array of pointers to the VARIANTs inside the 
                            // DISPPARAMS.
                            IntPtr[] pVariantArray = new IntPtr[pDispParams.cArgs];
    
                            IntPtr pVariantTemp = pDispParams.rgvarg;
                            for (i = 0; i < pDispParams.cArgs; i++)
                            {
                                pVariantArray[i] = pVariantTemp;
                                pVariantTemp += Marshal.SizeOf(typeof(VariantStructGeneric));
                            }
    
                            // We access the reference to the SAFEARRAY in the first VARIANT and 
                            // create a managed array from it. Note that this is an array of UInt16.
                            UInt16[] uiArray = Marshal.GetObjectForNativeVariant<UInt16[]>(pVariantArray[0]);
    
                            char[] chArray = new char[uiArray.Length];
    
                            // We convert the UInt16 array to a Char array.
                            for (i = 0; i < uiArray.Length; i++)
                            {
                                chArray[i] = Convert.ToChar(uiArray[i]);
                            }
    
                            Y(chArray);
    
                            break;
                        }
       ...
    }
    

    This worked well since the conversion from a UInt16 array to a Char array is done manually by my Invoke() method instead of relying on the Interop Marshaler.

    I think this may help you achieve your objectives. For more information on the ICustomQueryInterface interface, see : CustomQueryInterface

    I have also written an article with some code examples on ICustomQueryInterface and IDispatch. See :

    passing-a-reference-to-a-safearray-as-parameter-to-a-managed-com-event-handler-part-3

    Hope this will be helpful to you.