MSMQ: acesso remoto a fila privada não gera exceção — causas, ACK/NACK e correção

Removeu permissões na fila privada do MSMQ remoto e, mesmo assim, o Send não lança exceção? Este guia explica o porquê, mostra como confirmar falhas com ACK/NACK, e traz um passo a passo completo de diagnóstico, scripts e boas práticas para ambientes Windows/Windows Server.

Índice

Contexto do problema

Um cliente em um servidor Windows envia mensagens para uma fila privada do MSMQ hospedada em outro servidor, na mesma rede e domínio. Quando o computador‑cliente possui permissões completas na fila remota, envio e leitura funcionam normalmente. Ao remover essas permissões, esperava-se um erro imediato (por exemplo, “acesso negado”) ao chamar Send, porém a chamada retorna sem exceções aparentes. Por quê?

Como o MSMQ realmente funciona (e por que o Send não falha)

O MSMQ é assíncrono por design. A chamada Send não envia diretamente para a fila remota; ela coloca a mensagem em uma fila de saída local do serviço MSMQ no cliente. Depois, o serviço tenta a entrega ao destino em segundo plano. Se houver problemas de permissão, conectividade ou configuração, o erro pode ocorrer depois do retorno da sua chamada, e você só fica sabendo via:

  • Mensagens de confirmação (acknowledgements, ou ACK/NACK) enviadas para uma fila de administração que você especifica; e/ou
  • Filas de erros (Dead‑Letter / Trans. Dead‑Letter) e logs do Event Viewer.

Em outras palavras: Send “dá certo” localmente porque o serviço aceitou sua mensagem para processamento assíncrono — a falha efetiva (por exemplo, “sem permissão para gravar na fila remota”) surge após a tentativa de entrega, e não no momento do Send.

As causas mais prováveis e como atacar cada uma

Possível causaExplicação resumidaComo detectarO que fazer
Cache de configuração do MSMQApós alterar permissões, o cliente/servidor pode ainda usar ACLs em cache.Alterou ACL e o comportamento não mudou imediatamente.Reinicie o serviço Message Queuing em ambos os hosts para forçar atualização.
Fila local × fila remotaO código, sem perceber, aponta para uma fila local com o mesmo nome.Envios “sempre funcionam”, mesmo com ACLs remotas restritas.Verifique o FormatName completo, garantindo que é remoto (ex.: FormatName:DIRECT=OS:SERVIDOR\private$\fila).
Alterações não aplicadasAs permissões não foram de fato gravadas/propagadas.Ao reabrir propriedades, as ACLs não refletem as mudanças.Confirme as ACLs, salve novamente e aguarde replicação do AD, se aplicável.
Modo “insecure” / políticasConfigurações que relaxam validações de segurança podem mascarar falhas.Discrepância entre o esperado (acesso negado) e o observado.Revise HKLM\SOFTWARE\Microsoft\MSMQ\Parameters\Security e políticas de grupo.
Credenciais do processoO serviço/usuário de execução tem privilégios amplos (Domain Admin, LocalSystem, etc.).Somente quando executa como “Admin” tudo parece funcionar.Teste com uma conta padrão e identifique privilégios efetivos.
Firewalls e regras de serviçoBloqueios de rede não geram exceção imediata no Send.Entrega falha “silenciosamente” e há timeouts em segundo plano.Confirme portas 1801/TCP, 2101–2108 e DCOM/RPC; habilite logs.
ACK/NACK ausentesSem ACK/NACK, você não enxerga as falhas pós‑Send.Não há registros de chegada/recusa; DLQ cresce.Ative ACK/NACK na mensagem e/ou monitore a DLQ/Journal para confirmação.

Entendendo ACK/NACK no MSMQ (o “sinal vital” da entrega)

Para tornar a entrega observável, peça que o MSMQ envie mensagens de confirmação para uma fila de administração que você controla. Os tipos de confirmação mais úteis:

TipoO que indicaQuando usar
PositiveArrivalA mensagem chegou à fila de destino.Para validar conectividade/ACLs de gravação.
PositiveReceiveA mensagem foi recebida pela aplicação consumidora.Para confirmar processamento no destino.
NegativeArrivalNão foi possível alcançar a fila de destino.Para diagnosticar DNS/firewall/ACL inexistente.
NegativeReceiveA mensagem não foi recebida a tempo ou foi rejeitada.Para detectar falhas no consumidor ou TTL curto.

Dica: Ative também a DLQ (Dead‑Letter), pois mensagens não entregues/expiradas acabam lá. Para mensagens transacionais, a DLQ é a Transacional específica.

Diagnóstico passo a passo (playbook prático)

  1. Certifique-se do endereço remoto
    Use FormatName explícito para uma fila remota privada: FormatName:DIRECT=OS:SERVIDOR\private$\minhaFila FormatName:DIRECT=TCP:10.0.0.10\private$\minhaFila Evite caminhos ambíguos como .\private$\minhaFila (isso aponta para a máquina local).
  2. Reinicie o serviço MSMQ nos dois extremos
    Após mudanças de segurança/ACL, reinicie o serviço Message Queuing no cliente e no servidor hospedando a fila: sc stop MSMQ sc start MSMQ
  3. Ative ACK/NACK e crie uma fila de administração local
    Sem confirmações, você não verá os erros que ocorrem depois do Send.
  4. Monitore Event Viewer
    Verifique “Aplicativo” (origem: MSMQ) e, se disponível, “Aplicativos e Serviços → Microsoft → Windows → MSMQ”.
  5. Cheque permissões efetivas
    Teste com uma conta comum. Se possível, isole a conta de serviço do cliente para não herdar privilégios excessivos.
  6. Revise firewall/rede
    Garanta abertura das portas típicas (ex.: 1801/TCP; 2101–2108 conforme cenário) e DCOM/RPC, além de regras internas de inspeção.
  7. Observe DLQ/Journal
    Crescimento de DLQ ou ausência de registros no Journal dão pistas de falha de entrega/recebimento.
  8. AD e replicação
    Em ambientes com Active Directory, aguarde a replicação das mudanças de segurança entre controladores de domínio.

Exemplo em C#: enviando com ACK/NACK e verificando chegada

O exemplo abaixo usa System.Messaging (MSMQ) para enviar uma mensagem para uma fila privada remota usando FormatName e solicitar ACK/NACK para uma fila local de administração (que é automaticamente criada se não existir).

using System;
using System.Messaging;

class Program
{
static void Main()
{
// Fila de destino REMOTA (ajuste o host e o nome)
var destino = @"FormatName:DIRECT=OS:SERVIDOR\private$\minhaFila";
```
    // Fila local para receber ACK/NACK (pode ser privada local)
    var ackPath = @".\private$\ack_msmq";
    if (!MessageQueue.Exists(ackPath))
    {
        MessageQueue.Create(ackPath, transactional: false);
    }

    using var destinoQ = new MessageQueue(destino);
    using var ackQ = new MessageQueue(ackPath);

    // Solicitar confirmações de chegada e de falhas
    var msg = new Message
    {
        Body = "mensagem de diagnóstico",
        Label = "diag-msmq",
        Formatter = new BinaryMessageFormatter(),
        AdministrationQueue = ackQ,
        AcknowledgeType = AcknowledgeTypes.PositiveArrival
                          | AcknowledgeTypes.NegativeArrival
                          | AcknowledgeTypes.NegativeReceive
    };

    // (opcional) usar DLQ e TTL para enxergar falhas rapidamente
    msg.UseDeadLetterQueue = true;
    msg.TimeToReachQueue = TimeSpan.FromSeconds(30);     // ajustar à sua rede
    msg.TimeToBeReceived = TimeSpan.FromMinutes(2);

    destinoQ.Send(msg); // pode "funcionar" localmente, mesmo com falha remota

    // Ler o primeiro ACK/NACK recebido
    ackQ.MessageReadPropertyFilter.SetAll();
    try
    {
        var ack = ackQ.Receive(TimeSpan.FromSeconds(60));
        Console.WriteLine($"ACK: {ack.Acknowledgment} - CorrelationId: {ack.CorrelationId}");
    }
    catch (MessageQueueException ex) when (ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
    {
        Console.WriteLine("Sem ACK/NACK em até 60s. Verifique rede/ACL/TTL.");
    }
}
```
} 

Interpretação: Se vier um PositiveArrival, a fila remota aceitou a mensagem (logo, você tinha permissão de escrita). Um NegativeArrival costuma indicar que a mensagem não chegou (bloqueio de rede, fila inexistente, permissão negada, etc.). Um NegativeReceive sugere que a aplicação consumidora não retirou a mensagem a tempo.

Exemplo em PowerShell: envio com ACK/NACK

# Executar no cliente
Add-Type -AssemblyName System.Messaging

$destino = "FormatName:DIRECT=OS:SERVIDOR\private$\minhaFila"
$ackPath = ".\private$\ack_msmq"

if (-not [System.Messaging.MessageQueue]::Exists($ackPath)) {
[System.Messaging.MessageQueue]::Create($ackPath) | Out-Null
}

$destQ = New-Object System.Messaging.MessageQueue $destino
$ackQ  = New-Object System.Messaging.MessageQueue $ackPath

$msg = New-Object System.Messaging.Message
$msg.Body = "diagnostico"
$msg.Label = "diag-msmq"
$msg.Formatter = New-Object System.Messaging.BinaryMessageFormatter
$msg.AdministrationQueue = $ackQ
$msg.AcknowledgeType = [System.Messaging.AcknowledgeTypes]::PositiveArrival `                     -bor [System.Messaging.AcknowledgeTypes]::NegativeArrival`
-bor [System.Messaging.AcknowledgeTypes]::NegativeReceive
$msg.UseDeadLetterQueue = $true
$msg.TimeToReachQueue = [TimeSpan]::FromSeconds(30)

$destQ.Send($msg)

$ackQ.MessageReadPropertyFilter.SetAll()
try {
$ack = $ackQ.Receive([TimeSpan]::FromSeconds(60))
"ACK: $($ack.Acknowledgment)  CorrelationId: $($ack.CorrelationId)"
} catch {
"Sem ACK/NACK em 60s."
} 

Checklist rápido de investigação

  • Nome correto da fila: use FormatName:DIRECT=OS:SERVIDOR\private$\fila ou DIRECT=TCP:<ip>; confirme que não é .\private$.
  • Permissões mínimas: para enviar, a identidade do cliente precisa ao menos de WriteMessage. Para ler, ReceiveMessage/PeekMessage.
  • Reinício do serviço: reinicie o MSMQ no cliente e no servidor após alterar ACLs.
  • Portas/rede: verifique 1801/TCP (direct) e, conforme o cenário, 2101–2108 e DCOM/RPC.
  • ACK/NACK: sempre habilite acknowledgements em cenários críticos ou de diagnóstico.
  • DLQ/Journal: monitore as filas de erro e o journal para rastrear o ciclo de vida das mensagens.
  • Event Viewer: procure por eventos do MSMQ no cliente e no servidor.
  • Credenciais: teste execução com um usuário padrão (sem privilégios elevados).
  • AD: leve em conta o tempo de replicação de permissões.

Diferenças entre fila privada e pública

Filas privadas não são publicadas no Active Directory; você sempre as referencia por FormatName (geralmente via DIRECT=...) ou PathName local (.\private$\...). Filas públicas podem ser resolvidas via AD. No problema em questão, a fila é privada e remota, o que reforça a importância de usar FormatName:DIRECT=... e de checar conectividade/ACL diretamente no host de destino.

Erros “silenciosos” comuns e como tornar visíveis

  • Permissão insuficiente no destino: ative PositiveArrival/NegativeArrival. Se vier NegativeArrival, a gravação não ocorreu.
  • Fila inexistente no destino: NegativeArrival + eventos no log do servidor.
  • Firewall/DNS: NegativeArrival e/ou expiração do TimeToReachQueue com mensagem indo para DLQ.
  • Aplicação consumidora indisponível: PositiveArrival (chegou) porém NegativeReceive após o TTL de recebimento.

Como confirmar “é realmente remoto”

Três verificações simples:

  1. Inspeção visual no código/config: procure por DIRECT=OS:SERVIDOR\private$\fila ou DIRECT=TCP:IP\private$\fila. Se houver .\private$, é local.
  2. Desligue temporariamente o MSMQ do cliente: se o envio “funcionar” com o serviço desligado, você não está usando MSMQ local; se falhar de imediato, talvez referencie localmente.
  3. Crie uma fila local com o mesmo nome: se, ao criar .\private$\fila, o comportamento do envio mudar, havia ambiguidade.

Políticas e registro que impactam segurança

Em alguns ambientes, políticas de segurança do MSMQ podem permitir comportamentos mais permissivos (por exemplo, cenários legados). Revise chaves sob HKLM\SOFTWARE\Microsoft\MSMQ\Parameters\Security e as GPOs aplicáveis. Alinhamento com os times de Segurança/Infra evita surpresas, principalmente quando máquinas legadas convivem com versões mais novas do Windows Server.

Scripts úteis para o dia a dia

Reiniciar o serviço MSMQ nos dois lados

Invoke-Command -ComputerName CLIENTE,SERVIDOR `
  -ScriptBlock { sc stop MSMQ; sc start MSMQ }

Listar filas privadas locais (sanidade)

Get-ChildItem "HKLM:\SOFTWARE\Microsoft\MSMQ\Parameters\OCMsetup" | Out-Null
Em muitos servidores, você pode conferir rapidamente via MMC:
compmgmt.msc &rarr; Services and Applications &rarr; Message Queuing &rarr; Private Queues

Ver o crescimento da DLQ

# Caminhos padrão das DLQs do MSMQ:
.\System$;Deadletter                       (não transacional)
.\System$;Transactional dead-letter        (transacional)
Add-Type -AssemblyName System.Messaging
$dlq = New-Object System.Messaging.MessageQueue ".\System$;Deadletter"
$dlq.GetAllMessages().Count

Boas práticas para ambientes de produção

  • Padronize o uso de ACK/NACK em todas as integrações críticas. Trate a ausência de ACK como um incidente observável.
  • Defina TTLs realistas (TimeToReachQueue e TimeToBeReceived): curtos o bastante para não gerar filas “zumbi”, longos o suficiente para a sua rede.
  • Monitore DLQ e métricas (contagem por minuto, idade média de mensagens, etc.). Um spike na DLQ é sinal de problema.
  • Segregue contas de serviço por aplicação e ambiente; evite admins desnecessários.
  • Audite alterações de ACL em filas sensíveis. Documente quem pode Write/Receive.
  • Automatize verificações (ex.: um job diário que envia um “ping” MSMQ e valida o PositiveArrival).

FAQ rápido

“Por que eu nunca recebo exceção no Send?”
Porque o Send apenas enfileira localmente; a entrega real é assíncrona. Falhas aparecem via ACK/NACK, DLQ e logs.

“Consigo forçar uma validação imediata?”
Você pode abrir a fila explicitamente com as APIs nativas pedindo acesso de envio; se a infraestrutura recusar logo na abertura, você verá erro. Porém, em cenários remotos, boa parte das validações acontece no momento da entrega, e o método robusto continua sendo ACK/NACK + DLQ.

“A fila é transacional. Muda algo?”
Sim. Use transações ao enviar/receber e monitore a Transactional Dead‑Letter. Algumas propriedades (como DLQ não transacional) se comportam diferente.

“Uso .NET moderno. O System.Messaging ainda vale?”
O exemplo é voltado a Windows/Framework, onde MSMQ é tradicional. Em runtimes modernos no Windows, costuma-se usar interop ou serviços intermediários. O conceito de ACK/NACK e DLQ, contudo, permanece válido para o diagnóstico.

Modelo de decisão: o que observar primeiro

  1. Endereço: é remota via DIRECT=...?
  2. ACK: você está recebendo PositiveArrival ou algum Negative*?
  3. DLQ: cresceu após o teste?
  4. Event Viewer: há eventos de acesso negado/entrega falha?
  5. Rede: 1801/TCP (e 2101–2108/DCOM) liberados entre cliente e servidor?
  6. Credenciais: o processo cliente roda com privilégios além do esperado?
  7. Replicação: alterou ACL há pouco tempo em ambiente AD?

Resumo executivo

O Send do MSMQ “não falhar” após a remoção de permissões não é um bug; é consequência do modelo assíncrono. Para tornar falhas visíveis, peça ACK/NACK, monitore DLQ/Journal, e confira logs. Em paralelo, valide o FormatName remoto, reinicie o serviço após mudanças de ACL, e verifique portas/credenciais. Seguindo o playbook acima, você conseguirá identificar rapidamente se o problema é de permissão, endereço, rede ou consumo.

Passos práticos recomendados (recapitulação)

  1. Reinicie o serviço “Message Queuing” em ambos os hosts após alterar permissões.
  2. Use o Visualizador de Eventos (Aplicativo e MSMQ) para avisos/erros.
  3. Crie/automatize verificação de ACL por probe com ACK (C#/PowerShell).
  4. Teste com contas diferentes (usuário padrão vs. administrador).
  5. Ative confirmações (ACK/NACK) e DLQ; ajuste TTLs (TimeToReachQueue/TimeToBeReceived).
  6. Confirme FormatName remoto e regras de firewall/portas 1801, 2101–2108 e DCOM.
  7. Considere políticas/registro que afetam segurança (...MSMQ\Parameters\Security).

Conclusão: ao entender que o Send apenas inicia a entrega, e ao instrumentar o fluxo com ACK/NACK + DLQ + logs, você transforma um “silêncio” enganoso em um processo observável e confiável, reduzindo o tempo de resolução e aumentando a segurança operacional.

Índice