Condividi tramite


Conversioni implicite delle firme dei metodi nell'interoperabilità .NET

Per rimanere indipendenti dal linguaggio di programmazione, il sistema COM di Windows e molte API di Windows restituiscono un tipo Integer a 4 byte denominato HRESULT per indicare se un'API ha avuto esito positivo o negativo, insieme ad alcune informazioni sull'errore. Gli altri valori che devono essere passati al chiamante vengono "restituiti" tramite parametri puntatore che fungono da parametri "out" e sono in genere l'ultimo parametro della firma. I linguaggi come C# e Visual Basic convertono tradizionalmente un codice di errore in un'eccezione in modo che corrispondano al modo in cui gli errori vengono in genere propagati nel linguaggio e prevedono che le firme del metodo di interoperabilità non includano HRESULT. Per convertire la firma del metodo in una firma nativa, il runtime sposta il valore restituito del metodo in un parametro "out" aggiuntivo con un altro livello di riferimento indiretto (in altre parole, lo rende un puntatore al tipo restituito della firma gestita) e presuppone un valore restituito HRESULT. Se il metodo gestito restituisce void, non vengono aggiunti altri parametri e il valore restituito diventa HRESULT. Ad esempio, vedere i due metodi COM C# seguenti che vengono convertiti nella stessa firma nativa:

int Add(int a, int b);

void Add(int a, int b, out int sum);
HRESULT Add(int a, int b, /* out */ int* sum);

PreserveSig in COM

Per impostazione predefinita, tutti i metodi COM in C# usano la firma convertita. Per utilizzare ed esportare metodi senza la conversione della firma e la gestione dei valori HRESULT, aggiungere PreserveSigAttribute a un metodo dell'interfaccia COM. Quando l'attributo viene applicato a un metodo, non viene eseguita alcuna conversione della firma e non vengono generate eccezioni per i valori HRESULT con errore. Questo vale sia per il COM integrato che per il COM generato da codice sorgente. Ad esempio, vedere la firma del metodo C# seguente con un attributo PreserveSig e la relativa firma nativa corrispondente.

[PreserveSig]
int Add(int a, int b, out int sum);
HRESULT Add(int a, int b, int* sum);

Ciò può essere utile se il metodo potrebbe restituire valori HRESULT diversi che non sono errori, ma devono essere gestiti in modo diverso. Ad esempio, alcuni metodi potrebbero restituire il valore S_FALSE quando un metodo non ha esito negativo, ma restituisce solo risultati parziali e S_OK quando restituisce tutti i risultati.

PreserveSig con P/Invoke

L'attributo DllImportAttribute ha anche il campo bool PreserveSig che funziona in modo analogo a PreserveSigAttribute, ma con valore predefinito true. Per indicare che il runtime deve convertire la firma gestita e gestire l'oggetto HRESULT restituito, impostare il campo PreserveSig su false in DllImportAttribute. Ad esempio, vedere le firme seguenti di due P/Invoke nello stesso metodo nativo, uno con PreserveSig impostato su false e l'altro con il valore predefinito true.

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true, PreserveSig = false)]
public static extern void SHAutoComplete(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true)]
public static extern int SHAutoCompleteHRESULT(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

Nota

I P/Invoke generati da codice sorgente, che usano LibraryImportAttribute, non hanno un campo PreserveSig. Il codice generato presuppone sempre che la firma nativa e quella gestita siano identiche. Per altre informazioni, vedere P/Invoke generati da codice sorgente.

Gestire manualmente i valori HRESULT

Quando si chiama un metodo PreserveSig che restituisce un oggetto HRESULT, è possibile usare il metodo ThrowExceptionForHR per generare l'eccezione corrispondente se HRESULT indica un errore. Analogamente, quando si implementa un metodo PreserveSig, è possibile utilizzare il metodo GetHRForException per restituire l'oggetto HRESULT che indica un valore corrispondente per l'eccezione.

Effettuare il marshalling di HRESULT come struct

Quando si usa un metodo PreserveSig, è previsto che int sia il tipo gestito per HRESULT. Tuttavia, l'uso di uno struct personalizzato a 4 byte come tipo restituito consente di definire metodi e proprietà helper che possono semplificare l'utilizzo di HRESULT. Nel marshalling integrato, questo funziona automaticamente. Per usare uno struct al posto di int come rappresentazione gestita di HRESULT nel marshalling generato da codice sorgente, aggiungere l'attributo MarshalAsAttribute con Error come argomento. La presenza di questo attributo reinterpreta i bit di HRESULT come struct.

Vedi anche