This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Download Microsoft Edge
More info about Internet Explorer and Microsoft Edge
Both the
System.String
and
System.Text.StringBuilder
classes have similar marshalling behavior.
Strings are marshalled as a COM-style
BSTR
type or as a null-terminated string (a character array that ends with a null character). The characters within the string can be marshalled as Unicode (the default on Windows systems) or ANSI.
Strings used in interfaces
The following table shows the marshalling options for the string data type when marshalled as a method argument to unmanaged code. The
MarshalAsAttribute
attribute provides several
UnmanagedType
enumeration values to marshal strings to COM interfaces.
Enumeration type
Description of unmanaged format
UnmanagedType.BStr
(default)
A COM-style
BSTR
with a prefixed length and Unicode characters.
UnmanagedType.LPStr
A pointer to a null-terminated array of ANSI characters.
UnmanagedType.LPWStr
A pointer to a null-terminated array of Unicode characters.
This table applies to
String
. For
StringBuilder
, the only options allowed are
UnmanagedType.LPStr
and
UnmanagedType.LPWStr
.
The following example shows strings declared in the
IStringWorker
interface.
public interface IStringWorker
void PassString1(string s);
void PassString2([MarshalAs(UnmanagedType.BStr)] string s);
void PassString3([MarshalAs(UnmanagedType.LPStr)] string s);
void PassString4([MarshalAs(UnmanagedType.LPWStr)] string s);
void PassStringRef1(ref string s);
void PassStringRef2([MarshalAs(UnmanagedType.BStr)] ref string s);
void PassStringRef3([MarshalAs(UnmanagedType.LPStr)] ref string s);
void PassStringRef4([MarshalAs(UnmanagedType.LPWStr)] ref string s);
Public Interface IStringWorker
Sub PassString1(s As String)
Sub PassString2(<MarshalAs(UnmanagedType.BStr)> s As String)
Sub PassString3(<MarshalAs(UnmanagedType.LPStr)> s As String)
Sub PassString4(<MarshalAs(UnmanagedType.LPWStr)> s As String)
Sub PassStringRef1(ByRef s As String)
Sub PassStringRef2(<MarshalAs(UnmanagedType.BStr)> ByRef s As String)
Sub PassStringRef3(<MarshalAs(UnmanagedType.LPStr)> ByRef s As String)
Sub PassStringRef4(<MarshalAs(UnmanagedType.LPWStr)> ByRef s As String)
End Interface
The following example shows the corresponding interface described in a type library.
interface IStringWorker : IDispatch
HRESULT PassString1([in] BSTR s);
HRESULT PassString2([in] BSTR s);
HRESULT PassString3([in] LPStr s);
HRESULT PassString4([in] LPWStr s);
HRESULT PassStringRef1([in, out] BSTR *s);
HRESULT PassStringRef2([in, out] BSTR *s);
HRESULT PassStringRef3([in, out] LPStr *s);
HRESULT PassStringRef4([in, out] LPWStr *s);
When the CharSet is Unicode or a string argument is explicitly marked as [MarshalAs(UnmanagedType.LPWSTR)] and the string is passed by value (not ref
or out
), the string is pinned and used directly by native code. Otherwise, platform invoke copies string arguments, converting from the .NET Framework format (Unicode) to the platform unmanaged format. Strings are immutable and are not copied back from unmanaged memory to managed memory when the call returns.
Native code is only responsible for releasing the memory when the string is passed by reference and it assigns a new value. Otherwise, the .NET runtime owns the memory and will release it after the call.
The following table lists the marshalling options for strings when marshalled as a method argument of a platform invoke call. The MarshalAsAttribute attribute provides several UnmanagedType enumeration values to marshal strings.
Enumeration type
Description of unmanaged format
UnmanagedType.LPTStr
A pointer to a null-terminated array of platform-dependent characters.
UnmanagedType.LPUTF8Str
A pointer to a null-terminated array of UTF-8 encoded characters.
UnmanagedType.LPWStr
A pointer to a null-terminated array of Unicode characters.
UnmanagedType.TBStr
A COM-style BSTR
with a prefixed length and platform-dependent characters.
VBByRefStr
A value that enables Visual Basic to change a string in unmanaged code and have the results reflected in managed code. This value is supported only for platform invoke. This is the default value in Visual Basic for ByVal
strings.
This table applies to String. For StringBuilder, the only options allowed are LPStr
, LPTStr
, and LPWStr
.
The following type definition shows the correct use of MarshalAsAttribute
for platform invoke calls.
class StringLibAPI
[DllImport("StringLib.dll")]
public static extern void PassLPStr([MarshalAs(UnmanagedType.LPStr)] string s);
[DllImport("StringLib.dll")]
public static extern void PassLPWStr([MarshalAs(UnmanagedType.LPWStr)] string s);
[DllImport("StringLib.dll")]
public static extern void PassLPTStr([MarshalAs(UnmanagedType.LPTStr)] string s);
[DllImport("StringLib.dll")]
public static extern void PassLPUTF8Str([MarshalAs(UnmanagedType.LPUTF8Str)] string s);
[DllImport("StringLib.dll")]
public static extern void PassBStr([MarshalAs(UnmanagedType.BStr)] string s);
[DllImport("StringLib.dll")]
public static extern void PassAnsiBStr([MarshalAs(UnmanagedType.AnsiBStr)] string s);
[DllImport("StringLib.dll")]
public static extern void PassTBStr([MarshalAs(UnmanagedType.TBStr)] string s);
Class StringLibAPI
Public Declare Auto Sub PassLPStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.LPStr)> s As String)
Public Declare Auto Sub PassLPWStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.LPWStr)> s As String)
Public Declare Auto Sub PassLPTStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.LPTStr)> s As String)
Public Declare Auto Sub PassLPUTF8Str Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.LPUTF8Str)> s As String)
Public Declare Auto Sub PassBStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.BStr)> s As String)
Public Declare Auto Sub PassAnsiBStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.AnsiBStr)> s As String)
Public Declare Auto Sub PassTBStr Lib "StringLib.dll" (
<MarshalAs(UnmanagedType.TBStr)> s As String)
End Class
Strings used in structures
Strings are valid members of structures; however, StringBuilder buffers are invalid in structures. The following table shows the marshalling options for the String data type when the type is marshalled as a field. The MarshalAsAttribute attribute provides several UnmanagedType enumeration values to marshal strings to a field.
Enumeration type
Description of unmanaged format
UnmanagedType.LPTStr
A pointer to a null-terminated array of platform-dependent characters.
UnmanagedType.LPUTF8Str
A pointer to a null-terminated array of UTF-8 encoded characters.
UnmanagedType.LPWStr
A pointer to a null-terminated array of Unicode characters.
UnmanagedType.ByValTStr
A fixed-length array of characters; the array's type is determined by the character set of the containing structure.
The ByValTStr
type is used for inline, fixed-length character arrays that appear within a structure. Other types apply to string references contained within structures that contain pointers to strings.
The CharSet
argument of the StructLayoutAttribute that is applied to the containing structure determines the character format of strings in structures. The following example structures contain string references and inline strings, as well as ANSI, Unicode, and platform-dependent characters. The representation of these structures in a type library is shown in the following C++ code:
struct StringInfoA
char * f1;
char f2[256];
struct StringInfoW
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
struct StringInfoT
TCHAR * f1;
TCHAR f2[256];
The following example shows how to use the MarshalAsAttribute to define the same structure in different formats.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct StringInfoA
[MarshalAs(UnmanagedType.LPStr)] public string f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct StringInfoW
[MarshalAs(UnmanagedType.LPWStr)] public string f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
[MarshalAs(UnmanagedType.BStr)] public string f3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct StringInfoT
[MarshalAs(UnmanagedType.LPTStr)] public string f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
<MarshalAs(UnmanagedType.LPStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
End Structure
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure StringInfoW
<MarshalAs(UnmanagedType.LPWStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
<MarshalAs(UnmanagedType.BStr)> Public f3 As String
End Structure
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Auto)> _
Structure StringInfoT
<MarshalAs(UnmanagedType.LPTStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
End Structure
Fixed-length string buffers
In some circumstances, a fixed-length character buffer must be passed into unmanaged code to be manipulated. Simply passing a string does not work in this case because the callee cannot modify the contents of the passed buffer. Even if the string is passed by reference, there is no way to initialize the buffer to a given size.
The solution is to pass a byte[]
or char[]
, depending on expected encoding, as the argument instead of a String. The array, when marked with [Out]
, can be dereferenced and modified by the callee, provided it does not exceed the capacity of the allocated array.
For example, the Windows GetWindowText
API function (defined in winuser.h) requires that the caller pass a fixed-length character buffer to which the function writes the window's text. The lpString
argument points to a caller-allocated buffer of size nMaxCount
. The caller is expected to allocate the buffer and set the nMaxCount
argument to the size of the allocated buffer. The following example shows the GetWindowText
function declaration as defined in winuser.h.
int GetWindowText(
HWND hWnd, // Handle to window or control.
LPTStr lpString, // Text buffer.
int nMaxCount // Maximum number of characters to copy.
A char[]
can be dereferenced and modified by the callee. The following code example demonstrates how ArrayPool<char>
can be used to pre-allocate a char[]
.
using System;
using System.Buffers;
using System.Runtime.InteropServices;
internal static class NativeMethods
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern void GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
public class Window
internal IntPtr h; // Internal handle to Window.
public string GetText()
char[] buffer = ArrayPool<char>.Shared.Rent(256 + 1);
NativeMethods.GetWindowText(h, buffer, buffer.Length);
return new string(buffer);
Imports System
Imports System.Buffers
Imports System.Runtime.InteropServices
Friend Class NativeMethods
Public Declare Auto Sub GetWindowText Lib "User32.dll" _
(hWnd As IntPtr, <Out> lpString() As Char, nMaxCount As Integer)
End Class
Public Class Window
Friend h As IntPtr ' Friend handle to Window.
Public Function GetText() As String
Dim buffer() As Char = ArrayPool(Of Char).Shared.Rent(256 + 1)
NativeMethods.GetWindowText(h, buffer, buffer.Length)
Return New String(buffer)
End Function
End Class
Another solution is to pass a StringBuilder as the argument instead of a String. The buffer created when marshalling a StringBuilder
can be dereferenced and modified by the callee, provided it does not exceed the capacity of the StringBuilder
. It can also be initialized to a fixed length. For example, if you initialize a StringBuilder
buffer to a capacity of N
, the marshaller provides a buffer of size (N
+1) characters. The +1 accounts for the fact that the unmanaged string has a null terminator while StringBuilder
does not.
In general, passing StringBuilder
arguments is not recommended if you're concerned about performance. For more information, see String parameters.
See also
Default Marshalling Behavior
Marshalling Strings
Blittable and Non-Blittable Types
Directional Attributes
Copying and Pinning