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.