Compartilhar via


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.

Confira também