Поведение маршалинга по умолчаниюDefault Marshaling Behavior

Маршалинг взаимодействия работает по правилам, которые определяют поведение данных, связанных с параметрами метода, при их передаче между управляемой и неуправляемой памятью.Interop marshaling operates on rules that dictate how data associated with method parameters behaves as it passes between managed and unmanaged memory. Эти встроенные правила определяют такие операции маршалинга, как преобразования типов данных, возможность изменения вызываемым объектом переданных ему данных и возврата этих изменений вызывающему объекту, а также обстоятельства, при которых упаковщик обеспечивает оптимизацию производительности.These built-in rules control such marshaling activities as data type transformations, whether a callee can change data passed to it and return those changes to the caller, and under which circumstances the marshaler provides performance optimizations.

В этом разделе описаны стандартные характеристики службы маршалинга взаимодействия.This section identifies the default behavioral characteristics of the interop marshaling service. Представлены подробные сведения о маршалинге массивов, логических типов, символьных типов, делегатов, классов, объектов, строк и структур.It presents detailed information on marshaling arrays, Boolean types, char types, delegates, classes, objects, strings, and structures.

Примечание

Маршалинг универсальных типов не поддерживается.Marshaling of generic types is not supported. Дополнительные сведения см. в разделе Взаимодействие с помощью универсальных типов.For more information see, Interoperating Using Generic Types.

Управление памятью с помощью упаковщика взаимодействияMemory management with the interop marshaler

Упаковщик взаимодействия всегда пытается освободить память, распределенную неуправляемым кодом.The interop marshaler always attempts to free memory allocated by unmanaged code. Такое поведение согласуется с правилами управления памятью COM, но отличается от правил, присущих C++.This behavior complies with COM memory management rules, but differs from the rules that govern native C++.

Путаница может возникать, если ожидается поведение в стиле C++ (память не освобождается) при вызове неуправляемого кода, при котором память автоматически освобождается для указателей.Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers. Например, вызов приведенного ниже неуправляемого метода из библиотеки DLL C++ не освобождает память автоматически.For example, calling the following unmanaged method from a C++ DLL does not automatically free any memory.

Неуправляемая сигнатураUnmanaged signature

BSTR MethodOne (BSTR b) {  
     return b;  
}  

Однако если при определении метода в качестве прототипа вызова неуправляемого кода заменить каждый тип BSTR на тип String и вызвать MethodOne, среда CLR дважды попытается освободить b.However, if you define the method as a platform invoke prototype, replace each BSTR type with a String type, and call MethodOne, the common language runtime attempts to free b twice. Поведение маршалинга можно изменить с помощью типов IntPtr, а не типов String.You can change the marshaling behavior by using IntPtr types rather than String types.

Среда выполнения всегда использует метод CoTaskMemFree для освобождения памяти.The runtime always uses the CoTaskMemFree method to free memory. Если память, с которой работает программа, не была выделена с помощью метода CoTaskMemAlloc, то необходимо использовать IntPtr и освободить память вручную с применением подходящего метода.If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method. Аналогичным образом можно избежать автоматического освобождения памяти в ситуациях, когда память ни при каких обстоятельствах не должна освобождаться, например при использовании функции GetCommandLine из Kernel32.dll, которая возвращает указатель в память ядра.Similarly, you can avoid automatic memory freeing in situations where memory should never be freed, such as when using the GetCommandLine function from Kernel32.dll, which returns a pointer to kernel memory. Подробнее об освобождении памяти вручную см. в разделе Пример буферов.For details on manually freeing memory, see the Buffers Sample.

Маршалинг по умолчанию для классовDefault marshaling for classes

Маршалинг классов может выполняться только с помощью COM-взаимодействия и только в качестве интерфейсов.Classes can be marshaled only by COM interop and are always marshaled as interfaces. Иногда интерфейс, используемый для маршалинга класса, называют интерфейсом класса.In some cases the interface used to marshal the class is known as the class interface. Подробнее о переопределении интерфейса класса другим выбранным интерфейсом см. в статье Introducing the class interface (Введение в интерфейс класса).For information about overriding the class interface with an interface of your choice, see Introducing the class interface.

Передача классов в COMPassing Classes to COM

При передаче управляемого класса в COM упаковщик взаимодействия автоматически инкапсулирует этот класс, используя COM-прокси, и передает интерфейс класса, полученный с помощью прокси, в вызов метода COM.When a managed class is passed to COM, the interop marshaler automatically wraps the class with a COM proxy and passes the class interface produced by the proxy to the COM method call. Затем прокси делегирует все вызовы для интерфейса класса назад в управляемый объект.The proxy then delegates all calls on the class interface back to the managed object. Прокси также предоставляет другие интерфейсы, которые не реализованы классом явным образом.The proxy also exposes other interfaces that are not explicitly implemented by the class. Прокси от имени класса автоматически реализует такие интерфейсы, как IUnknown и IDispatch.The proxy automatically implements interfaces such as IUnknown and IDispatch on behalf of the class.

Передача классов в код .NETPassing Classes to .NET Code

В COM коклассы обычно не используются в качестве аргументов метода.Coclasses are not typically used as method arguments in COM. Вместо кокласса обычно передается интерфейс по умолчанию.Instead, a default interface is usually passed in place of the coclass.

При передаче интерфейса в управляемый код упаковщик взаимодействия отвечает за инкапсуляцию интерфейса в соответствующую оболочку и за передачу этой оболочки в управляемый метод.When an interface is passed into managed code, the interop marshaler is responsible for wrapping the interface with the proper wrapper and passing the wrapper to the managed method. Выбор используемой оболочки может вызывать затруднения.Determining which wrapper to use can be difficult. У каждого экземпляра COM-объекта есть одна уникальная оболочка вне зависимости от числа интерфейсов, реализуемых объектом.Every instance of a COM object has a single, unique wrapper, no matter how many interfaces the object implements. Например, COM-объект, реализующий пять различных интерфейсов, имеет только одну оболочку.For example, a single COM object that implements five distinct interfaces has only one wrapper. Одна и та же оболочка предоставляет все пять интерфейсов.The same wrapper exposes all five interfaces. Если создаются два экземпляра COM-объекта, то создаются и два экземпляра оболочки.If two instances of the COM object are created, then two instances of the wrapper are created.

Чтобы тип оболочки не менялся в течение всего времени ее существования, упаковщик взаимодействия должен выбрать правильную оболочку при первой передаче интерфейса, предоставляемого данным объектом, через упаковщик.For the wrapper to maintain the same type throughout its lifetime, the interop marshaler must identify the correct wrapper the first time an interface exposed by the object is passed through the marshaler. Упаковщик идентифицирует объект, просматривая один из интерфейсов, реализуемых объектом.The marshaler identifies the object by looking at one of the interfaces the object implements.

Например, упаковщик определяет, что оболочка класса должна использоваться для инкапсуляции интерфейса, переданного в управляемый код.For example, the marshaler determines that the class wrapper should be used to wrap the interface that was passed into managed code. При первой передаче этого интерфейса через упаковщик последний проверяет, поступил ли этот интерфейс от известного объекта.When the interface is first passed through the marshaler, the marshaler checks whether the interface is coming from a known object. Эта проверка выполняется в двух случаях:This check occurs in two situations:

  • Интерфейс реализуется другим управляемым объектом, переданным в COM где-то в другом месте.An interface is being implemented by another managed object that was passed to COM elsewhere. Упаковщик может легко идентифицировать интерфейсы, предоставляемые управляемыми объектами, и может находить соответствие между интерфейсом и управляемым объектом, предоставившим реализацию.The marshaler can readily identify interfaces exposed by managed objects and is able to match the interface with the managed object that provides the implementation. Затем управляемый объект передается в метод, и никакой оболочки не требуется.The managed object is then passed to the method and no wrapper is needed.

  • Интерфейс реализуется объектом, который уже инкапсулирован.An object that has already been wrapped is implementing the interface. Чтобы определить, так ли это, упаковщик запрашивает у объекта его интерфейс IUnknown и сравнивает возвращенный интерфейс с интерфейсами других, уже инкапсулированных объектов.To determine whether this is the case, the marshaler queries the object for its IUnknown interface and compares the returned interface to the interfaces of other objects that are already wrapped. Если интерфейс совпадает с интерфейсом другой оболочки, то объекты имеют одинаковые идентификаторы и существующая оболочка передается методу.If the interface is the same as that of another wrapper, the objects have the same identity and the existing wrapper is passed to the method.

Если интерфейс не является интерфейсом известного объекта, упаковщик выполняет указанные ниже действия.If an interface is not from a known object, the marshaler does the following:

  1. Упаковщик запрашивает у объекта интерфейс IProvideClassInfo2.The marshaler queries the object for the IProvideClassInfo2 interface. Если он предоставлен, то упаковщик использует значение CLSID, возвращенное из IProvideClassInfo2.GetGUID, для идентификации кокласса, предоставляющего интерфейс.If provided, the marshaler uses the CLSID returned from IProvideClassInfo2.GetGUID to identify the coclass providing the interface. Используя идентификатор CLSID, упаковщик может найти оболочку в реестре, если перед этим сборка была зарегистрирована.With the CLSID, the marshaler can locate the wrapper from the registry if the assembly has previously been registered.

  2. Упаковщик запрашивает у этого интерфейса интерфейс IProvideClassInfo.The marshaler queries the interface for the IProvideClassInfo interface. Если он предоставлен, упаковщик использует интерфейс ITypeInfo, возвращенный из метода IProvideClassInfo.GetClassinfo, для определения значения CLSID класса, предоставляющего интерфейс.If provided, the marshaler uses the ITypeInfo returned from IProvideClassInfo.GetClassinfo to determine the CLSID of the class exposing the interface. Упаковщик может использовать значение CLSID для нахождения метаданных оболочки.The marshaler can use the CLSID to locate the metadata for the wrapper.

  3. Если упаковщик все еще не может идентифицировать класс, то он инкапсулирует интерфейс в оболочку универсального класса с именем System.__ComObject.If the marshaler still cannot identify the class, it wraps the interface with a generic wrapper class called System.__ComObject.

Маршалинг по умолчанию для делегатовDefault marshaling for delegates

Управляемый делегат маршалируется как COM-интерфейс или как указатель на функцию на основе описанного ниже механизма вызовов.A managed delegate is marshaled as a COM interface or as a function pointer, based on the calling mechanism:

  • Для вызова неуправляемого кода делегат по умолчанию маршалируется как указатель на функцию.For platform invoke, a delegate is marshaled as an unmanaged function pointer by default.

  • Для COM-взаимодействия делегат по умолчанию маршалируется как COM-интерфейс типа _Delegate.For COM interop, a delegate is marshaled as a COM interface of type _Delegate by default. Интерфейс _Delegate определяется в библиотеке типов Mscorlib.tlb и содержит метод Delegate.DynamicInvoke, который позволяет вызывать метод, на который ссылается делегат.The _Delegate interface is defined in the Mscorlib.tlb type library and contains the Delegate.DynamicInvoke method, which enables you to call the method that the delegate references.

В таблице ниже показаны варианты маршалинга для типа данных управляемого делегата.The following table shows the marshaling options for the managed delegate data type. Атрибут MarshalAsAttribute предоставляет несколько значений перечисления UnmanagedType для маршалинга делегатов.The MarshalAsAttribute attribute provides several UnmanagedType enumeration values to marshal delegates.

Тип перечисленияEnumeration type Описание неуправляемого форматаDescription of unmanaged format
UnmanagedType.FunctionPtrUnmanagedType.FunctionPtr Указатель на неуправляемую функцию.An unmanaged function pointer.
UnmanagedType.InterfaceUnmanagedType.Interface Интерфейс типа _Delegate, как определено в Mscorlib.tlb.An interface of type _Delegate, as defined in Mscorlib.tlb.

Рассмотрим пример кода, в котором методы DelegateTestInterface экспортируются в библиотеку типов COM.Consider the following example code in which the methods of DelegateTestInterface are exported to a COM type library. Обратите внимание на то, что только делегаты, помеченные ключевым словом ref (или ByRef), передаются как параметры In/Out.Notice that only delegates marked with the ref (or ByRef) keyword are passed as In/Out parameters.

using System;  
using System.Runtime.InteropServices;  
  
public interface DelegateTest {  
void m1(Delegate d);  
void m2([MarshalAs(UnmanagedType.Interface)] Delegate d);     
void m3([MarshalAs(UnmanagedType.Interface)] ref Delegate d);    
void m4([MarshalAs(UnmanagedType.FunctionPtr)] Delegate d);   
void m5([MarshalAs(UnmanagedType.FunctionPtr)] ref Delegate d);     
}  

Представление библиотеки типовType library representation

importlib("mscorlib.tlb");  
interface DelegateTest : IDispatch {  
[id(…)] HRESULT m1([in] _Delegate* d);  
[id(…)] HRESULT m2([in] _Delegate* d);  
[id(…)] HRESULT m3([in, out] _Delegate** d);  
[id()] HRESULT m4([in] int d);  
[id()] HRESULT m5([in, out] int *d);  
   };  

Указатель на функцию может быть разыменован, так же как и любой другой указатель на неуправляемую функцию.A function pointer can be dereferenced, just as any other unmanaged function pointer can be dereferenced.

В этом примере при маршалировании двух делегатов в качестве UnmanagedType.FunctionPtr будет получен результат int и указатель на int.In this example, when the two delegates are marshaled as UnmanagedType.FunctionPtr, the result is an int and a pointer to an int. Поскольку типы делегатов маршалируются, int здесь представляет указатель на void (void*), то есть адрес делегата в памяти.Because delegate types are being marshaled, int here represents a pointer to a void (void*), which is the address of the delegate in memory. Другими словами, этот результат относится к 32-разрядным системам Windows, поскольку int здесь представляет размер указателя на функцию.In other words, this result is specific to 32-bit Windows systems, since int here represents the size of the function pointer.

Примечание

Ссылка на указатель на функцию в управляемом делегате, хранимая в неуправляемом коде, не препятствует выполнению средой CLR сборки мусора для управляемого объекта.A reference to the function pointer to a managed delegate held by unmanaged code does not prevent the common language runtime from performing garbage collection on the managed object.

Например, приведенный ниже код некорректен, так как ссылка на объект cb, передаваемая методу SetChangeHandler, не сохраняет объект cb в активном состоянии по окончании времени существования метода Test.For example, the following code is incorrect because the reference to the cb object, passed to the SetChangeHandler method, does not keep cb alive beyond the life of the Test method. После применения к объекту cb процедуры сборки мусора указатель на функцию, переданный в SetChangeHandler, становится недействительным.Once the cb object is garbage collected, the function pointer passed to SetChangeHandler is no longer valid.

public class ExternalAPI {  
   [DllImport("External.dll")]  
   public static extern void SetChangeHandler(  
      [MarshalAs(UnmanagedType.FunctionPtr)]ChangeDelegate d);  
}  
public delegate bool ChangeDelegate([MarshalAs(UnmanagedType.LPWStr) string S);  
public class CallBackClass {  
   public bool OnChange(string S){ return true;}  
}  
internal class DelegateTest {  
   public static void Test() {  
      CallBackClass cb = new CallBackClass();  
      // Caution: The following reference on the cb object does not keep the   
      // object from being garbage collected after the Main method   
      // executes.  
      ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));     
   }  
}  

Чтобы компенсировать неожиданную сборку мусора, вызывающий объект должен гарантировать, что объект cb будет находиться в активном состоянии до тех пор, пока используется указатель на неуправляемую функцию.To compensate for unexpected garbage collection, the caller must ensure that the cb object is kept alive as long as the unmanaged function pointer is in use. Как показано в примере ниже, при необходимости можно настроить передачу уведомлений от неуправляемого кода в управляемый о том, что указатель на функцию больше не нужен.Optionally, you can have the unmanaged code notify the managed code when the function pointer is no longer needed, as the following example shows.

internal class DelegateTest {  
   CallBackClass cb;  
   // Called before ever using the callback function.  
   public static void SetChangeHandler() {  
      cb = new CallBackClass();  
      ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));  
   }  
   // Called after using the callback function for the last time.  
   public static void RemoveChangeHandler() {  
      // The cb object can be collected now. The unmanaged code is   
      // finished with the callback function.  
      cb = null;  
   }  
}  

Маршалинг по умолчанию для типов значенийDefault marshaling for value types

Большинство типов значений, такие как целые числа и числа с плавающей запятой, являются непреобразуемыми и не требуют маршалинга.Most value types, such as integers and floating-point numbers, are blittable and do not require marshaling. Преобразуемые типы представлены в управляемой и неуправляемой памяти по-разному и требуют маршалинга.Other non-blittable types have dissimilar representations in managed and unmanaged memory and do require marshaling. Другие типы требуют явного форматирования при пересечении границы взаимодействия.Still other types require explicit formatting across the interoperation boundary.

В этом разделе представлены сведения о следующих форматированных типах значений:This section provides information on the following formatted value types:

Кроме описания форматированных типов, в этом разделе определяются системные типы значений, имеющие нестандартное поведение маршалинга.In addition to describing formatted types, this topic identifies System Value Types that have unusual marshaling behavior.

Форматированный тип — это сложный тип, который содержит информацию, явным образом определяющую размещение его членов в памяти.A formatted type is a complex type that contains information that explicitly controls the layout of its members in memory. Сведения о размещении членов предоставляются с помощью атрибута StructLayoutAttribute.The member layout information is provided using the StructLayoutAttribute attribute. Размещение может принимать одно из указанных ниже значений перечисления LayoutKind.The layout can be one of the following LayoutKind enumeration values:

  • LayoutKind.AutomaticLayoutKind.Automatic

    Указывает, что среда CLR для повышения эффективности может свободно изменять порядок членов типа.Indicates that the common language runtime is free to reorder the members of the type for efficiency. Тем не менее, когда тип значения передается в неуправляемый код, размещение членов является предсказуемым.However, when a value type is passed to unmanaged code, the layout of the members is predictable. При попытке маршалинга такой структуры автоматически вызывается исключение.An attempt to marshal such a structure automatically causes an exception.

  • LayoutKind.SequentialLayoutKind.Sequential

    Указывает, что члены типа должны размещаться в неуправляемой памяти в том же порядке, в котором они появляются в определении управляемого типа.Indicates that the members of the type are to be laid out in unmanaged memory in the same order in which they appear in the managed type definition.

  • LayoutKind.ExplicitLayoutKind.Explicit

    Указывает, что члены располагаются в соответствии с атрибутом FieldOffsetAttribute, предоставляемым с каждым полем.Indicates that the members are laid out according to the FieldOffsetAttribute supplied with each field.

Типы значений, используемые в вызове неуправляемого кодаValue Types Used in Platform Invoke

В примере ниже типы Point и Rect предоставляют сведения о размещении членов с помощью атрибута StructLayoutAttribute.In the following example the Point and Rect types provide member layout information using the StructLayoutAttribute.

Imports System.Runtime.InteropServices  
<StructLayout(LayoutKind.Sequential)> Public Structure Point  
   Public x As Integer  
   Public y As Integer  
End Structure  
<StructLayout(LayoutKind.Explicit)> Public Structure Rect  
   <FieldOffset(0)> Public left As Integer  
   <FieldOffset(4)> Public top As Integer  
   <FieldOffset(8)> Public right As Integer  
   <FieldOffset(12)> Public bottom As Integer  
End Structure  
using System.Runtime.InteropServices;  
[StructLayout(LayoutKind.Sequential)]  
public struct Point {  
   public int x;  
   public int y;  
}     
  
[StructLayout(LayoutKind.Explicit)]  
public struct Rect {  
   [FieldOffset(0)] public int left;  
   [FieldOffset(4)] public int top;  
   [FieldOffset(8)] public int right;  
   [FieldOffset(12)] public int bottom;  
}  

При маршалинге в неуправляемый код эти форматированные типы маршалируются как структуры стиля C.When marshaled to unmanaged code, these formatted types are marshaled as C-style structures. Это обеспечивает простой способ вызова неуправляемого интерфейса API с аргументами в виде структур.This provides an easy way of calling an unmanaged API that has structure arguments. Например, структуры POINT и RECT могут передаваться в функцию PtInRect Microsoft Windows API следующим образом:For example, the POINT and RECT structures can be passed to the Microsoft Windows API PtInRect function as follows:

BOOL PtInRect(const RECT *lprc, POINT pt);  

Вы можете передать структуры с помощью следующего определения вызова неуправляемого кода:You can pass structures using the following platform invoke definition:

Friend Class NativeMethods
    Friend Declare Auto Function PtInRect Lib "User32.dll" (
        ByRef r As Rect, p As Point) As Boolean
End Class
internal static class NativeMethods
{
   [DllImport("User32.dll")]
   internal static extern bool PtInRect(ref Rect r, Point p);
}

Тип значения Rect должен передаваться по ссылке, потому что неуправляемый интерфейс API ожидает, что в функцию будет передан указатель на RECT.The Rect value type must be passed by reference because the unmanaged API is expecting a pointer to a RECT to be passed to the function. Тип значения Point передается по значению, так как неуправляемый интерфейс API ожидает, что POINT будет передан в стек.The Point value type is passed by value because the unmanaged API expects the POINT to be passed on the stack. Это небольшое различие очень важно.This subtle difference is very important. Ссылки передаются в неуправляемый код как указатели.References are passed to unmanaged code as pointers. Значения передаются в неуправляемый код в стеке.Values are passed to unmanaged code on the stack.

Примечание

При маршалинге форматированного типа в виде структуры доступны только поля внутри этого типа.When a formatted type is marshaled as a structure, only the fields within the type are accessible. Если у типа есть методы, свойства или события, то они недоступны из неуправляемого кода.If the type has methods, properties, or events, they are inaccessible from unmanaged code.

Классы тоже можно маршалировать в неуправляемый код как структуры стиля языка С при условии, что они имеют фиксированное размещение членов.Classes can also be marshaled to unmanaged code as C-style structures, provided they have fixed member layout. Сведения о размещении членов класса также предоставляются с помощью атрибута StructLayoutAttribute.The member layout information for a class is also provided with the StructLayoutAttribute attribute. Основное различие между типами значений с фиксированным размещением и классами с фиксированным размещением заключает в способе маршалинга в неуправляемый код.The main difference between value types with fixed layout and classes with fixed layout is the way in which they are marshaled to unmanaged code. Типы значений передаются по значению (в стеке), поэтому любые изменения, внесенные в члены этого типа вызываемым объектом, не видны вызывающему объекту.Value types are passed by value (on the stack) and consequently any changes made to the members of the type by the callee are not seen by the caller. Ссылочные типы передаются по ссылке (ссылка на тип передается в стеке). Поэтому все изменения, внесенные в члены непреобразуемого типа вызываемым объектом, видны вызывающему объекту.Reference types are passed by reference (a reference to the type is passed on the stack); consequently, all changes made to blittable-type members of a type by the callee are seen by the caller.

Примечание

Если ссылочный тип содержит члены преобразуемого типа, преобразование должно выполняться дважды: первый раз — при передаче аргумента неуправляемой стороне, а второй раз — при возврате из вызова.If a reference type has members of non-blittable types, conversion is required twice: the first time when an argument is passed to the unmanaged side and the second time on return from the call. В связи с появлением дополнительных издержек параметры In/Out необходимо применять к аргументу в явном виде, если вызывающий объект должен учитывать изменения, внесенные вызываемым объектом.Due to this added overhead, In/Out parameters must be explicitly applied to an argument if the caller wants to see changes made by the callee.

В примере ниже класс SystemTime имеет последовательное размещение членов и может быть передан в функцию GetSystemTime API Windows.In the following example, the SystemTime class has sequential member layout and can be passed to the Windows API GetSystemTime function.

<StructLayout(LayoutKind.Sequential)> Public Class SystemTime  
   Public wYear As System.UInt16  
   Public wMonth As System.UInt16  
   Public wDayOfWeek As System.UInt16  
   Public wDay As System.UInt16  
   Public wHour As System.UInt16  
   Public wMinute As System.UInt16  
   Public wSecond As System.UInt16  
   Public wMilliseconds As System.UInt16  
End Class  
[StructLayout(LayoutKind.Sequential)]  
   public class SystemTime {  
   public ushort wYear;   
   public ushort wMonth;  
   public ushort wDayOfWeek;   
   public ushort wDay;   
   public ushort wHour;   
   public ushort wMinute;   
   public ushort wSecond;   
   public ushort wMilliseconds;   
}  

Функция GetSystemTime определяется следующим образом:The GetSystemTime function is defined as follows:

void GetSystemTime(SYSTEMTIME* SystemTime);  

Эквивалентное определение вызова неуправляемого кода GetSystemTime выглядит следующим образом:The equivalent platform invoke definition for GetSystemTime is as follows:

Friend Class NativeMethods
    Friend Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (
        ByVal sysTime As SystemTime)
End Class
internal static class NativeMethods
{
   [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
   internal static extern void GetSystemTime(SystemTime st);
}

Обратите внимание на то, что аргумент SystemTime не типизирован как ссылочный аргумент, потому что SystemTime — это класс, а не тип значения.Notice that the SystemTime argument is not typed as a reference argument because SystemTime is a class, not a value type. В отличие от типов значений, классы всегда передаются по ссылке.Unlike value types, classes are always passed by reference.

В примере кода ниже показан другой класс Point, который содержит метод с именем SetXY.The following code example shows a different Point class that has a method called SetXY. Так как этот тип имеет последовательную компоновку, он может быть передан в неуправляемый код и маршалирован как структура.Because the type has sequential layout, it can be passed to unmanaged code and marshaled as a structure. Однако член SetXY нельзя вызвать из неуправляемого кода, даже когда объект передается по ссылке.However, the SetXY member is not callable from unmanaged code, even though the object is passed by reference.

<StructLayout(LayoutKind.Sequential)> Public Class Point  
   Private x, y As Integer  
   Public Sub SetXY(x As Integer, y As Integer)  
      Me.x = x  
      Me.y = y  
   End Sub  
End Class  
[StructLayout(LayoutKind.Sequential)]  
public class Point {  
   int x, y;  
   public void SetXY(int x, int y){   
      this.x = x;  
      this.y = y;  
   }  
}  

Типы значений, используемые во COM-взаимодействииValue Types Used in COM Interop

Форматированные типы могут также передаваться в вызовы метода COM-взаимодействия.Formatted types can also be passed to COM interop method calls. Фактически при экспорте в библиотеку типов типы значений автоматически преобразуются в структуры.In fact, when exported to a type library, value types are automatically converted to structures. Как показано в примере ниже, тип значения Point становится определением типа (typedef) с именем Point.As the following example shows, the Point value type becomes a type definition (typedef) with the name Point. Все ссылки на тип значения Point в любом другом месте библиотеки типов заменяются на определение типа typedef Point.All references to the Point value type elsewhere in the type library are replaced with the Point typedef.

Представление библиотеки типовType library representation

typedef struct tagPoint {  
   int x;  
   int y;  
} Point;  
interface _Graphics {  
   …  
   HRESULT SetPoint ([in] Point p)  
   HRESULT SetPointRef ([in,out] Point *p)  
   HRESULT GetPoint ([out,retval] Point *p)  
}  

Те же самые правила, которые используются для маршалинга значений и ссылок в вызовы неуправляемого кода, применяются и при маршалинге через COM-интерфейсы.The same rules used to marshal values and references to platform invoke calls are used when marshaling through COM interfaces. Например, когда экземпляр типа значения Point передается из .NET Framework в COM, Point передается по значению.For example, when an instance of the Point value type is passed from the .NET Framework to COM, the Point is passed by value. Если тип значения Point передается по ссылке, указатель на Point передается в стеке.If the Point value type is passed by reference, a pointer to a Point is passed on the stack. Упаковщик взаимодействия не поддерживает более высокие уровни косвенного обращения (Point **) в любом направлении.The interop marshaler does not support higher levels of indirection (Point **) in either direction.

Примечание

Структуры, для которых значение перечисления LayoutKind установлено равным Explicit, не могут использоваться в COM-взаимодействии, так как экспортированная библиотека типов не может представлять явное размещение.Structures having the LayoutKind enumeration value set to Explicit cannot be used in COM interop because the exported type library cannot express an explicit layout.

Системные типы значенийSystem Value Types

Пространство имен System содержит несколько типов значений, представляющих собой упакованную форму простых типов среды выполнения.The System namespace has several value types that represent the boxed form of the runtime primitive types. Например, структура типов значений System.Int32 представляет собой упакованную форму ELEMENT_TYPE_I4.For example, the value type System.Int32 structure represents the boxed form of ELEMENT_TYPE_I4. Вместо маршалинга этих типов в качестве структур, как в случае с другими форматированными типами, их следует маршалировать так же, как и соответствующие простые типы.Instead of marshaling these types as structures, as other formatted types are, you marshal them in the same way as the primitive types they box. Соответственно, System.Int32 маршалируется как ELEMENT_TYPE_I4, а не как структура, содержащая один член типа long.System.Int32 is therefore marshaled as ELEMENT_TYPE_I4 instead of as a structure containing a single member of type long. В таблице ниже приведен список типов значений в пространстве имен System, являющихся упакованными представлениями простых типов.The following table contains a list of the value types in the System namespace that are boxed representations of primitive types.

Системный тип значенияSystem value type Тип элементаElement type
System.Boolean ELEMENT_TYPE_BOOLEANELEMENT_TYPE_BOOLEAN
System.SByte ELEMENT_TYPE_I1ELEMENT_TYPE_I1
System.Byte ELEMENT_TYPE_UI1ELEMENT_TYPE_UI1
System.Char ELEMENT_TYPE_CHARELEMENT_TYPE_CHAR
System.Int16 ELEMENT_TYPE_I2ELEMENT_TYPE_I2
System.UInt16 ELEMENT_TYPE_U2ELEMENT_TYPE_U2
System.Int32 ELEMENT_TYPE_I4ELEMENT_TYPE_I4
System.UInt32 ELEMENT_TYPE_U4ELEMENT_TYPE_U4
System.Int64 ELEMENT_TYPE_I8ELEMENT_TYPE_I8
System.UInt64 ELEMENT_TYPE_U8ELEMENT_TYPE_U8
System.Single ELEMENT_TYPE_R4ELEMENT_TYPE_R4
System.Double ELEMENT_TYPE_R8ELEMENT_TYPE_R8
System.String ELEMENT_TYPE_STRINGELEMENT_TYPE_STRING
System.IntPtr ELEMENT_TYPE_IELEMENT_TYPE_I
System.UIntPtr ELEMENT_TYPE_UELEMENT_TYPE_U

Некоторые типы значений в пространстве имен System обрабатываются по-другому.Some other value types in the System namespace are handled differently. Так как неуправляемый код уже имеет хорошо определенные форматы для таких типов, у упаковщика есть специальные правила для их маршалинга.Because the unmanaged code already has well-established formats for these types, the marshaler has special rules for marshaling them. В таблице ниже представлен список этих специальных типов значений в пространстве имен System, а также неуправляемых типов, в которые они маршалируются.The following table lists the special value types in the System namespace, as well as the unmanaged type they are marshaled to.

Системный тип значенияSystem value type Тип IDLIDL type
System.DateTime DATEDATE
System.Decimal DECIMALDECIMAL
System.Guid GUIDGUID
System.Drawing.Color OLE_COLOROLE_COLOR

В приведенном ниже коде показано определение неуправляемых типов DATE, GUID, DECIMAL и OLE_COLOR в библиотеке типов Stdole2.The following code shows the definition of the unmanaged types DATE, GUID, DECIMAL, and OLE_COLOR in the Stdole2 type library.

Представление библиотеки типовType library representation

typedef double DATE;  
typedef DWORD OLE_COLOR;  
  
typedef struct tagDEC {  
    USHORT    wReserved;  
    BYTE      scale;  
    BYTE      sign;  
    ULONG     Hi32;  
    ULONGLONG Lo64;  
} DECIMAL;  
  
typedef struct tagGUID {  
    DWORD Data1;  
    WORD  Data2;  
    WORD  Data3;  
    BYTE  Data4[ 8 ];  
} GUID;  

В коде ниже показаны соответствующие определения в управляемом интерфейсе IValueTypes.The following code shows the corresponding definitions in the managed IValueTypes interface.

Public Interface IValueTypes  
   Sub M1(d As System.DateTime)  
   Sub M2(d As System.Guid)  
   Sub M3(d As System.Decimal)  
   Sub M4(d As System.Drawing.Color)  
End Interface  
public interface IValueTypes {  
   void M1(System.DateTime d);  
   void M2(System.Guid d);  
   void M3(System.Decimal d);  
   void M4(System.Drawing.Color d);  
}  

Представление библиотеки типовType library representation

[…]  
interface IValueTypes : IDispatch {  
   HRESULT M1([in] DATE d);  
   HRESULT M2([in] GUID d);  
   HRESULT M3([in] DECIMAL d);  
   HRESULT M4([in] OLE_COLOR d);  
};  

См. такжеSee also