Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Aviso
Marshaling entre apartamentos a partir de código .NET: Quando um aplicativo .NET cria um objeto COM em processo cujo ThreadingModel não corresponde ao apartamento de chamada, o COM cria o objeto em um apartamento diferente e retorna um proxy. Chamar métodos por meio desse proxy incorre em sobrecarga de marshaling e pode causar impasses se o thread do apartamento de destino estiver bloqueado.
Padrão de falha comum:
// A .NET console app defaults to MTA (no explicit CoInitializeEx).
// Creating an Apartment-threaded COM object forces COM to spin up
// a hidden STA thread. If that STA thread has no message pump,
// calls that require marshaling back to it will hang.
// Fix: If your COM object requires STA, mark Main with [STAThread]:
[STAThread]
static void Main(string[] args)
{
var comObj = new MyCOMObject(); // Created in the STA — no proxy needed
}
Melhores práticas para consumidores .NET de servidores COM em processo:
- Verifique o valor de registro do objeto em
HKCR\CLSID\{...}\InprocServer32 - Use
[STAThread]no seu ponto de entrada se estiver consumindo objetos com threads de apartamento (WinForms/WPF fazem isso automaticamente) - Nunca chame
Thread.Join()ouTask.Wait()em uma thread STA — useawaitou bombeie mensagens comDispatcher.PushFrame()
Um servidor em processo não chama CoInitialize, CoInitializeEx ou OleInitialize para marcar seu modelo de threading. Para objetos baseados em DLL ou em processo com reconhecimento de thread, você precisa definir o modelo de threading no Registro. O modelo padrão quando você não especifica um modelo de threading é um único thread por processo. Para especificar um modelo, adicione o valor ThreadingModel à chave InprocServer32 no registro.
As DLLs que dão suporte à instanciação de um objeto de classe devem implementar e exportar as funções DllGetClassObject e DllCanUnloadNow. Quando um cliente deseja uma instância da classe compatível com a DLL, uma chamada para CoGetClassObject (diretamente ou por meio de uma chamada para CoCreateInstance) chama DllGetClassObject para obter um ponteiro para seu objeto de classe quando o objeto é implementado em uma DLL. DllGetClassObject deve, portanto, ser capaz de distribuir vários objetos de classe ou um único objeto thread-safe (essencialmente apenas usando InterlockedIncrement/InterlockedDecrement em suas contagens de referência internas).
Como o nome indica, DllCanUnloadNow é chamado para determinar se a DLL que a implementa está em uso, permitindo que o chamador o descarregue com segurança se não estiver. Chamadas para CoFreeUnusedLibraries de qualquer thread sempre percorrem o thread do apartamento principal para chamar DllCanUnloadNow.
Assim como outros servidores, os servidores em processo podem ser de encadeamento único, de encadeamento por apartamento ou de encadeamento livre. Esses servidores podem ser usados por qualquer cliente OLE, independentemente do modelo de threading usado por esse cliente.
Todas as combinações de interoperabilidade entre modelos de encadeamento são permitidas entre clientes e objetos no processo. A interação entre um cliente e um objeto em processo que usa modelos de threading diferentes é exatamente como a interação entre clientes e servidores fora de processo. Para um servidor em processo, quando o modelo de threading do cliente e do servidor em processo é diferente, o COM deve se interpor entre o cliente e o objeto.
Quando um objeto em processo que dá suporte ao modelo de thread único é chamado simultaneamente por vários threads de um cliente, o COM não pode permitir que os threads do cliente acessem diretamente a interface do objeto (o objeto não foi projetado para esse acesso). Em vez disso, o COM deve garantir que as chamadas sejam sincronizadas e sejam feitas apenas pelo thread do cliente que criou o objeto. Portanto, COM cria o objeto no apartamento principal do cliente e requer que todos os outros apartamentos cliente acessem o objeto usando proxies.
Quando um apartamento com thread livre (modelo de apartamento multithreaded) em um cliente cria um servidor em processo com thread de apartamento, o COM cria um thread de "host" de modelo de apartamento único no cliente. A thread principal criará o objeto, e o ponteiro da interface será encaminhado de volta para o apartamento de threads livres do cliente. Da mesma forma, quando um apartamento de thread único em um cliente de modelo de apartamento cria um servidor em processo de thread livre, o COM cria um thread de host sem thread (apartamento multithread no qual o objeto será criado e, em seguida, empacotado de volta para o apartamento de thread único do cliente).
Note
Em geral, se você criar uma interface personalizada em um servidor em processo, também deverá fornecer o código de marshaling para ele para que o COM possa realizar marshaling da interface entre os apartamentos cliente.
O COM ajuda a proteger o acesso a objetos fornecidos por uma DLL de thread único, exigindo acesso do mesmo apartamento do cliente no qual foram criados. Além disso, todos os pontos de entrada de DLL (como DllGetClassObject e DllCanUnloadNow) e dados globais sempre devem ser acessados pelo mesmo apartamento. O COM cria esses objetos no apartamento principal do cliente, dando ao apartamento principal acesso direto aos ponteiros do objeto. As chamadas dos outros apartamentos utilizam marshaling entre threads para transitar do proxy para o stub no apartamento principal e, em seguida, para o objeto. Isso permite que COM sincronize chamadas para o objeto. As chamadas interthread são lentas, portanto, é recomendável que esses servidores sejam reescritos para dar suporte a vários apartamentos.
Como um servidor de thread única em processo, um objeto fornecido por uma DLL de modelo de apartamentos deve ser acessado pelo mesmo apartamento cliente de onde foi criado. No entanto, os objetos fornecidos por esse servidor podem ser criados em vários apartamentos do cliente; portanto, o servidor deve implementar seus pontos de entrada (como DllGetClassObject e DllCanUnloadNow) para uso multithread. Por exemplo, se dois apartamentos de um cliente tentarem criar duas instâncias do objeto em processo simultaneamente, DllGetClassObject poderá ser chamado simultaneamente por ambos os apartamentos. DllCanUnloadNow deve ser gravado para que a DLL não seja descarregada enquanto o código ainda estiver em execução na DLL.
Se a DLL fornecer apenas uma única instância da fábrica de classes para criar todos os objetos, a fábrica de classes também deve ser projetada para uso multi-thread, pois será acessada por vários apartamentos de cliente. Se a DLL criar uma nova instância da fábrica de classes sempre que DllGetClassObject for chamado, a fábrica de classes não precisará ser thread-safe.
Os objetos criados pela fábrica de classes não precisam ser thread-safe. Uma vez criado por um thread, o objeto é sempre acessado por esse thread e todas as chamadas para o objeto são sincronizadas por COM. O modelo de apartamento do cliente que criou esse objeto obterá um ponteiro direto para o objeto. Os apartamentos cliente que são diferentes do apartamento no qual o objeto foi criado devem acessar o objeto por meio de proxies. Esses proxies são criados quando o cliente marshala a interface entre seus apartamentos.
Quando uma DLL em processo ThreadingModel valor é definido como "Ambos", um objeto fornecido por essa DLL pode ser criado e usado diretamente (sem um proxy) em apartamentos cliente de thread único ou multithreaded. No entanto, ele pode ser usado diretamente somente dentro do apartamento no qual foi criado. Para dar o objeto a qualquer outro apartamento, o objeto deve ser empacotado. O objeto DLL deve implementar sua própria sincronização e pode ser acessado por vários apartamentos de cliente ao mesmo tempo.
Para acelerar o desempenho do acesso com threading livre a objetos DLL dentro do processo, o COM fornece a função CoCreateFreeThreadedMarshaler. Essa função cria um objeto de marshaling com thread livre que pode ser agregado com um objeto de servidor em processo. Quando um apartamento cliente no mesmo processo precisa de acesso a um objeto em outro apartamento, agregar o marshaler de thread livre fornece ao cliente um ponteiro direto para o objeto do servidor, em vez de para um proxy, quando o cliente realiza o marshaling da interface do objeto para um apartamento diferente. O cliente não precisa fazer nenhuma sincronização. Isso funciona somente no mesmo processo; o marshaling padrão é usado como referência ao objeto que é enviado para outro processo.
Importante
Substituição moderna para CoCreateFreeThreadedMarshaler: Para código novo destinado ao Windows 8.1+, prefira RoGetAgileReference e a interface IAgileReference. Eles fornecem uma maneira mais segura de passar referências de objetos entre apartamentos sem as armadilhas do marshaler de thread livre (que ignora completamente a proteção de apartamento e pode mascarar bugs de threading). Para objetos WinRT, a agilidade é o padrão — os objetos WinRT dão suporte a IAgileObject automaticamente, a menos que optem explicitamente por não oferecer esse suporte.
Um objeto fornecido por uma DLL em processo que dá suporte apenas ao threading gratuito é um objeto de thread livre. Ele implementa sua própria sincronização e pode ser acessado por vários threads de cliente ao mesmo tempo. Esse servidor não realiza marshaling de interfaces entre threads, portanto, esse servidor pode ser criado e usado diretamente (sem um proxy) apenas por apartamentos multithread em um cliente. Os apartamentos de thread único que o criarem o acessarão por meio de um proxy.
Tópicos relacionados: