From: Scott Ballard on
Greetings,

I'm having difficulty marshalling strings in a C# COM server
(VS2008, .NET Framework 2.0) 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.
From: Giovanni Dicanio on
"Scott Ballard" <scott.ballard(a)gmail.com> ha scritto nel messaggio
news:7e021c23-b175-4082-ada4-0f5e0ae88805(a)h27g2000yqm.googlegroups.com...

> I'm having difficulty marshalling strings in a C# COM server
> (VS2008, .NET Framework 2.0) 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.

I'm not sure about that... in fact, you wrote that your C++ app uses a COM
interface with a method like this:

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

So the C++ app is expecting a BSTR (COM string type), which is Unicode
UTF-16 (to my knowledge).

Could you please clarify your goal?

Am I missing something?

Thanks,
Giovanni


From: Scott Ballard on
On Apr 12, 10:58 am, "Giovanni Dicanio"
<giovanniDOTdica...(a)REMOVEMEgmail.com> wrote:
> "Scott Ballard" <scott.ball...(a)gmail.com> ha scritto nel messaggionews:7e021c23-b175-4082-ada4-0f5e0ae88805(a)h27g2000yqm.googlegroups.com...
>
> > I'm having difficulty marshalling strings in a C# COM server
> > (VS2008, .NET Framework 2.0) 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.
>
> I'm not sure about that... in fact, you wrote that your C++ app uses a COM
> interface with a method like this:
>
> > HRESULT Foo([in] BSTR incoming, [out] BSTR* outgoing);
>
> So the C++ app is expecting a BSTR (COM string type), which is Unicode
> UTF-16 (to my knowledge).
>
> Could you please clarify your goal?
>
> Am I missing something?
>
> Thanks,
> Giovanni

Hi Giovanni,

Thank you for the quick 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.

In C# you can apply the MarshalAs.AnsiBStr (COM-style BSTR with a
prefixed length and ANSI characters) attribute to a 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.
From: Hector Santos on
We had similar porting issues when we put together our .NET API.

What was a big helper was the

Microsoft P/Invoke Interop Assistant utility

that translate C/C++ prototypes code to .NET code for C# or VB.

You have to download it. I think from here:

http://clrinterop.codeplex.com/releases/view/14120
http://clrinterop.codeplex.com/Wikipage
http://channel9.msdn.com/posts/funkyonex/The-P-Invoke-Interop-Assistant/

If you want a rich set of examples of C/C++ structures and 250+ API
ported to a .NET interface which I also compiled into a .NET COM
interface, see

http://www.winserver.com/public/wcsdk

To maybe save you time that I wasted, I spent a lot of time thinking I
had to do my own marshaling coding with .NET class wrappers, but it
turned out that was just getting the prototyping and delegates right
worked most of the time.

But if I recall one of the issue with many of the functions is that
VARIANTS are not supported out of the box (BSTR is a variant) or I
didn't figure it out.

However, since we already had a C/C++ proxy DLL that does BSTR
variants interfacing for Classic VB and COM API support, using this
proxy (WCVB.DLL) for some of the functions in .NET API) this turned
out easier than trying to use some 3rd party .NET variant class I had
found. I don't recall if I could of used the iMarshalling stuff or I
tried or not, but it was just easily to use the C/C++ to BSTR proxy
DLL we already had.

For example, most of the API use our WIN32 DLL wcsrv2.dll, but for
some other functions we use the proxy WCVB.DLL.

Example:

The ported function in .NET

[DllImport("wcvb.dll",
EntryPoint="vbWcGetPrivateProfileString",
SetLastError=true)]
public extern static string WcGetPrivateProfileString(
string section,
string key,
string def,
string ini);

the pure WIN32 function is in wcserver.h header for WCSRV2.DLL

BOOL APIENTRY WcGetPrivateProfileString
(const char *sect,
const char *key,
const char *defvalue,
char *dest,
DWORD *destsize,
const char *inifile);

Since this returns as a parameter an asciiz string, it doesn't lend
itself to COM and .NET. It is better to have it return a string to
avoid any marshaling conversion code on your own. So a 2nd DLL
provides a VARIANT version wrapper function for the above C WIN32
function:

BSTR APIENTRY vbWcGetPrivateProfileString
(const char *sect,
const char *key,
const char *defvalue,
const char *inifile)
{
char *dest = NULL;
DWORD size = 0;
while (1) {
size += 1024;
dest = new CHAR[size];
if
(WcGetPrivateProfileString(sect,key,defvalue,dest,&size,inifile)) {
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
delete dest;
return NULL;
}
}
BSTR result = SysAllocStringLen((OLECHAR *)dest, (strlen(dest)+1)/2);
delete dest;
return result;
}

Hope the above gives you stuff things to look at.

--


Scott Ballard wrote:

> Greetings,
>
> I'm having difficulty marshalling strings in a C# COM server
> (VS2008, .NET Framework 2.0) 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.



--
HLS
From: Giovanni Dicanio on
"Scott Ballard" <scott.ballard(a)gmail.com> ha scritto nel messaggio
news:21fc1384-12fe-463b-aef5-b6084e297475(a)s9g2000yqa.googlegroups.com...

> Thank you for the quick response.

You're welcome.


> The whole point of this exercise is
> to replace an old VC++6.0 COM server with a modern C# COM Server,

I think that it is possible to write modern C++ COM Servers with C++ and
ATL.


> 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.

I'm not sure I understood well.
Could it be possible for you to post a sample short string byte dump in the
actual mode and in the expected desired mode?


> In C# you can apply the MarshalAs.AnsiBStr (COM-style BSTR with a
> prefixed length and ANSI characters) attribute to a 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.

Would it be possible for you to build the "ANSI BSTR" manually in C# code?
Basically: C# strings are Unicode UTF-16; convert from Unicode to some
ANSI/MBCS encoding, and call SysAllocStringByteLen from C#:

http://msdn.microsoft.com/en-us/library/ms221637(VS.100).aspx

The documentation reads:

> Takes an ANSI string as input, and returns a BSTR that contains
> an ANSI string. Does not perform any ANSI-to-Unicode translation.


HTH,
Giovanni