.NET Forum / .NET Framework / Interop / June 2005
passing string array to C++
|
|
Thread rating:  |
Bruce Parker - 12 Jun 2005 02:11 GMT I have an unmanaged api I need to call. The definition for it is the following:
DLL_API int Adapt( Global* g, char** strings, int numStrings);
I need to wrap this api in C++ and then pass a string array from c# to the wrapper api. Here is my attempt:
C++ wrapper DLL_API long MyAdapt( long g, WCHAR **strings, long numStrings) { char cbuf[2048]; int x; //I believe this is wrong - what is the right way for (x=0; x<numStrings; x++) unicodeToAscii( cbuf, (WCHAR* )strings[x], sizeof(cbuf) ); return( (long) ::Adapt( (Global* ) gr,(char**) cbuf, numStrings )) }
c# [DllImport("MyWrapper.dll", CharSet=CharSet.Auto)] private static extern int MyAdapt(int gr, string[] MyStrings,int numStrings);
When I execute MyAdapt I get an error telling me there is something wrong on how I am passing my parameters.
I am using the .NET Compact framework for my c# application.
Question 1: How do I pass a string array to unmanaged C++ (Embedded Visual C++)?
Question 2: What do the C++ parameters need to be?
Question 3: I need to convert the string array with my unicodeToAscii routine to make it a char**. How do I do this, including I do not know the size of the array until the size is passed to me through the numStrings parameter.
My C++ is weak now, I haven't done it 8 years, so I am really rusty.
"Peter Huang" [MSFT] - 13 Jun 2005 08:56 GMT Hi
The .NET P/Invoke Layer will help to pass the ansi version string to unmanaged dll. Here is a simple test. You may have a try and let me know the result.
[C++] CPPDLL_API long MyAdapt( long g, char **strings, long numStrings) { for(int i=0;i<numStrings;i++) { printf("%s \n",strings[i]); strings[i][2]='A'+i;//Change the string } return 0; }
[C#] [DllImport(@"..\..\..\CPPDLL\Debug\CPPDLL.dll",EntryPoint="MyAdapt",CharSet= CharSet.Ansi,SetLastError=true)] private static extern int MyAdapt(int g,[In,Out] string[] users, int numUsers);
public static void InteropStringArray() { string[] strs = new string[]{"Hello","World"}; int rt = MyAdapt(2,strs,2); foreach(string s in strs) { Console.WriteLine(s); } } [STAThread] static void Main(string[] args) { InteropStringArray(); }
BTW: I am not very familar with Embeded develop very much, the code above should work but I tested only on .NET framework. For compactframework or evc issue, you may try to post in the newsgroup below.
microsoft.public.dotnet.framework.compactframework microsoft.public.pocketpc.developer microsoft.public.windowsce.embedded.vc Best regards,
Peter Huang Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "AS IS" with no warranties, and confers no rights.
Bruce Parker - 14 Jun 2005 01:01 GMT The .NET Compact framework does not support Charset.Ansi. I need to be able to do this with Charset.Auto which is the same as Charset.Unicode. How would you do this using the Charset.Unicode. A WCHAR ** seems to be incorrect on the C++ side.
> Hi > [quoted text clipped - 50 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 14 Jun 2005 07:12 GMT Hi
Based on my research, all the System API on the WinCE is of Unicode version, so it is common to handle all the string in the winCE of unicode version. I think it is better for you to convert your dll to handle Unicode version string all the time.
Here is the WCHAR version P/Invoke for your reference.
[C++] CPPDLL_API long MyAdapt( long g, WCHAR **strings, long numStrings) { for(int i=0;i<numStrings;i++) { printf("%S \n",strings[i]); strings[i][2]='A'+i; } return 0; } extern "C" CPPDLL_API long MyAdapt( long g, WCHAR **strings, long numStrings);
[C#] [DllImport(@"..\..\..\CPPDLL\Debug\CPPDLL.dll",EntryPoint="MyAdapt",CharSet= CharSet.Unicode,SetLastError=true)] private static extern int MyAdapt(int g,[In,Out] string[] users, int numUsers); public static void InteropStringArray() { string[] strs = new string[]{"Hello","World"}; int rt = MyAdapt(2,strs,2); foreach(string s in strs) { Console.WriteLine(s); } } [STAThread] static void Main(string[] args) { InteropStringArray(); }
Best regards,
Peter Huang Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "AS IS" with no warranties, and confers no rights.
Bruce Parker - 15 Jun 2005 00:47 GMT I have tried myself the way you indicate below on how to pass the strings. Unfortunately it appears the .NET Compact framework marshalling does not support this. Can you verify this with the Microsoft team responsible for this and what is their response? I would think passing an array of strings from the .NET Compact framework to Embedded Visual C++ would work, but it doesn't. The error message I get back from .NET is "Bad arguments or incorrect declaration". I really need this mystery solved.
> Hi > [quoted text clipped - 47 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 15 Jun 2005 09:20 GMT Hi
So far I am contacting the related embeded support engineer. I will update you with new information ASAP.
Best regards,
Peter Huang Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "AS IS" with no warranties, and confers no rights.
"Peter Huang" [MSFT] - 17 Jun 2005 03:12 GMT Hi
Sorry for delay response, so far we are still investigating the issue, I will update you ASAP.
Thanks for your understanding!
Best regards,
Peter Huang Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "AS IS" with no warranties, and confers no rights.
Yan-Hong Huang[MSFT] - 20 Jun 2005 03:41 GMT Hello,
I am reviewing this issue thread. We need to submit it to compact framework support team and discuss it with them. That may need some more days. The support team may need some more information if necessary. Could you please post a reply here to let us know you are still monitoring it? So we will go ahead to process it.
Thanks very much.
Best regards, Yanhong Huang Microsoft Community Support
Get Secure! ¨C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! -http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as p&SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Bruce Parker - 21 Jun 2005 03:03 GMT Yes, I am still monitoring this.
> Hello, > [quoted text clipped - 16 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Yan-Hong Huang[MSFT] - 21 Jun 2005 08:24 GMT Thanks very much for the quick update. I am contacting CE support team today and we will reply here with more information as soon as possible.
Thanks again for your patience.
Best regards, Yanhong Huang Microsoft Community Support
Get Secure! ¨C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! -http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as p&SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
"Peter Huang" [MSFT] - 21 Jun 2005 08:35 GMT Hi
Thank you for your response, so far our related support team is looking into the issue. I will update you with new information ASAP.
Best regards,
Peter Huang Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "AS IS" with no warranties, and confers no rights.
Gary Lewis - 22 Jun 2005 00:23 GMT Greetings!
Unfortunately, as you've already seen, the .NET Compact Framework (NETCF) does not support passing a string array to unmanaged code. That said, I am working on some sample code to workaround the issue but it will take me a bit of time to complete. Look for a reply on Wednesday afternoon or Thursday morning after I've tested the code.
Cheers!
Gary Lewis MCAD Technical Lead Microsoft Mobile & Embedded Devices Developer Support
Gary Lewis - 22 Jun 2005 21:34 GMT OK, this may seem a bit convoluted, but here is the solution:
The normal code signature to be called would look like this:
private static extern int MyAdapt( int gbl, string[] strings, int count ); where strings is an array of strings and count is the number of strings. gbl is irrelevant in this context.
This signature cannot be used directly because NETCF 1.0 does not support it. The solution is to write C# code to convert the string array into a single delimited string (using a StringBuilder) and have C++ code convert it back into the desired form. Here is the C# code:
// I assume you have code to fill this private string[] m_strings;
// Signature of the C++ wrapper function [DllImport("Unmanaged.DLL")] private static extern int MyAdapt( int gbl, StringBuilder stringList, int count );
// Compute size of StringBuilder (or guess :-) int size = 1; // Allows for terminator
foreach ( string sz in m_strings ) size += sz.Length + 1;
// Fill a StringBuilder StringBuilder sb = new StringBuilder( size );
foreach ( string sz in m_strings ) { sb.Append( sz ); sb.Append( '\0' ); }
sb.Append( '\0' );
// Call the unmanaged code int rc = MyAdapt( 42, sb, m_strings.Length );
Next, we create a C++ wrapper function that converts the string back into an array of strings. Note that this implementation converts the Unicode strings from NETCF to ANSI strings. The code can be simplified a good deal if this is not required. Just allocate pStrs as a WCHAR** and fill in the pointers directly by computing the proper offsets into stringList. No additional memory allocations are required in this case. If your C++ code can work with Unicode this I recommend NOT converting them to ANSI.
Here is C++ code that includes conversion:
UNMANAGED_API int MyAdapt( int gbl, LPCWSTR stringList, int count ) { int ix, iy, iz; // Indexes int retCode = 0; // Return code to caller
// Allocate array of string pointers char** pStrs = new char * [ count ];
// Scan the embedded strings for ( ix = iz = 0; stringList[ ix ] != 0; ix++ ) { // Compute start and length of current Unicode string LPCWSTR pszBegin = &stringList[ ix ];
// Look for end of string for ( iy = 0; stringList[ ix + iy ] != 0; iy++ ) ;
// Allocate ANSI string area allowing for string termination pStrs[ iz ] = new char[ iy + 1 ];
// Translate the string int ret = WideCharToMultiByte( CP_ACP, 0, pszBegin, iy + 1, pStrs[ iz ], iy + 1, NULL, NULL );
// Set up for next string iz += 1; // Next output string ix += iy; // Next input string }
// Now call the internal function (the function we are wrapping)
// retcode = InternalMyAdapt( gbl, pStrs, count );
// Must free strings we allocated - skip this if the internal function // is responsible for the string array. for ( ix = 0; ix < count; ix++ ) delete [] pStrs[ ix ];
// Delete the array itself (see above) delete pStrs;
// Return result return retCode; }
Disclaimer for all above code:
// THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE.
Bruce Parker - 23 Jun 2005 18:49 GMT Thanks for the help.
Does this apply to all marshalling of passing arrays of pointers? I have other challenges in this area such as passing an array of pointers to class instances. I am at the point where I am going to let Embedded C++ managed these arrays and provide C++ methods for the C# to work with them.
In addition, will we see any improvements in this area with NETCF 2.0?
> OK, this may seem a bit convoluted, but here is the solution: > [quoted text clipped - 102 lines] > // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A > // PARTICULAR PURPOSE. Gary Lewis - 27 Jun 2005 19:44 GMT You wrote "Does this apply to all marshalling of passing arrays of pointers? "
Arrays of references to objects are problematic for NETCF 1.0 since we don't have the MarshalAs attribute to tell the CLR how to marshal the data that the data is referencing.
I suspect that the most straightforward implementation is the one I used - let C (not C++) manage the array and expose only array elements to Managed code via C-language wrapper functions. If you use C++ you will need to deal with keeping a copy of the "this" pointer around in your C# code during the life of your C++ class and handle (or alias) mangled names. Writing a set of C-language collection management functions should be fairly simple.
Gary
Gary Lewis - 29 Jun 2005 18:44 GMT To follow up in your other question: yes, NETCF 2.0 P/Invoke has MarshalAs and is much closer to the desktop in functionality.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|