Discussion:
Marshalling C# Strings to VC++ 6.0
(too old to reply)
Scott Ballard
2010-04-12 14:37:45 UTC
Permalink
Raw Message
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
Raw Message
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


"Scott Ballard" <***@gmail.com> wrote in message
news:bf461423-60b3-4854-9c3f-***@c36g2000yqm.googlegroups.com...
> 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.
Scott Ballard
2010-04-12 22:27:35 UTC
Permalink
Raw Message
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.

On Apr 12, 4:06 pm, "Wilson, Phil" <***@wonderware.nospam.com>
wrote:
> 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
>
> "Scott Ballard" <***@gmail.com> wrote in message
>
> news:bf461423-60b3-4854-9c3f-***@c36g2000yqm.googlegroups.com...
>
>
>
> > 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.- Hide quoted text -
>
> - Show quoted text -
Scott Ballard
2010-04-13 14:20:17 UTC
Permalink
Raw Message
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.

On Apr 12, 4:27 pm, Scott Ballard <***@gmail.com> wrote:
> 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.
>
> On Apr 12, 4:06 pm, "Wilson, Phil" <***@wonderware.nospam.com>
> wrote:
>
>
>
> > 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
>
> > "Scott Ballard" <***@gmail.com> wrote in message
>
> >news:bf461423-60b3-4854-9c3f-***@c36g2000yqm.googlegroups.com...
>
> > > 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.- Hide quoted text -
>
> > - Show quoted text -- Hide quoted text -
>
> - Show quoted text -
Brian Muth
2010-04-15 21:09:42 UTC
Permalink
Raw Message
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...