Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Маршаллирование — это процесс преобразования типов, когда необходимо, чтобы они взаимодействовали между управляемым и нативным кодом.
Маршаллирование необходимо, так как типы в управляемом и неуправляемом коде отличаются. Например, в управляемом коде имеется string
, тогда как неуправляемые строки могут иметь кодировку .NET string
(UTF-16), кодировку ANSI Code Page, UTF-8, null-terminated, ASCII и т. д. По умолчанию подсистема P/Invoke старается правильно поступать, основываясь на поведении по умолчанию, более подробно описанном в этой статье. Однако для таких ситуаций, когда вам нужен дополнительный контроль, можно использовать атрибут MarshalAs , чтобы указать ожидаемый тип на неуправляемой стороне. Например, если вы хотите, чтобы строка была отправлена в виде строки, завершаемой значением NULL, UTF-8, это можно сделать следующим образом:
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPStr)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
Если вы примените атрибут System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
к сборке, правила из следующего раздела не применяются. Сведения о том, как значения .NET предоставляются нативному коду при применении этого атрибута, см. в разделе disabled runtime marshalling.
Правила по умолчанию для маршалинга распространенных типов
Как правило, среда выполнения пытается выполнить "правильное дело" при маршаллинге, чтобы требовать наименьшее количество работы от вас. В следующих таблицах описывается, как каждый тип управляется по умолчанию при использовании в параметрах или полях. Целое число и типы символов фиксированной ширины C99/C++11 используются для обеспечения правильности следующей таблицы для всех платформ. Вы можете использовать любой собственный тип, который имеет такие же требования к выравниванию и размеру, как и у этих типов.
В первой таблице описываются сопоставления для различных типов, для которых маршализация одинакова и для P/Invoke, и для маршализации полей.
Ключевое слово C# | Тип .NET | Тип по умолчанию |
---|---|---|
byte |
System.Byte |
uint8_t |
sbyte |
System.SByte |
int8_t |
short |
System.Int16 |
int16_t |
ushort |
System.UInt16 |
uint16_t |
int |
System.Int32 |
int32_t |
uint |
System.UInt32 |
uint32_t |
long |
System.Int64 |
int64_t |
ulong |
System.UInt64 |
uint64_t |
char |
System.Char |
Либо char , либо char16_t , в зависимости от кодировки P/Invoke или структуры. См. документацию по кодировке. |
System.Char |
Либо char* , либо char16_t* , в зависимости от кодировки P/Invoke или структуры. См. документацию по кодировке. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
Типы указателей .NET (например) void* |
void* |
|
Тип, производный от System.Runtime.InteropServices.SafeHandle |
void* |
|
Тип, производный от System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Тип Win32 BOOL |
decimal |
System.Decimal |
СТРУКТУРА COM DECIMAL |
Делегат .NET | Указатель на нативную функцию | |
System.DateTime |
Тип Win32 DATE |
|
System.Guid |
Тип Win32 GUID |
Некоторые категории маршаллинга имеют разные значения по умолчанию, если вы маршалируете в качестве параметра или структуры.
Тип .NET | Собственный тип (параметр) | Собственный тип (поле) |
---|---|---|
Массив .NET | Указатель на начало массива нативных представлений элементов массива. | Запрещено без атрибута [MarshalAs] |
Класс с LayoutKind , Sequential или Explicit |
Указатель на нативное представление класса | Нативное представление класса |
В следующей таблице приведены правила маршаллинга по умолчанию, которые применимы только для Windows. На платформах, отличных от Windows, вы не можете маршалить эти типы.
Тип .NET | Собственный тип (параметр) | Собственный тип (поле) |
---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM-интерфейс | Запрещено без атрибута [MarshalAs] |
System.ArgIterator |
va_list |
Не разрешенный |
System.Collections.IEnumerator |
IEnumVARIANT* |
Не разрешенный |
System.Collections.IEnumerable |
IDispatch* |
Не разрешенный |
System.DateTimeOffset |
int64_t число галок с полуночи 1 января 1601 г. |
int64_t число галок с полуночи 1 января 1601 г. |
Некоторые типы можно маршалировать только как параметры, а не как поля. Эти типы перечислены в следующей таблице:
Тип .NET | Собственный тип (только параметр) |
---|---|
System.Text.StringBuilder |
Либо char* или char16_t* в зависимости от CharSet P/Invoke. См. документацию по кодировке. |
System.ArgIterator |
va_list (только в Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Если эти значения по умолчанию не совсем соответствуют вашим ожиданиям, можно настроить способ маршалловки параметров. В статье по маршалингу параметров описано, как настроить способ маршаллирования различных типов параметров.
Маршалирование по умолчанию в сценариях COM
При вызове методов в COM-объектах в .NET среда выполнения .NET изменяет правила маршаллинга по умолчанию в соответствии с общей семантикой COM. В следующей таблице перечислены правила, которые среда выполнения .NET использует в сценариях COM:
Тип .NET | Собственный тип (вызовы метода COM) |
---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
Типы делегатов |
_Delegate* в .NET Framework. Запрещено в .NET Core и .NET 5+. |
System.Drawing.Color |
OLECOLOR |
Массив .NET | SAFEARRAY |
System.String[] |
SAFEARRAY из BSTR |
Маршаллинг классов и структур
Еще одним аспектом маршаллинга типа является передача структуры в неуправляемый метод. Например, для некоторых неуправляемых методов требуется структура в качестве параметра. В таких случаях необходимо создать соответствующую структуру или класс в управляемой части мира, чтобы использовать его в качестве параметра. Однако просто определения класса недостаточно, вам также необходимо указать маршаллеру, как именно сопоставить поля в классе с неуправляемой структурой.
StructLayout
Здесь атрибут становится полезным.
[LibraryImport("kernel32.dll")]
static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Millisecond;
}
public static void Main(string[] args)
{
SystemTime st = new SystemTime();
GetSystemTime(st);
Console.WriteLine(st.Year);
}
В предыдущем коде показан простой пример вызова функции GetSystemTime()
. Интересный бит находится в строке 4. Атрибут указывает, что поля класса должны быть сопоставлены последовательно с структурой на другой (неуправляемой) стороне. Это означает, что именование полей не важно, важно только их порядок, так как он должен соответствовать неуправляемой структуре, показанной в следующем примере:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
Иногда маршалирование по умолчанию для вашей структуры не соответствует вашим требованиям. Статья Настройка маршаллинга структур научит вас настраивать процесс маршаллирования вашей структуры.