Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Este artigo fornece observações complementares à documentação de referência para esta API.
A interface ICustomMarshaler fornece invólucros personalizados para lidar com chamadas de método.
Um marshaller fornece uma ponte entre a funcionalidade de interfaces antigas e novas. O marshaling personalizado oferece os seguintes benefícios:
- Ele permite que aplicativos cliente que foram projetados para trabalhar com uma interface antiga também trabalhem com servidores que implementam uma nova interface.
- Permite que aplicações cliente desenvolvidas para funcionar com uma nova interface operem com servidores que implementam uma interface antiga.
Se tiver uma interface que introduza um comportamento de marshaling diferente ou que seja exposta ao COM (Component Object Model) de uma forma diferente, poderá projetar um marshaller personalizado em vez de usar o marshaller de interop. Usando um marshaller personalizado, você pode minimizar a distinção entre novos componentes do .NET Framework e componentes COM existentes.
Por exemplo, suponha que você esteja desenvolvendo uma interface gerenciada chamada INew. Quando essa interface é exposta ao COM por meio de um wrapper chamável COM (CCW) padrão, ela tem os mesmos métodos da interface gerida e usa as regras de marshaling incorporadas no marshaller de interoperabilidade. Agora suponha que uma interface COM bem conhecida chamada IOld já fornece a mesma funcionalidade que a INew interface. Ao conceber um marshaller personalizado, pode fornecer uma implementação não-gerida de IOld que simplesmente delega as chamadas à implementação gerida da interface INew. Portanto, o marshaller personalizado atua como uma ponte entre as interfaces gerenciadas e não gerenciadas.
Observação
Os marshallers personalizados não são invocados ao chamar de código gerido para código não gerido numa interface exclusivamente de distribuição.
Definir o tipo de marshalling
Antes de criar um marshaller personalizado, você deve definir as interfaces gerenciadas e não gerenciadas que serão empacotadas. Essas interfaces geralmente executam a mesma função, mas são expostas de forma diferente a objetos gerenciados e não gerenciados.
Um compilador gerenciado produz uma interface gerenciada a partir de metadados, e a interface resultante se parece com qualquer outra interface gerenciada. O exemplo a seguir mostra uma interface típica.
public interface INew
{
void NewMethod();
}
Public Interface INew
Sub NewMethod()
End Interface
Você define o tipo não gerido na Interface Definition Language (IDL) e compila-o com o compilador Microsoft Interface Definition Language (MIDL). Você define a interface dentro de uma instrução de biblioteca e atribui a ela uma ID de interface com o atributo identificador exclusivo universal (UUID), como demonstra o exemplo a seguir.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
[uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
interface IOld : IUnknown
HRESULT OldMethod();
}
O compilador MIDL produz vários arquivos de saída. Se a interface for definida em Old.idl, o arquivo de saída Old_i.c define uma const variável com o identificador de interface (IID) da interface, como demonstra o exemplo a seguir.
const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};
O arquivo Old.h também é produzido pelo MIDL. Ele contém uma definição C++ da interface que pode ser incluída no código-fonte C++.
Implementar a interface ICustomMarshaler
O seu marshaller personalizado deve implementar a ICustomMarshaler interface para fornecer os wrappers apropriados para o tempo de execução.
O código C# a seguir exibe a interface base que deve ser implementada por todos os marshallers personalizados.
public interface ICustomMarshaler
{
Object MarshalNativeToManaged(IntPtr pNativeData);
IntPtr MarshalManagedToNative(Object ManagedObj);
void CleanUpNativeData(IntPtr pNativeData);
void CleanUpManagedData(Object ManagedObj);
int GetNativeDataSize();
}
Public Interface ICustomMarshaler
Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
Sub CleanUpNativeData( pNativeData As IntPtr )
Sub CleanUpManagedData( ManagedObj As Object )
Function GetNativeDataSize() As Integer
End Interface
A ICustomMarshaler interface inclui métodos que fornecem suporte à conversão, suporte à limpeza e informações sobre os dados a serem empacotados.
| Tipo de operação | Método ICustomMarshaler | Descrição |
|---|---|---|
| Conversão (de código nativo para código gerenciado) | MarshalNativeToManaged | Transforma um ponteiro para dados nativos num objeto gerido. Esse método retorna um wrapper chamável de tempo de execução personalizado (RCW) que pode empacotar a interface não gerenciada que é passada como um argumento. O marshaller deve retornar uma instância do RCW personalizado para esse tipo. |
| Conversão (de código gerenciado para código nativo) | MarshalManagedToNative | Converte um objeto gerido em um ponteiro para dados nativos. Esse método retorna um wrapper chamável COM personalizado (CCW) que pode empacotar a interface gerenciada que é passada como um argumento. O marshaller deve retornar uma instância do CCW personalizado para esse tipo. |
| Limpeza (de código nativo) | CleanUpNativeData | Permite ao marshaller limpar os dados nativos (o CCW) que são retornados pelo método MarshalManagedToNative. |
| Limpeza (de código gerenciado) | CleanUpManagedData | Permite que o marshaller limpe os dados geridos (o RCW) retornados pelo método MarshalNativeToManaged. |
| Informações (sobre código nativo) | GetNativeDataSize | Retorna o tamanho dos dados não gerenciados a serem empacotados. |
Conversão
ICustomMarshaler.MarshalNativeToManaged
Transforma um ponteiro para dados nativos num objeto gerido. Esse método retorna um wrapper chamável de tempo de execução personalizado (RCW) que pode empacotar a interface não gerenciada que é passada como um argumento. O marshaller deve retornar uma instância do RCW personalizado para esse tipo.
ICustomMarshaler.MarshalManagedToNative
Converte um objeto gerido em um ponteiro para dados nativos. Esse método retorna um wrapper chamável COM personalizado (CCW) que pode empacotar a interface gerenciada que é passada como um argumento. O marshaller deve retornar uma instância do CCW personalizado para esse tipo.
Limpeza
ICustomMarshaler.CleanUpNativeData
Permite ao marshaller limpar os dados nativos (o CCW) que são retornados pelo método MarshalManagedToNative.
ICustomMarshaler.CleanUpManagedData
Permite que o marshaller limpe os dados geridos (o RCW) retornados pelo método MarshalNativeToManaged.
Informações sobre o tamanho
ICustomMarshaler.GetNativeDataSize
Retorna o tamanho dos dados não gerenciados a serem empacotados.
Observação
Se um marshaller personalizado chamar qualquer método que defina o último erro P/Invoke ao realizar o intercâmbio de dados de nativo para gerido ou ao limpar, o valor retornado por Marshal.GetLastWin32Error() e Marshal.GetLastPInvokeError() representará a chamada nos processos de intercâmbio de dados ou de limpeza. Isso pode fazer com que erros passem despercebidos ao usar marshallers personalizados com P/Invokes com DllImportAttribute.SetLastError definido como true. Para preservar o último erro de P/Invoke, use os métodos Marshal.GetLastPInvokeError() e Marshal.SetLastPInvokeError(Int32) na implementação ICustomMarshaler.
Implementar o método GetInstance
Além de implementar a ICustomMarshaler interface, os marshallers personalizados devem implementar um método static chamado GetInstance que aceita a String como parâmetro e tem um tipo de retorno de ICustomMarshaler. Este static método é chamado pela camada de interoperabilidade COM do Common Language Runtime para criar uma instância do marshaller personalizado. A cadeia de caracteres que é passada para GetInstance é um cookie que o método pode usar para personalizar o marshaller personalizado retornado. O exemplo a seguir mostra uma implementação mínima, mas completa ICustomMarshaler .
public class NewOldMarshaler : ICustomMarshaler
{
public static ICustomMarshaler GetInstance(string pstrCookie)
=> new NewOldMarshaler();
public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
public int GetNativeDataSize() => throw new NotImplementedException();
}
Aplicar o atributo MarshalAsAttribute
Para usar um marshaller personalizado, deve aplicar o atributo MarshalAsAttribute ao parâmetro ou campo que está a ser processado.
Você também deve passar o UnmanagedType.CustomMarshaler valor de enumeração para o MarshalAsAttribute construtor. Além disso, você deve especificar o MarshalType campo com um dos seguintes parâmetros nomeados:
MarshalType (obrigatório): O nome qualificado para montagem do marshaller personalizado. O nome deve incluir o namespace e a classe do marshaller personalizado. Se o marshaller personalizado não estiver definido no assembly em que é usado, você deverá especificar o nome do assembly no qual ele está definido.
Observação
Você pode usar o MarshalTypeRef campo em vez do MarshalType campo. MarshalTypeRef usa um tipo que é mais fácil de especificar.
MarshalCookie (opcional): um cookie que é passado para o marshaler personalizado. Você pode usar o cookie para fornecer informações adicionais ao processador. Por exemplo, se o mesmo marshaller for usado para fornecer vários encapsuladores, o cookie identifica um encapsulador específico. O cookie é passado para o método
GetInstancedo marshaller.
O atributo MarshalAsAttribute identifica o marshaller personalizado, de modo a ativar a envolvente apropriada. O serviço de interoperabilidade do Common Language Runtime examina o atributo e cria o marshaller personalizado na primeira vez que o argumento (parâmetro ou campo) precisa ser marshalado.
O tempo de execução, em seguida, chama os métodos MarshalNativeToManaged e MarshalManagedToNative no marshaller personalizado para ativar o wrapper correto para lidar com a chamada.
Use um marshaller personalizado
Quando o marshaller personalizado estiver completo, você poderá usá-lo como um invólucro personalizado para um tipo específico. O exemplo a seguir mostra a definição da interface gerenciada IUserData :
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
No exemplo a seguir, a interface IUserData usa o marshaller personalizado NewOldMarshaler para permitir que aplicativos cliente não gerenciados passem uma interface IOld para o método DoSomeStuff. A descrição gerenciada do DoSomeStuff método usa uma INew interface, como mostrado no exemplo anterior, enquanto a versão não gerenciada do DoSomeStuff usa um ponteiro de IOld interface, como mostrado no exemplo a seguir.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
[uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
interface IUserData : IUnknown
HRESULT DoSomeStuff(IUnknown* pIOld);
}
A biblioteca de tipos gerada pela exportação da definição gerenciada produz a definição não gerenciada mostrada IUserData neste exemplo em vez da definição padrão. O MarshalAsAttribute atributo aplicado ao INew argumento na definição gerenciada do DoSomeStuff método indica que o argumento usa um marshaller personalizado, como mostra o exemplo a seguir.
using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
void DoSomeStuff(
[MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType="NewOldMarshaler")]
INew pINew
);
}
Public Interface IUserData
Sub DoSomeStuff( _
<MarshalAs(UnmanagedType.CustomMarshaler, _
MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface
Nos exemplos anteriores, o primeiro parâmetro fornecido ao atributo MarshalAsAttribute é o valor de enumeração UnmanagedType.CustomMarshalerUnmanagedType.CustomMarshaler.
O segundo parâmetro é o MarshalType campo, que fornece o nome qualificado para montagem do marshaller personalizado. Esse nome consiste no namespace e na classe do marshaller personalizado (MarshalType="MyCompany.NewOldMarshaler").