Discussion:
Marshalling C# Strings to VC++ 6.0
(too old to reply)
Scott Ballard
2010-04-12 14:37:45 UTC
Permalink
Greetings,

I'm having difficulty marshalling strings in a C# COM server back to a
VC++ 6.0 client application. The C++ application wants to consume a
single byte per character string, but the C# application is sending it
back as a two byte per character string. Fundamentally I know what
the problem is (C# strings are UNICODE) I just don't know where/how to
inject the code to fix it. Unfortunately I can't touch the C++
application; it must remain unchanged. It was written against a COM
interface that defines the method like this in the IDL:

HRESULT Foo([in] BSTR incoming, [out] BSTR* outgoing);

In C# the interface is defined like this:

void Foo([In, MarshalAs(UnmanagedType.BStr) string incoming,
[MarshalAs(UnmanagedType.BStr] out string outgoing);

I've tried different MarshalAs types and an ICustomMarshaler for the
"outgoing" string to no avail (I can provide additional details if
needed). The odd thing is the C# COM server has no trouble reading
the "incoming" string from C++. I'm really hoping someone can give me
a pointer or two on how to do this. Thank you very much for your
help.

Regards,

Scott B.
Wilson, Phil
2010-04-12 22:06:39 UTC
Permalink
I'm not sure what it means when you say that the C++ wants to consume a
single byte per character string. BSTR is Unicode, so you've got a Unicode
IDL that wants Unicode strings and C# code that returns them. Are you saying
that in spite of the IDL being Unicode the VC++ actually wants Ansi
characters?
--
Phil Wilson
The Definitive Guide to Windows Installer
http://www.apress.com/book/view/1590592972
Post by Scott Ballard
Greetings,
I'm having difficulty marshalling strings in a C# COM server back to a
VC++ 6.0 client application. The C++ application wants to consume a
single byte per character string, but the C# application is sending it
back as a two byte per character string. Fundamentally I know what
the problem is (C# strings are UNICODE) I just don't know where/how to
inject the code to fix it. Unfortunately I can't touch the C++
application; it must remain unchanged. It was written against a COM
HRESULT Foo([in] BSTR incoming, [out] BSTR* outgoing);
void Foo([In, MarshalAs(UnmanagedType.BStr) string incoming,
[MarshalAs(UnmanagedType.BStr] out string outgoing);
I've tried different MarshalAs types and an ICustomMarshaler for the
"outgoing" string to no avail (I can provide additional details if
needed). The odd thing is the C# COM server has no trouble reading
the "incoming" string from C++. I'm really hoping someone can give me
a pointer or two on how to do this. Thank you very much for your
help.
Regards,
Scott B.
Scott Ballard
2010-04-12 22:27:35 UTC
Permalink
Hi Phil,

Thank you for your response. The whole point of this exercise is to
replace an old VC++6.0 COM server with a modern C# COM Server, without
touching the old VC++6.0 client application. If I run the C++ client
application in a debugger I can definitely see the old C++ COM server
returns the string as one byte wide characters (despite the IDL
defining BSTR type). However, when I replace the C++ COM server with
C# COM server and run the C++ client application in the debugger I can
see the new C# COM Server is returning two byte wide characters.

So yes, it appears the C++ application wants ANSI characters despite
the IDL BSTR designation. In C# you can apply the MarshalAs.AnsiBStr
(COM-style BSTR with a prefixed length and ANSI characters) attribute
to an interface method's variable, but this only works for platform
invoke calls. I sure wish I could use this for a regular COM interop
call. I'm open to other suggestions too. Thank you again.

Regards,
Scott B.
Post by Wilson, Phil
I'm not sure what it means when you say that the C++ wants to consume a
single byte per character string. BSTR is Unicode, so you've got a Unicode
IDL that wants Unicode strings and C# code that returns them. Are you saying
that in spite of the IDL being Unicode the VC++ actually wants Ansi
characters?
--
Phil Wilson
The Definitive Guide to Windows Installerhttp://www.apress.com/book/view/1590592972
Post by Scott Ballard
Greetings,
I'm having difficulty marshalling strings in a C# COM server back to a
VC++ 6.0 client application.  The C++ application wants to consume a
single byte per character string, but the C# application is sending it
back as a two byte per character string.  Fundamentally I know what
the problem is (C# strings are UNICODE) I just don't know where/how to
inject the code to fix it.  Unfortunately I can't touch the C++
application; it must remain unchanged.  It was written against a COM
HRESULT Foo([in] BSTR incoming, [out] BSTR* outgoing);
void Foo([In, MarshalAs(UnmanagedType.BStr) string incoming,
[MarshalAs(UnmanagedType.BStr] out string outgoing);
I've tried different MarshalAs types and an ICustomMarshaler for the
"outgoing" string to no avail (I can provide additional details if
needed).  The odd thing is the C# COM server has no trouble reading
the "incoming" string from C++.  I'm really hoping someone can give me
a pointer or two on how to do this.   Thank you very much for your
help.
Regards,
Scott B.- Hide quoted text -
- Show quoted text -
Scott Ballard
2010-04-13 14:20:17 UTC
Permalink
This is solved. It turns out the old C++ code was using
SysAllocStringByteLen to create an ANSI BSTR (one byte per
character). I modified the new C# code to P/Invoke
SysAllocStringByteLen and everything works fine now. I never thought
of jamming the ANSI output of SysAllocStringByteLen into a C# string.
The C# string looks strange in the debugger (since it reads two bytes
per character and the ANSI string is only one byte per character), but
it works!

Regards,

Scott B.
Post by Scott Ballard
Hi Phil,
Thank you for your response.  The whole point of this exercise is to
replace an old VC++6.0 COM server with a modern C# COM Server, without
touching the old VC++6.0 client application.  If I run the C++ client
application in a debugger I can definitely see the old C++ COM server
returns the string as one byte wide characters (despite the IDL
defining BSTR type).  However, when I replace the C++ COM server with
C# COM server and run the C++ client application in the debugger I can
see the new C# COM Server is returning two byte wide characters.
So yes, it appears the C++ application wants ANSI characters despite
the IDL BSTR designation.  In C# you can apply the MarshalAs.AnsiBStr
(COM-style BSTR with a prefixed length and ANSI characters) attribute
to an interface method's variable, but this only works for platform
invoke calls.  I sure wish I could use this for a regular COM interop
call.  I'm open to other suggestions too.  Thank you again.
Regards,
Scott B.
Post by Wilson, Phil
I'm not sure what it means when you say that the C++ wants to consume a
single byte per character string. BSTR is Unicode, so you've got a Unicode
IDL that wants Unicode strings and C# code that returns them. Are you saying
that in spite of the IDL being Unicode the VC++ actually wants Ansi
characters?
--
Phil Wilson
The Definitive Guide to Windows Installerhttp://www.apress.com/book/view/1590592972
Post by Scott Ballard
Greetings,
I'm having difficulty marshalling strings in a C# COM server back to a
VC++ 6.0 client application.  The C++ application wants to consume a
single byte per character string, but the C# application is sending it
back as a two byte per character string.  Fundamentally I know what
the problem is (C# strings are UNICODE) I just don't know where/how to
inject the code to fix it.  Unfortunately I can't touch the C++
application; it must remain unchanged.  It was written against a COM
HRESULT Foo([in] BSTR incoming, [out] BSTR* outgoing);
void Foo([In, MarshalAs(UnmanagedType.BStr) string incoming,
[MarshalAs(UnmanagedType.BStr] out string outgoing);
I've tried different MarshalAs types and an ICustomMarshaler for the
"outgoing" string to no avail (I can provide additional details if
needed).  The odd thing is the C# COM server has no trouble reading
the "incoming" string from C++.  I'm really hoping someone can give me
a pointer or two on how to do this.   Thank you very much for your
help.
Regards,
Scott B.- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -
Brian Muth
2010-04-15 21:09:42 UTC
Permalink
Congratulations on solving this!

I'd try to locate the original author of that code and then shoot him. That
would prevent a repetition of that kind of coding.

Loading...