ドキュメント

Unity Engine

Unity Engine

Pass strings between C# and native code

Marshal strings and string arrays between C# and native code, and pass Unity.Collections strings without extra copies.
読み終わるまでの所要時間 5 分最終更新 1日前

When you pass a string between managed and unmanaged code, the default marshalling behavior usually makes a copy of the string and converts it between the .NET format and the default platform format, if necessary. You can change this behavior with
MarshalAsAttribute
(Microsoft). For more information, refer to Strings used in platform invoke (Microsoft).
注
Different platforms use different memory allocators. When native code returns a buffer that the .NET marshaller later frees, you must allocate it with the allocator the runtime expects the COM allocator (
CoTaskMemAlloc
) on Windows and the C allocator (
malloc
) on other platforms. Allocating and freeing memory with mismatched functions can crash your program.

Pass managed strings

The following C++ code defines unmanaged functions that receive and return string data:
#include <stdio.h>#include <stdlib.h>#include <string.h>#if defined(_WIN32)#include <objbase.h>#endif#if defined(_WIN32)void* Allocate(size_t size){ // On Windows, the runtime uses the COM allocator to free memory that native code returns. return CoTaskMemAlloc(size);}#elsevoid* Allocate(size_t size){ // On other platforms, the runtime uses the C allocator to free memory that native code returns. 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) { // The output appears in the Player log file. printf("%s\n", message); fflush(stdout); } const char* GetUnmanagedString() { // The runtime frees this buffer with the matching allocator. return AllocateAndCopyString("Hello from C\u002B\u002B"); } 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); }}
注
For information about the annotations you need to compile and call functions such as these as part of a dynamically loaded library, refer to Call unmanaged functions from managed code.
The following C# example uses these unmanaged functions to do the following:
  • Pass a string to an 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 a string parameter. Marshal as UTF-8 to preserve non-ASCII text. // The default string marshalling isn't Unicode-safe on every platform [DllImport("__Internal")] private static extern void SendString([MarshalAs(UnmanagedType.LPUTF8Str)] string message); // Return a string from native code. When you marshal the return value as LPUTF8Str, // the runtime copies the string into managed memory and frees the native buffer // with the default allocator of the platform: CoTaskMemFree on Windows and free elsewhere. // This matches the Allocate() function in the native code. [DllImport("__Internal")] [return: MarshalAs(UnmanagedType.LPUTF8Str)] private static extern string GetUnmanagedString(); // Pass a string array parameter. Marshal each element as UTF-8 for the same reason as SendString. // Without ArraySubType, the elements use the ANSI marshalling of the platform, 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 the 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. string managedStr = GetUnmanagedString(); Debug.Log(managedStr); // Pass a string array to the native function. string[] strings = new string[] { "one", "two", "three" }; SendStringArray(strings, strings.Length); }}
重要
The scripting runtime expects managed strings to be immutable. Don't write unmanaged code that changes the data a managed string references, because it can cause unpredictable behavior in your application.
When you marshal strings as
LPUTF8Str
, as the previous example does, native code receives correct UTF-8 bytes. However,
printf
doesn't display UTF-8 text the same way on every platform. On platforms where the console and C runtime default to UTF-8, such as macOS, Linux, Android, and iOS, the
printf
calls in the example display non-ASCII text correctly. On Windows, the console and C runtime default to a non-UTF-8 code page, so
printf
output of UTF-8 text can appear garbled even though the marshalled data is correct.
To print non-ASCII text on Windows, use one of the following approaches:
  • Configure the console for UTF-8, for example with
    SetConsoleOutputCP(CP_UTF8)
    .
  • Convert the UTF-8 bytes to UTF-16 with
    MultiByteToWideChar
    and print them with the wide-character APIs.

Pass strings stored in unmanaged collection types

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
SendCollectionsString
function in the previous C/C++ code example). These examples rely on pointers and require an
unsafe
context.
using 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 that contains a string. [DllImport("__Internal")] private static extern void SendCollectionsString(IntPtr textData, int letterCount); public static void RunExamples() { // Use an unsafe block because the code uses pointers to Collections buffers. unsafe { // Pass a pointer to a NativeText instance. var nativeText = new NativeText("十人十色", Allocator.Temp); SendCollectionsString((IntPtr)nativeText.GetUnsafePtr(), nativeText.Length); nativeText.Dispose(); // Pass a pointer to a FixedString instance. var fixedStr = new FixedString128Bytes("Fixed string"); SendCollectionsString((IntPtr)fixedStr.GetUnsafePtr(), fixedStr.Length); // Pass a pointer to a NativeArray that contains a string of UTF-8 characters. // Managed strings are UTF-16, so convert the string to UTF-8 bytes first. byte[] utf8Bytes = Encoding.UTF8.GetBytes("雨降って地固まる"); var nativeArray = new NativeArray<byte>(utf8Bytes, Allocator.Temp); void* unsafePtr = nativeArray.GetUnsafePtr(); SendCollectionsString((IntPtr)unsafePtr, nativeArray.Length); nativeArray.Dispose(); } }}
注
If you use a precompiled, dynamically linked library, change the
DllImport
value to the library name. Use the special string
__Internal
for source code plug-ins and statically linked libraries. Refer to DllImport attribute for more information.

Additional resources