You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/framework/interop/default-marshalling-for-strings.md
+37-14Lines changed: 37 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,8 @@
1
1
---
2
2
title: "Default Marshalling for Strings"
3
3
description: Review the default marshalling behavior for strings in interfaces, platform invoke, structures, & fixed-length string buffers in .NET.
4
-
ms.date: 10/04/2021
4
+
ms.date: 03/11/2026
5
+
ai-usage: ai-assisted
5
6
dev_langs:
6
7
- "csharp"
7
8
- "vb"
@@ -239,7 +240,7 @@ int GetWindowText(
239
240
);
240
241
```
241
242
242
-
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[]`.
243
+
A `char[]` can be dereferenced and modified by the callee. The recommended approach is to use <xref:System.Buffers.ArrayPool`1>to rent a `char[]`, which avoids repeated heap allocations. The following code example demonstrates this pattern.
243
244
244
245
```csharp
245
246
usingSystem;
@@ -249,7 +250,7 @@ using System.Runtime.InteropServices;
Another solution is to pass a <xref:System.Text.StringBuilder> as the argument instead of a <xref:System.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.
288
-
289
-
> [!NOTE]
290
-
> In general, passing `StringBuilder` arguments is not recommended if you're concerned about performance. For more information, see [String parameters](../../standard/native-interop/best-practices.md#string-parameters).
299
+
You might also consider passing a <xref:System.Text.StringBuilder> instead of a <xref:System.String>. The buffer that's created when a `StringBuilder` is marshalled can be dereferenced and modified by the callee, provided it doesn't 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` doesn't.
300
+
301
+
> [!CAUTION]
302
+
> Avoid `StringBuilder` parameters when performance matters. Marshalling a `StringBuilder`*always* creates a native buffer copy. A typical call to get a string out of native code can result in four allocations:
303
+
>
304
+
> 1. A managed `StringBuilder` buffer.
305
+
> 1. A native buffer allocated during marshalling.
306
+
> 1. If `[Out]`, the native buffer contents are copied into a newly allocated managed array.
307
+
> 1. A `string` allocated by `ToString()`.
308
+
>
309
+
> Reusing the same `StringBuilder` across calls saves only one allocation. Using a character buffer rented from `ArrayPool<char>` is much more efficient—it reduces subsequent calls to just the allocation for `ToString()`.
310
+
>
311
+
> Additionally, the `StringBuilder` capacity does **not** include a hidden null terminator, which interop always accounts for. This is a common mistake, because most APIs want the size of the buffer *including* the null. This can result in wasted or unnecessary allocations, and it prevents the runtime from optimizing `StringBuilder` marshalling to minimize copies.
312
+
>
313
+
> For more information, see [String parameters](../../standard/native-interop/best-practices.md#string-parameters) and [CA1838: Avoid `StringBuilder` parameters for P/Invokes](../../fundamentals/code-analysis/quality-rules/ca1838.md).
0 commit comments