From: Scott Ballard on 12 Apr 2010 12:51 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 12 Apr 2010 12:58 "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 12 Apr 2010 15:03 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 12 Apr 2010 15:10 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 12 Apr 2010 18:35 "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
|
Next
|
Last
Pages: 1 2 Prev: trouble with CDC Next: AfxOleInit() and CoInitializeEx in a non-GUI thread ? |