Traduções de assinatura de método implícito na interoperabilidade do .NET
Para manter a linguagem de programação independente, o sistema COM do Windows e muitas APIs do Windows retornam um tipo inteiro de 4 bytes chamado HRESULT
para indicar se uma API foi bem-sucedida ou falhou, juntamente com algumas informações sobre a falha. Outros valores que precisam ser passados para o chamador são "retornados" por meio de parâmetros de ponteiro que atuam como parâmetros "out" e normalmente são o último parâmetro na assinatura. Linguagens como C# e Visual Basic tradicionalmente convertem um código de falha em uma exceção para corresponder a como as falhas geralmente são propagadas no idioma, e esperam que as assinaturas do método de interoperabilidade não incluam o HRESULT
. Para converter a assinatura do método em uma assinatura nativa, o runtime move o valor retornado do método para um parâmetro "out" adicional com mais um nível de indireção (em outras palavras, torna-o um ponteiro para o tipo de retorno da assinatura gerenciada) e assume um valor retornado HRESULT
. Se o método gerenciado retornar void
, nenhum parâmetro adicional será adicionado e o valor retornado se tornará um HRESULT
. Por exemplo, confira os dois métodos COM C# a seguir que se traduzem para a mesma assinatura 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 em COM
Todos os métodos COM em C# devem usar a assinatura traduzida por padrão. Para usar e exportar métodos sem a tradução de assinatura e o tratamento de valores HRESULT
, adicione o PreserveSigAttribute a um método de interface COM. Quando o atributo é aplicado a um método, nenhuma tradução é feita para a assinatura e as exceções não são geradas para valores com falha HRESULT
. Isso se aplica ao COM interno e ao COM gerado pela origem. Por exemplo, confira a assinatura do método C# a seguir com um atributo PreserveSig
e sua assinatura nativa correspondente.
[PreserveSig]
int Add(int a, int b, out int sum);
HRESULT Add(int a, int b, int* sum);
Isso pode ser útil se o método puder retornar valores diferentes de HRESULT
que não são falhas, mas devem ser tratados de forma diferente. Por exemplo, alguns métodos podem retornar o valor S_FALSE
quando um método não falha, mas apenas retorna resultados parciais, e S_OK
quando ele retorna todos os resultados.
PreserveSig
com P/Invokes
O atributo DllImportAttribute também tem o campo bool PreserveSig
que funciona de forma semelhante ao PreserveSigAttribute
, mas o padrão é true
. Para indicar que o runtime deve traduzir a assinatura gerenciada e manipular o HRESULT
que é retornado, defina o campo PreserveSig
como false
no DllImportAttribute
. Por exemplo, confira as seguintes assinaturas de dois P/Invokes para o mesmo método nativo, uma com PreserveSig
definido como false
, e outra com ele como o valor padrão 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);
Observação
P/Invokes gerados pela origem, que usam o campo LibraryImportAttribute, não têm nenhum campo PreserveSig
. O código gerado sempre pressupõe que a assinatura nativa e a gerenciada sejam idênticas. Para obter mais informações, confira P/Invokes gerados pela origem.
Manipular valores HRESULT
manualmente
Ao chamar um método PreserveSig
que retorna um HRESULT
, você pode usar o método ThrowExceptionForHR para gerar a exceção correspondente se HRESULT
indicar uma falha. Da mesma forma, ao implementar um método PreserveSig
, você pode usar o método GetHRForException para retornar o HRESULT
que indica um valor correspondente para a exceção.
Realizar marshaling de HRESULTs como structs
Ao usar um método PreserveSig
, espera-se que int
seja o tipo gerenciado para HRESULT
. No entanto, usar um struct personalizado de 4 bytes como o tipo de retorno permite definir métodos auxiliares e propriedades que podem simplificar o trabalho com o HRESULT
. No marshalling interno, isso funciona automaticamente. Para usar um struct no lugar de int
como a representação gerenciada de HRESULT
no marshaling gerado pela origem, adicione o atributo MarshalAsAttribute com Error como o argumento. A presença desse atributo reinterpreta os bits do struct HRESULT
como o struct.