Marshaling Strings

team lib

Strings pose a problem when you're calling unmanaged methods . Most managed types map onto one unmanaged equivalent-for example, a Boolean maps onto a C++ bool- but a managed string can map onto several unmanaged types.

When you're calling Windows API functions, a managed string can map onto four unmanaged string types:

  • ANSI strings, represented by the LPSTR type

  • Unicode strings, represented by LPWSTR

  • System-dependent strings, represented by LPTSTR

  • COM strings, represented by BSTR

    Note 

    The system-dependent type, LPTSTR , is provided so that the same API function can be used on both Unicode and ANSI systems. LPTSTR will be represented by LPSTR or LPWSTR , depending on whether the ANSI or Unicode version, respectively, of the Windows API function is being called.

The default conversion for managed string arguments depends on the information supplied in the Platform Invoke prototype. As I explained in Chapter 12, the CharSet parameter can be used with the DllImport attribute to specify which character encoding to use. Table 13-2 summarizes the values that CharSet can take and the default values assumed by the .NET programming languages.

Table 13-2: Values for the DllImport CharSet Parameter

CharSet Value

Description

Ansi

Marshals strings as ANSI characters . This is the default value for Visual Basic .NET and Microsoft Visual C#.

Auto

Chooses ANSI or Unicode, depending on the target system. The default is ANSI on Windows 98 and Windows ME, and Unicode on Windows NT, Windows 2000, Windows XP, and Windows 2003 Server.

None

This value is obsolete and is equivalent to CharSet.Ansi. This is the default value for managed C++.

Unicode

Marshals strings as Unicode characters.

String and StringBuilder

When marshaling strings, remember that .NET System.String objects are immutable: once created, their content cannot be changed. This means that System.String objects can be used only as [in] parameters and return values; you should use the System.Text.StringBuilder type to represent string arguments that will be marshaled back to the managed caller.

To use a C-style string as a return type from an unmanaged function, you will normally use a managed string, as shown in the following C# code fragment:

 //Unmanagedfunction char*ReturnsAString(); //PlatformInvokeprototype [DllImport("mydll.dll")] publicexternstringReturnsAString(); 

When a return value is marshaled in this way, the interop marshaler will assume that it has to free the unmanaged memory returned by the function once it has created the managed string and initialized it. Occasionally this is not the case: for instance, the GetCommandLine API returns a string buffer that is owned by the system and which must not be freed by the caller. In this case, you need to specify an IntPtr as the return type. An IntPtr is an integer that is large enough to hold a pointer, but the interop marshaler will not automatically treat this as a pointer and will not free the memory it references.

Here is a Visual C# example showing how to return an IntPtr and reference the string through the IntPtr :

 //TheGetCommandLineAPIreturnsapointertoabuffer //thatisownedbythesystem,andwhichmustnotbe //freedbythecaller [DllImport("kernel32.dll",CharSet=CharSet.Auto)] publicstaticexternIntPtrGetCommandLine(); //CallGetCommandLine IntPtrip=GetCommandLine(); stringcmdLine=Marshal.PtrToStringAuto(ip); 

Note how the Marshal.PtrToStringAuto method is used to marshal the data referenced by the IntPtr back into a string.

When a string argument to an unmanaged function needs to be marshaled back to the caller, you need to use a StringBuilder . In the following example, the FormatMessage API builds a string and passes it back in the fifth argument:

 //ThePlatformInvokeprototypeforFormatMessage [DllImport("kernel32.dll",CharSet=CharSet.Auto)] publicstaticexternintFormatMessage(intflags, IntPtrsource,intmessageId, intlangId,StringBuilderbuff, intsize,IntPtrargs); //CallingthefunctionfromC#code StringBuilderbuff=newStringBuilder(256); FormatMessage(TestPI.FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero,errCode,0, buff,buff.Capacity,IntPtr.Zero); Console.WriteLine("Errormessage:{0}",buff); 

A StringBuilder object needs to be constructed so that it can be passed to the FormatMessage function, and you need to ensure that the object has enough capacity to hold the returned string.

 
team lib


COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net