Can MarshalAs interop attributes be set dynamically or overloaded?

Let's say I have an ATL based COM object that exposes the following method signature:

STDMETHOD(Store)(/*[in]*/ BSTR bstrEncoding, /*[in]*/ BSTR bstrMessage, /*[out, retval]*/ long *plResult);

The first parameter (bstrEncoding) in this method indicates what type of buffer (i.e. either "ansi" or "unicode") is actually contained in the second parameter (bstrMessage). To correctly use this method from managed code I need to be able to manipulate how the second parameter is marshalled in my custom RCW (created by using reflector to dissassemble a VS generated interop assembly).

For example:

If a unicode string is passed as the second param:

int x = Foo.Store("unicode", someString);

I'd simply accept the default behavior as provided by MarshalAs(UnmanagedType.BStr) in my interop wrapper

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)]
int Store([In, MarshalAs(UnmanagedType.BStr)] string bstrEncoding, [In, MarshalAs(UnmanagedType.BStr)] string bstrMessage);

But if I am wishing to invoke this method with the intent to pass binary content as the message body:

string ansiBuffer = Encoding.ASCII.GetString(someByteArray);
int y = Foo.Store("ansi", ansiBuffer);

I'd need to modify the marshalling logic to use UnmanagedType.AnsiBStr to prevent the contents from being converted from ansi to unicode as it gets passed.

MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)]
int Store([In, MarshalAs(UnmanagedType.BStr)] string bstrEncoding, [In, MarshalAs(UnmanagedType.AnsiBStr)] string bstrMessage);

Is there anyway I can control this at runtime or some way I can overload the calls to Foo.Store in my interop code (i.e "StoreA" and "StoreW") in such a fashion that they both ultimately invoke the same target method in the COM object

Thanks




Answer this question

Can MarshalAs interop attributes be set dynamically or overloaded?

  • Cem Usta

    you could try declaring two DllImport's for the same function. You'd have to make sure they have different parameter types. I don't know how successful you'd be with the target function uses BSTR.

    If it were me, I'd simply write two C++ functions to get around the CLRs marhsalling of String types. They would just accept a type argument (I'd make it a enum) and a byte array. It would then create appropriate variables to pass as arguments to the function you already have.



  • shruthi04

    A couple of things:

    Attributes are declarative; they're cooked into the code upon compilation.

    Encoding.ASCII.GetString() converts ASCII bytes to a Unicode string, not to an ANSI string. Your sample code where you call Foo.Store after calling ASCII.GetString() is wrong by passing "ansi".

    If you want the charset of a parameter to change depending the value of another parameter then you can't use automatic mashalling. You'll have to change your method to accept a byte array rather than a BSTR and ensure you're passing the correct data.



  • Al-Arabi

    I think it might be a way using custom marshaling. Instead of string define a struct that has enconding and bytes and apply your marshaling attribute to it. When asked to marshal to COM, do the right thing by looking at encoding. Never tried it though.
  • Ri_Kamen

    Thanks Peter.

    Yep, I know the encoding doesn't really do anything for me other than grab the bytes from an array and convert them to a unicode sytem.string, but that is what the interop method siggy for the COM object expects. As I mentioned, if I hack the interop code to use MarshalAs(UnmanagedType.AnsiBStr) I am able to extract the body of the BSTR as an ascii byte array (without the unicode padding provided by the call to Encoding.GetString), but if I send a unicode BSTR the marshalling also converts that buffer to an ansi representation (i.e. "H/0e/0l/0l/0o/0/0" becomes "Hello/0/0/0/0/0/0" when it gets received in the COM method (and pity the fool that actually may have embedded actual unicode characters in the string).

    I guess what I am really asking here is if there is anyway I can doctor the managed client facing interop interface so that I can provide 2 separate method signatures that essentially resolve to the same COM call, only using different marshalling instructions (short of creating a second COM wrapper that does the same thing).

    i.e "StoreA" resolves to a version of Store that marshalls UnmanagedType.AnsiBStr, and "StoreW" also resolves to Store but marshalls UnmanagedType.BStr

    In other words, expose an interop interface containing StoreA and StoreW method signatures with a corresponding implementation of each method that invoke the same COM method "Store" only applying different marshalling attributes. If I try to declare two versions of the extern Store signature in this fashion the compiler complains because they have the same signature with the exception of the MarshalAs attributes (which is expected behavior but is essentially what I am trying to accomplish here).

    It is much more attractive for us to modify the managed client than it would be to change the existing COM object's interface. Am I stuck with needing to create a native COM wrapper that exposes separate methods and then let it resolve the actual calls to the underlying dll I was just hoping I could somehow modify the interop wrapper to do the same thing for me without using an additional COM middleman as a facade.



  • Can MarshalAs interop attributes be set dynamically or overloaded?