Copiando e fixando
Ao realizar marshalling dos dados, o marshaler de interoperabilidade pode copiar ou fixar os dados nos quais o marshalling está sendo realizado. A cópia dos dados coloca uma cópia dos dados de um local de memória em outro local de memória. A ilustração a seguir mostra as diferenças entre a cópia de um tipo de valor e a cópia de um tipo passado por referência da memória gerenciada para a não gerenciada.
Os argumentos de método passados por valor nos quais está sendo realizado o marshalling para o código não gerenciado como valores na pilha. O processo de cópia é direto. Os argumentos passados por referência são passados como ponteiros na pilha. Tipos de referência também são passados por valor e por referência. Como mostra a ilustração a seguir, os tipos de referência passados por valor são copiados ou fixados:
A anexação temporária bloqueia os dados em seu local atual de memória, impedindo, portanto, que eles sejam relocados pelo coletor de lixo do Common Language Runtime. O marshaler fixa os dados para reduzir a sobrecarga da cópia e melhorar o desempenho. O tipo dos dados determina se eles são copiados ou fixados durante o processo de marshalling. A fixação é executada automaticamente durante o marshalling de objetos, como String. No entanto, também é possível fixar a memória manualmente usando a classe GCHandle.
Classes blittable formatadas
As classes blittable formatadas têm o layout fixo (formatado) e uma representação de dados comum na memória gerenciada e não gerenciada. Quando esses tipos exigem o marshalling, um ponteiro para o objeto no heap é passado diretamente para o computador chamado. O receptor pode alterar o conteúdo do local de memória que está sendo referenciado pelo ponteiro.
Observação
O computador chamado poderá alterar o conteúdo da memória se o parâmetro estiver marcado como Out ou In/Out. Por outro lado, o computador chamado deve evitar alterar o conteúdo quando o parâmetro é definido para marshal como In, que é o padrão para tipos blittable formatados. A modificação de um objeto de Entrada gera problemas quando a mesma classe é exportada para uma biblioteca de tipos e usada para fazer chamadas entre apartments.
Classes não blittable formatadas
As classes não blittable formatadas têm o layout fixo (formatado), mas a representação de dados é diferente na memória gerenciada e não gerenciada. Os dados podem exigir transformação nas seguintes condições:
Se uma classe não blittable tem o marshalling realizado por valor, o computador chamado recebe um ponteiro para uma cópia da estrutura de dados.
Se uma classe não blittable tem o marshalling realizado por referência, o computador chamado recebe um ponteiro para um ponteiro para uma cópia da estrutura de dados.
Se o atributo InAttribute for definido, essa cópia será sempre inicializada com o estado da instância, com marshalling conforme necessário.
Se o atributo OutAttribute for definido, o estado será sempre copiado novamente para a instância após o retorno, com marshalling conforme necessário.
Se InAttribute e OutAttribute forem definidos, ambas as cópias serão necessárias. Se um dos atributos for omitido, o marshaller poderá otimizar eliminando a cópia.
Tipos de referência
Tipos de referência podem ser passados por valor ou por referência. Quando eles são passados por valor, um ponteiro para o tipo é passado na pilha. Quando são passados por referência, um ponteiro para um ponteiro para o tipo é passado na pilha.
Os tipos de referência têm o seguinte comportamento condicional:
Se um tipo de referência é passado por valor e tem membros de tipos não blittable, os tipos são convertidos duas vezes:
Quando um argumento é passado para o lado não gerenciado.
Após o retorno da chamada.
Para evitar cópia e conversões desnecessárias, esses tipos têm o marshalling realizado em parâmetros In. Aplique os atributos InAttribute e OutAttribute explicitamente a um argumento para que o chamador veja as alterações feitas pelo receptor.
Se um tipo de referência é passado por valor e tem somente membros de tipos blittable, ele pode ser fixado durante o marshalling e todas as alterações feitas nos membros do tipo pelo computador chamado são vistas pelo chamador. Aplique InAttribute e OutAttribute explicitamente se desejar obter esse comportamento. Sem esses atributos direcionais, o marshaller de interoperabilidade não exporta as informações direcionais para a biblioteca de tipos (ele exporta-as como Entrada, que é o padrão) e isso poderá causar problemas com o marshalling entre apartments do COM.
Se um tipo de referência for passado por referência, ele terá o marshalling realizado como Entrada/Saída por padrão.
System.String e System.Text.StringBuilder
Quando os dados têm o marshalling realizado para um código não gerenciado por valor ou por referência, o marshaller normalmente copia os dados para um buffer secundário (possivelmente, convertendo conjuntos de caracteres durante a cópia) e passa uma referência para o buffer para o computador chamado. A menos que a referência seja um BSTR alocado com SysAllocString, a referência é sempre alocada com CoTaskMemAlloc.
Como uma otimização quando um tipo String ou StringBuilder tem o marshalling realizado por valor (como uma cadeia de caracteres Unicode), o marshaller passa ao computador chamado um ponteiro direto para cadeias de caracteres gerenciadas no buffer interno do Unicode, em vez de copiá-lo para um novo buffer.
Cuidado
Quando uma cadeia de caracteres é passada por valor, o receptor nunca deve alterar a referência passada pelo marshaller. Isso pode corromper o heap gerenciado.
Quando um System.String é passado por referência, o marshaller copia o conteúdo da cadeia de caracteres para um buffer secundário antes de fazer a chamada. Em seguida, ele copia o conteúdo do buffer para uma nova cadeia de caracteres após o retorno da chamada. Essa técnica garante que a cadeia de caracteres gerenciada imutável permaneça inalterada.
Quando um System.Text.StringBuilder é passado por valor, o marshaller passa uma referência a uma cópia temporária do buffer interno do StringBuilder diretamente para o chamador. O chamador e o receptor devem concordar com o tamanho do buffer. O chamador é responsável pela criação de um StringBuilder de tamanho adequado. O receptor deve tomar as precauções necessárias para garantir que o buffer não tenha estouro. StringBuilder é uma exceção à regra em que os tipos de referência passados por valor são passados como parâmetros de In
por padrão. StringBuilder
é sempre passado como In
/Out
.