Strings
Read time 5 minutesLast updated 12 days ago
When you pass a string between managed and unmanaged code, in most cases, the default marshalling behavior makes a copy of the string and converts it between the .NET and default platform formats, if necessary. You can change this behavior with a MarshalAsAttribute. Refer to Strings used in platform invoke for more information.
The following examples demonstrate ways to pass string data between managed and unmanaged functions.
#include <stdlib.h> // malloc#include <string.h> // strlen, memcpy#if defined(_WIN32)#include <objbase.h> // CoTaskMemAlloc#endif#if defined(_WIN32)void* Allocate(size_t size){ // .NET runtime assumes the COM allocator when freeing memory returned from native code on Windows-based platforms return CoTaskMemAlloc(size);}#elsevoid* Allocate(size_t size){ // .NET runtime assumes the C allocator when freeing memory returned from native code on non-Windows platforms return malloc(size);}#endifchar* AllocateAndCopyString(const char* value){ size_t bufferSize = strlen(value) + 1; void* ret = Allocate(bufferSize); memcpy(ret, value, bufferSize); return (char*)ret;}extern "C" { void SendString(const char* message) { printf("%s\n", message); // Shown in Player.log fflush(stdout); } const char* GetUnmanagedString() { return AllocateAndCopyString("Hello from C\u002B\u002B"); // The runtime frees this with the matching allocator } void SendStringArray(const char** messages, int length) { for(int i = 0; i < length; i++) { printf("%s\n", messages[i]); } fflush(stdout); } void SendCollectionsString(const char* strPtr, int length) { printf("%.*s\n", length, strPtr); fflush(stdout); }}
This C# example uses the unmanaged functions to demonstrate how to:
- pass a string to unmanaged function
- return a string from an unmanaged function
- pass a string array to an unmanaged function
using UnityEngine;using System;using System.Runtime.InteropServices;public class StringMarshalExample : MonoBehaviour{ // Pass string parameter. Marshal as UTF-8 so non-ASCII text is preserved; // the default string marshalling isn't Unicode-safe on every platform. [DllImport("__Internal")] private static extern void SendString([MarshalAs(UnmanagedType.LPUTF8Str)] string message); // Return string from native. Marshalling the return value as LPUTF8Str makes // the runtime copy the string into managed memory and free the native buffer // with the platform's default allocator (CoTaskMemFree on Windows, free // elsewhere), which matches the Allocate() function in the native code. [DllImport("__Internal")] [return: MarshalAs(UnmanagedType.LPUTF8Str)] private static extern string GetUnmanagedString(); // Pass string array parameter. Marshal each element as UTF-8 for the same // reason as SendString; without ArraySubType the elements default to the // platform's ANSI marshalling, which isn't Unicode-safe. [DllImport("__Internal")] private static extern void SendStringArray( [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPUTF8Str)] string[] strings, int length); void Start() { // Pass strings to native function SendString("Hello from C#"); SendString("七転び八起き"); // Get a string from the native function. The runtime copies it into managed // memory and frees the native buffer for you. string managedStr = GetUnmanagedString(); Debug.Log(managedStr); // Pass string arrays to native function string[] strings = new string[] { "one", "two", "three" }; SendStringArray(strings, strings.Length); }}
The Unity.Collections namespace contains a few types that you can use to work with strings stored in unmanaged memory. Use these types to avoid the frequent string copy and garbage collection that occurs with the System.String class.
The following examples demonstrate a few ways that you can pass strings stored in Unity.Collections types to unmanaged functions (such as the function in the previous C/C++ code example). These examples rely on pointers and require an context.
SendCollectionsStringunsafeusing Unity.Collections;using Unity.Collections.LowLevel.Unsafe;using System;using System.Runtime.InteropServices;using System.Text;public class CollectionStringExample{ // Pass a pointer to a Unity.Collections object containing a string [DllImport("__Internal")] private static extern void SendCollectionsString(IntPtr textData, int letterCount); public static void RunExamples() { unsafe // Unsafe because of use of pointers to Collections buffers { // Pass pointer to NativeText instance var nativeText = new NativeText("十人十色", Allocator.Temp); SendCollectionsString((IntPtr)nativeText.GetUnsafePtr(), nativeText.Length); nativeText.Dispose(); // Pass pointer to FixedString instance var fixedStr = new FixedString128Bytes("Fixed string"); SendCollectionsString((IntPtr)fixedStr.GetUnsafePtr(), fixedStr.Length); // Pass pointer to NativeArray containing a string of UTF8 characters byte[] utf8Bytes = Encoding.UTF8.GetBytes("雨降って地固まる"); // Managed strings are UTF-16 var nativeArray = new NativeArray<byte>(utf8Bytes, Allocator.Temp); void* unsafePtr = nativeArray.GetUnsafePtr(); SendCollectionsString((IntPtr)unsafePtr, nativeArray.Length); nativeArray.Dispose(); } }}