Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il marshalling è il processo di trasformazione di tipi quando devono transitare tra codice gestito e codice nativo.
Il marshalling è necessario perché i tipi nel codice gestito e non gestito sono diversi. Nel codice gestito, ad esempio, si dispone di un string
, mentre le stringhe non gestite possono essere codifica .NET string
(UTF-16), codifica della tabella codici ANSI, UTF-8, terminazione null, ASCII e così via. Per impostazione predefinita, il sottosistema P/Invoke tenta di eseguire l'operazione corretta in base al comportamento predefinito, descritto in questo articolo. Tuttavia, per queste situazioni in cui è necessario un controllo aggiuntivo, è possibile usare l'attributo MarshalAs per specificare il tipo previsto sul lato non gestito. Ad esempio, se si vuole che la stringa venga inviata come stringa UTF-8 con terminazione Null, è possibile eseguire questa operazione come segue:
[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);
Applicando l'attributo System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
all'assembly, le regole nella sezione seguente non si applicano. Per informazioni su come i valori .NET vengono esposti al codice nativo quando viene applicato questo attributo, vedere Marshalling di runtime disabilitato.
Regole predefinite per il marshalling di tipi comuni
In genere, il runtime tenta di eseguire la "cosa giusta" quando si esegue il marshalling per richiedere la quantità minima di lavoro da parte dell'utente. Le tabelle seguenti descrivono come ogni tipo viene gestito per impostazione predefinita quando viene utilizzato come parametro o campo. I tipi di caratteri e integer a larghezza fissa C99/C++11 vengono usati per garantire che la tabella seguente sia corretta per tutte le piattaforme. È possibile usare qualsiasi tipo nativo con gli stessi requisiti di allineamento e dimensioni di questi tipi.
Questa prima tabella descrive i mapping per i vari tipi per i quali il marshalling è lo stesso sia per P/Invoke che per il marshalling dei campi.
Parola chiave C# | Tipo di .NET | Tipo nativo |
---|---|---|
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 oppure char16_t a seconda della codifica del P/Invoke o della struttura. Vedere la documentazione del set di caratteri. |
System.Char |
char* oppure char16_t* a seconda della codifica del P/Invoke o della struttura. Vedere la documentazione del set di caratteri. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
Tipi di puntatore .NET (ad esempio void* ) |
void* |
|
Tipo derivato da System.Runtime.InteropServices.SafeHandle |
void* |
|
Tipo derivato da System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Tipo Win32 BOOL |
decimal |
System.Decimal |
Struct COM DECIMAL |
Delegato .NET | Puntatore a funzione nativa | |
System.DateTime |
Tipo Win32 DATE |
|
System.Guid |
Tipo Win32 GUID |
Alcune categorie di marshalling hanno impostazioni predefinite diverse se si esegue il marshalling come parametro o come struttura.
Tipo di .NET | Tipo nativo (parametro) | Tipo nativo (campo) |
---|---|---|
Matrice .NET | Un puntatore all'inizio di un array che contiene le rappresentazioni native degli elementi. | Non consentito senza un [MarshalAs] attributo |
Classe con un LayoutKind di Sequential o Explicit |
Puntatore alla rappresentazione nativa della classe | Rappresentazione nativa della classe |
La tabella seguente include le regole di marshalling predefinite valide solo per Windows. Nelle piattaforme non Windows non è possibile effettuare il marshalling di questi tipi.
Tipo di .NET | Tipo nativo (parametro) | Tipo nativo (campo) |
---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Interfaccia COM | Non consentito senza un [MarshalAs] attributo |
System.ArgIterator |
va_list |
Non consentito |
System.Collections.IEnumerator |
IEnumVARIANT* |
Non consentito |
System.Collections.IEnumerable |
IDispatch* |
Non consentito |
System.DateTimeOffset |
int64_t che rappresenta il numero di tick dalla mezzanotte del 1° gennaio 1601 |
int64_t che rappresenta il numero di tick dalla mezzanotte del 1° gennaio 1601 |
È possibile eseguire il marshalling di alcuni tipi solo come parametri e non come campi. Questi tipi sono elencati nella tabella seguente:
Tipo di .NET | Tipo nativo (solo parametro) |
---|---|
System.Text.StringBuilder |
O char* o char16_t* a seconda di CharSet del P/Invoke. Vedere la documentazione del set di caratteri. |
System.ArgIterator |
va_list (solo in Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Se queste impostazioni predefinite non eseguono esattamente le operazioni desiderate, è possibile personalizzare la modalità di marshalling dei parametri. L'articolo sul marshalling dei parametri illustra come personalizzare il marshalling dei diversi tipi di parametri.
Marshalling predefinito negli scenari COM
Quando si chiamano metodi su oggetti COM in .NET, il runtime .NET modifica le regole di marshalling predefinite in modo che corrispondano alla semantica COM comune. La tabella seguente elenca le regole usate dai runtime .NET negli scenari COM:
Tipo di .NET | Tipo nativo (chiamate al metodo COM) |
---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
Tipi delegati |
_Delegate* in .NET Framework. Non consentito in .NET Core e .NET 5+. |
System.Drawing.Color |
OLECOLOR |
Matrice .NET | SAFEARRAY |
System.String[] |
SAFEARRAY di BSTR |
Marshalling di classi e strutture
Un altro aspetto del marshalling dei tipi è come passare una struttura a un metodo non gestito. Ad esempio, alcuni dei metodi non gestiti richiedono uno struct come parametro. In questi casi, è necessario creare uno struct o una classe corrispondente in una parte gestita del mondo per usarlo come parametro. Tuttavia, la semplice definizione della classe non è sufficiente, è anche necessario indicare al marshaller come eseguire il mapping dei campi nella classe allo struct non gestito. Qui l'attributo StructLayout
diventa utile.
[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);
}
Il codice precedente mostra un semplice esempio di chiamata in GetSystemTime()
funzione. La parte interessante è alla riga 4. L'attributo specifica che i campi della classe devono essere mappati in sequenza allo struct sull'altro lato (non gestito). Ciò significa che la denominazione dei campi non è importante, ma solo il relativo ordine è importante, perché deve corrispondere allo struct non gestito, illustrato nell'esempio seguente:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
In alcuni casi, il marshalling predefinito per la tua struttura non soddisfa le tue esigenze. L'articolo Personalizzazione del marshalling della struttura illustra come personalizzare come viene eseguito il marshalling della struttura.