Pergunta

Se deparar com o que parece à primeira vista como uma MT-problema, mas eu estou tentando entender em detalhes o modelo STA usado por COM +.

eficazmente, tenho uma legado componente COM +, escrito em VB6, que põe em um nativa (isto é, não-COM) DLL Win32 escrita em C ++.

Tendo alguma intermittant (e impossível de reproduzir em testar) problemas com ele, eu adicionei um código de depuração para descobrir o que estava acontecendo e descobriram que, quando ocorrem os problemas, eu tinha mensagens de log intercalados no arquivo - por isso implicava que a DLL estava sendo chamado por dois threads ao mesmo tempo.

Agora o registo vai para um arquivo por thread baseado em _getpid () e GetCurrentThreadId (), então parece que quando o código no ++ DLL C é chamado, está sendo chamado duas vezes no mesmo segmento, ao mesmo tempo. Minha compreensão do STA diz sugere que este poderia ser o caso como COM Marshalls as instâncias individuais de objetos em um único suspende fios e continua a execução à vontade.

Unfortuantly Eu não tenho certeza para onde ir a partir daqui. Estou lendo que eu deveria estar chamando CoInitialiseEx () em DllMain () para contar COM que esta é uma DLL STA, mas outros lugares dizem que isso é válido somente para COM DLLs e não terá qualquer efeito em uma DLL nativa. A única outra opção é envolver partes do DLL como seções críticas ao acesso serialize (tendo tudo o acerto de desempenho que tem no queixo).

Eu poderia tentar refazer a DLL, mas não há vars estaduais ou globais não compartilhados - tudo está em variáveis ??locais portanto, em teoria cada chamada deve obter sua própria pilha, mas eu estou querendo saber se o modelo STA é basicamente ter algum estranho efeito sobre a esta e apenas re-entrar no DLL já carregado no mesmo ponto de entrada, tal como uma outra chamada. Unfortuantly, eu não sei como provar ou testar esta teoria.

As perguntas são basicamente:

  1. Quando um chamadas de componentes STA COM + a DLL nativa, não há nada no modelo STA para evitar a "thread" ativo de ser suspenso e controle que está sendo passado para outro "thread" no meio de uma chamada de DLL?
  2. é CoInitialiseEx () o caminho certo para resolver isso, ou não?
  3. Se nenhum (1) ou (2) são "boas" suposições, o que está acontecendo?
Foi útil?

Solução

Em um servidor COM apartamento segmentado, cada instância da classe COM é garantida para ser acessos por um único segmento. Isto significa a instância é thread-safe. No entanto, muitos casos podem ser criados simultaniously, usando diferentes threads. Agora, na medida em que o servidor COM está em causa, a sua DLL nativa não tem que fazer nada especial. Basta pensar em kernel32.dll, que é usado por todos os executável? - ele inicializar COM quando usado por um servidor COM

Do ponto de vista DLL, você tem que ter certeza que você está seguro fio, como instâncias diferentes podem chamá-lo ao mesmo tempo. STA não irá protegê-lo neste caso. Desde que você dizer que você não está usando qualquer variável global, só posso supor o problema está em outro lugar, e só acontece para mostrar em circunstâncias que parecem apontar para as coisas COM. Tem certeza de que não tem algumas ++ problemas de memória e velho C?

Outras dicas

Eu suspeito que o seu problema era que no fundo em algum lugar dentro do chamado DLL, ele fez uma chamada COM saída para outro apartamento (outro segmento no mesmo processo, ou um objeto na MTA, ou outro processo inteiramente). COM permite uma espera thread STA para o resultado de uma chamada para receber uma outra chamada de entrada, processá-lo de forma recursiva. É destinado somente para conversações em curso entre os mesmos objetos - ou seja, A chama B, B chama A de volta, A chama B novamente - mas pode receber chamadas de outros objetos se você já entregou um ponteiro de interface para vários clientes, ou o cliente já dividiu o ponteiro de interface para outro cliente. Geralmente é uma má idéia para distribuir ponteiros interface para um objeto single-threaded para vários segmentos de clientes, uma vez que só vai ter que esperar para o outro. Criar um objeto trabalhador por thread.

COM não é possível suspender e execução currículo à vontade em qualquer segmento - uma nova chamada em um thread STA só pode chegar através da bomba de mensagem. Quando 'bloqueado' à espera de uma resposta, o thread STA é realmente bombeamento de mensagens, verificar com o filtro de mensagens (ver IMessageFilter) se a mensagem deve ser tratada. No entanto, manipuladores de mensagem não deve fazer uma nova chamada de saída - se o fizerem COM retornará um erro RPC_E_CANTCALLOUT_INEXTERNALCALL ( "É ilegal para chamar enquanto filtro de mensagens de dentro")

Problemas semelhantes podem ocorrer se você tiver uma bomba de mensagem (GetMessage / DispatchMessage) em qualquer lugar dentro da DLL nativa. Eu tive problemas com DoEvents do VB em procedimentos de interface.

CoInitializeEx só deve ser chamado pelo criador de um fio, porque só eles sabem o seu comportamento bombeamento mensagem será. É provável que, se você tentar chamá-lo em DllMain ele vai simplesmente falhar, como seu DLL nativa está sendo chamado em resposta a uma chamada COM, de modo que o chamador deve ter em última análise, já chamado CoInitializeEx no thread, a fim de fazer a chamada. Fazê-lo na notificação DLL_THREAD_ATTACH, por tópicos recém-criados, pode trabalhar superficialmente, mas fazer com que o programa não funcione corretamente se os blocos COM quando deveria bombear e vice-versa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top