Что такое маршалинг?csharp-121

Определение маршалинга

Маршалинг (Marshaling) — это процесс преобразования и передачи данных между различными контекстами выполнения, где эти данные могут иметь разные представления. В .NET это чаще всего относится к:

  • Взаимодействию между управляемым (managed) и неуправляемым (unmanaged) кодом
  • Межпроцессному взаимодействию (IPC)
  • Работе с различными доменами приложений (AppDomain)

Основные сценарии использования

1. P/Invoke

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(
    IntPtr hWnd,
    [MarshalAs(UnmanagedType.LPTStr)] string text,
    [MarshalAs(UnmanagedType.LPTStr)] string caption,
    uint type);

// Вызов неуправляемого кода с маршалингом строк
MessageBox(IntPtr.Zero, "Hello World", "Title", 0);

2. COM Interop

// Маршалинг COM объекта в .NET
[ComImport]
[Guid("000208D5-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IExcelApplication
{
    void Quit();
}

// Использование
Type excelType = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(excelType);
excel.Quit(); // Маршалинг вызова через RCW

3. Маршалинг между доменами приложений

public class MarshalByRefObjectExample : MarshalByRefObject
{
    public string GetDomainName() => AppDomain.CurrentDomain.FriendlyName;
}

// В другом домене
var domain = AppDomain.CreateDomain("NewDomain");
var obj = (MarshalByRefObjectExample)domain.CreateInstanceAndUnwrap(
    typeof(MarshalByRefObjectExample).Assembly.FullName,
    typeof(MarshalByRefObjectExample).FullName);
Console.WriteLine(obj.GetDomainName()); // "NewDomain"

Типы маршалинга

1. Blittable маршалинг

Для типов, имеющих одинаковое представление в управляемой и неуправляемой памяти (int, float, byte и т.д.). Не требует преобразования.

2. Не-blittable маршалинг

Для сложных типов, требующих преобразования:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Person
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string Name;
    public int Age;
}

3. Пользовательский маршалинг

public class CustomMarshaller : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string cookie) => new CustomMarshaller();

    public void CleanUpManagedData(object ManagedObj) { ... }
    public void CleanUpNativeData(IntPtr pNativeData) { ... }
    // ... другие методы интерфейса
}

Атрибуты маршалинга

Атрибут Назначение
[MarshalAs] Указывает как маршалить параметры
[StructLayout] Задает расположение полей структуры
[DllImport] Импорт неуправляемых функций
[In] и [Out] Направление передачи данных

Пример сложного маршалинга

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool GetDiskFreeSpaceEx(
    string directoryName,
    out ulong freeBytesAvailable,
    out ulong totalNumberOfBytes,
    out ulong totalNumberOfFreeBytes);

// Использование
ulong free, total, totalFree;
GetDiskFreeSpaceEx(@"C:\", out free, out total, out totalFree);

Производительность и проблемы

  1. Накладные расходы:

    • Маршалинг сложных структур может быть дорогим
    • Частые переходы между managed/unmanaged контекстами снижают производительность
  2. Проблемы памяти:

    • Утечки при неправильном освобождении неуправляемых ресурсов
    • Неправильная маркировка [In]/[Out] может вызвать лишнее копирование
  3. Советы по оптимизации:

    • Минимизировать вызовы через границы
    • Использовать blittable типы где возможно
    • Кэшировать дескрипторы (handles)

Резюмируем

  1. Маршалинг — ключевой механизм взаимодействия между managed/unmanaged кодом
  2. Основные сценарии: P/Invoke, COM Interop, междоменное взаимодействие
  3. Типы маршалинга: Blittable (без преобразования), Non-Blittable (с преобразованием), Custom
  4. Оптимизация: Сводить к минимуму, использовать правильные атрибуты, следить за памятью

Принцип: "Маршалинг — это мост между мирами, но каждый переход через мост имеет свою цену"