Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O .NET 7 introduz um gerador de código-fonte para P/Invokes que reconhece o LibraryImportAttribute código em C#.
Quando não está a usar a geração de código, o sistema de interoperabilidade incorporado no runtime .NET gera um stub IL — um fluxo de instruções IL que é compilado Just-In-Time — em tempo de execução para facilitar a transição de código gerido para código não gerido. O código a seguir mostra a definição e, em seguida, chamando um P/Invoke que usa esse mecanismo:
[DllImport(
"nativelib",
EntryPoint = "to_lower",
CharSet = CharSet.Unicode)]
internal static extern string ToLower(string str);
// string lower = ToLower("StringToConvert");
O stub IL lida com a organização de parâmetros e valores de retorno e chama o código não gerenciado, respeitando as configurações que DllImportAttribute afetam como o código não gerenciado deve ser invocado (por exemplo, SetLastError). Como este stub de IL é gerado em tempo de execução, não está disponível para o compilador ahead-of-time (AOT) ou cenários de recorte de IL. A geração da IL representa um custo importante a considerar para o marshaling. Esse custo pode ser medido em termos de desempenho do aplicativo e suporte para potenciais plataformas de destino que podem não permitir a geração de código dinâmico. O modelo de aplicativo AOT nativo aborda problemas com a geração de código dinâmico pré-compilando todo o código com antecedência diretamente no código nativo. Usar DllImport não é uma opção para plataformas que exigem cenários completos de AOT nativo e, portanto, usar outras abordagens (por exemplo, geração de origem) é mais apropriado. Depurar a lógica de empacotamento em DllImport cenários também é um exercício não trivial.
O gerador de código-fonte P/Invoke, incluído no SDK .NET 7 e ativado por padrão, procura por LibraryImportAttribute num método static e partial para ativar a geração de código de marshalling em tempo de compilação, eliminando a necessidade de gerar um stub IL em tempo de execução e permitindo que o P/Invoke seja integrado em linha.
Analisadores e fixadores de código também estão incluídos para ajudar na migração do sistema integrado para o gerador de origem e com o uso em geral.
Utilização básica
O LibraryImportAttribute é projetado para ser semelhante ao DllImportAttribute em uso. Podemos converter o exemplo anterior para usar a geração de origem P/Invoke usando o LibraryImportAttribute e marcando o método como partial em vez de extern:
[LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str);
Durante a compilação, o gerador de código-fonte será acionado para gerar uma implementação do ToLower método que lida com o empacotamento do parâmetro e o string valor de retorno como UTF-16. Como o empacotamento agora é gerado pelo código-fonte, você pode realmente olhar e percorrer a lógica em um depurador.
MarshalAs
O gerador de fonte também respeita o MarshalAsAttribute. O código anterior também pode ser escrito como:
[LibraryImport(
"nativelib",
EntryPoint = "to_lower")]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static partial string ToLower(
[MarshalAs(UnmanagedType.LPWStr)] string str);
Algumas configurações MarshalAsAttribute para não são suportadas. O gerador de origem emitirá um erro se você tentar usar configurações não suportadas. Para obter mais informações, consulte Diferenças de DllImport.
Convenção de chamada
Para especificar a convenção de chamada, use UnmanagedCallConvAttribute, por exemplo:
[LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(
CallConvs = new[] { typeof(CallConvStdcall) })]
internal static partial string ToLower(string str);
Diferenças de DllImport
LibraryImportAttribute destina-se a ser uma conversão direta de na maioria dos DllImportAttribute casos, mas há algumas mudanças intencionais:
- CallingConvention não tem equivalente em LibraryImportAttribute. UnmanagedCallConvAttribute em vez disso, deve ser utilizado.
- CharSet (para CharSet) foi substituído por StringMarshalling (para StringMarshalling). ANSI foi removido e UTF-8 está agora disponível como uma opção de primeira classe.
-
BestFitMapping e ThrowOnUnmappableChar não têm equivalente. Esses campos só eram relevantes ao empacotar uma cadeia de caracteres ANSI no Windows. O código gerado para empacotar uma cadeia de caracteres ANSI terá o comportamento equivalente de
BestFitMapping=falseeThrowOnUnmappableChar=false. -
ExactSpelling não tem equivalente. Este campo era uma configuração centrada no Windows e não tinha efeito em sistemas operacionais que não fossem Windows. O nome do método ou EntryPoint deve ser a ortografia exata do nome do ponto de entrada. Este campo tem usos históricos relacionados com o e
AsufixosWusados na programação Win32. - PreserveSig não tem equivalente. Este campo era uma configuração centrada no Windows. O código gerado sempre traduz diretamente a assinatura.
- O projeto deve ser marcado como não seguro usando AllowUnsafeBlocks.
Há também diferenças no suporte para algumas configurações em MarshalAsAttribute, empacotamento padrão de certos tipos e outros atributos relacionados à interoperabilidade. Para obter mais informações, consulte nossa documentação sobre diferenças de compatibilidade.