Quem cria scripts de automação e suporte no Windows já precisou montar aquele clássico coletor de diagnóstico: uma ferramenta leve que o usuário executa para levantar versão do sistema operacional, interfaces de rede, IPs locais/públicos, resolução DNS de servidores de correio, portas abertas (443, 587), expiração de certificados e rotas de traceroute.
Ao testar um coletor em PowerShell feito para diagnosticar chamadas de conectividade de e-mail, me deparei com uma situação curiosa. Com o Novo Outlook (New Outlook) aberto e minimizado na barra de tarefas, o script teimava em cuspir:
== VERSAO DO OUTLOOK (INSTALADO OU NAO) ==
Outlook nao encontrado
O aplicativo estava rodando na minha frente, mas a verificação insistia em afirmar que ele não existia no computador.
Abaixo, explico por que a abordagem clássica baseada em chaves do Registro do Windows falha nas versões modernas do Office e como refatorar a detecção usando Get-AppxPackage.
1. O diagnóstico: onde estava o erro no registro?#
A lógica original de verificação baseava-se em chaves do Registro do usuário atual (HKCU), procurando por parâmetros do pacote Office tradicional:
# ABORDAGEM PROBLEMÁTICA ORIGINAL
Write-Line "`n== VERSAO DO OUTLOOK (INSTALADO OU NAO) =="
$officeRegPaths = @(
"HKCU:\Software\Microsoft\Office\16.0\Common\ExperimentEcs\Overrides",
"HKCU:\Software\Microsoft\Office\16.0\Outlook\Options"
)
$outlookStatus = "Outlook nao encontrado"
foreach ($path in $officeRegPaths) {
if (Test-Path $path) {
$reg = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
if ($reg -ne $null -and $reg.EnableNewOutlook -eq 1) {
$outlookStatus = "Nova Experiencia (New)"
} elseif ($reg -ne $null -and $reg.EnableNewOutlook -eq 0) {
$outlookStatus = "Experiencia Classica (Classic)"
}
}
}
Write-Line $outlookStatus
Por que essa checagem falhou?#
Essa lógica parte da premissa de que o Outlook Clássico (Win32 / MSO) está instalado e o usuário apenas ativou a chave de alternância (toggle "Experimentar o novo Outlook"). Nesse caso específico de transição, o Office cria a chave EnableNewOutlook no HKCU.
No entanto, o Novo Outlook (New Outlook) é um aplicativo moderno empacotado (UWP/PWA distribuído pela Microsoft Store ou pré-instalado no Windows 11). Em computadores que já vêm com ele de fábrica ou que não possuem a suíte Office Win32 tradicional, a árvore HKCU:\Software\Microsoft\Office\16.0 simplesmente não existe.
Como o caminho não era encontrado, o loop terminava silenciosamente e o script caía no fallback padrão: "Outlook nao encontrado".
2. A solução definitiva com Get-AppxPackage#
Para identificar o cliente de e-mail com precisão, a ordem de verificação precisa ser invertida:
- Primeira prioridade: Consultar o catálogo de pacotes de aplicativos modernos do Windows (
Get-AppxPackage -Name "Microsoft.OutlookForWindows"). - Segundo passo (Fallback): Caso o pacote AppX não exista, inspecionar a instalação do Office tradicional (Click-to-Run no
HKLM). - Terceiro passo (Fallback legatário): Checar instalações antigas via instalador MSI no
HKCU.
Refatorei o bloco de detecção para o seguinte código:
# Versao do Outlook (Tratamento para New Outlook e Classic)
Write-Line "`n== VERSAO DO OUTLOOK (INSTALADO OU NAO) =="
$outlookStatus = "Outlook nao encontrado"
try {
# 1. Verificacao prioritaria do Novo Outlook (App moderno / Microsoft Store)
$newOutlook = Get-AppxPackage -Name "Microsoft.OutlookForWindows" -ErrorAction SilentlyContinue
if ($newOutlook) {
$outlookStatus = "Nova Experiencia (New Outlook instalado via App)"
} else {
# 2. Checagem de instalacao do Office classico (Click-to-Run)
$classicOutlookPath = "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration"
if (Test-Path $classicOutlookPath) {
$officeSuite = Get-ItemProperty -Path $classicOutlookPath
if ($officeSuite.ProductReleaseIds -like "*Outlook*") {
$outlookStatus = "Experiencia Classica (Classic) instalada via ClickToRun"
}
} else {
# 3. Fallback para instalacoes MSI antigas
$classicOutlookMSIPath = "HKCU:\Software\Microsoft\Office\16.0\Outlook\Setup"
if (Test-Path $classicOutlookMSIPath) {
$outlookStatus = "Experiencia Classica (Classic) instalada via MSI"
}
}
}
} catch {
$outlookStatus = "Erro ao verificar a versao do Outlook."
}
Write-Line $outlookStatus
3. O script completo de diagnóstico de rede e correio#
Abaixo compartilho o script coletor completo e tratado. Ele inclui remoção automática de acentos (para evitar erros de codificação no terminal), teste de portas via socket TCP .NET nativo, checagem de certificados de usuário e tracert com barra de progresso em tempo real:
# Script Diagnostico Coletor - Versao Final com Tratamento de Caracteres e Caminho Fixo
$DataHora = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$arquivo = "C:\diagnostico_$DataHora.txt"
# Funcao para remover acentuacao de textos antes da escrita
function Remove-Acento {
param ($texto)
$normalized = $texto.Normalize([System.Text.NormalizationForm]::FormD)
return -join ($normalized.ToCharArray() | Where-Object { [System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne 'NonSpacingMark' })
}
# Funcao de teste rapido de conectividade via TCP Socket nativo
function Test-Port {
param(
[string]$hostname,
[int]$port,
[int]$timeout = 3000
)
$tcpClient = New-Object System.Net.Sockets.TcpClient
try {
$asyncResult = $tcpClient.BeginConnect($hostname, $port, $null, $null)
$wait = $asyncResult.AsyncWaitHandle.WaitOne($timeout, $false)
if (!$wait) {
return $false
}
$tcpClient.EndConnect($asyncResult)
return $true
}
catch {
return $false
}
finally {
$tcpClient.Close()
}
}
# Funcao de escrita padronizada sem acentos
function Write-Line {
param([string]$line)
$lineSemAcento = Remove-Acento $line
$lineSemAcento | Out-File -FilePath $arquivo -Append
}
Write-Line "===== INICIO DO DIAGNOSTICO =====`n"
Write-Line "Data e Hora: $DataHora`n"
# 1. Informacoes do Sistema
Write-Line "== DADOS DO SISTEMA =="
systeminfo | Out-String | ForEach-Object { Write-Line $_ }
# 2. Usuario Logado
Write-Line "`n== USUARIO LOGADO =="
whoami | Out-String | ForEach-Object { Write-Line $_ }
# 3. IP Local e IP Publico
Write-Line "`n== IP LOCAL E PUBLICO =="
Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.InterfaceAlias -notlike "Loopback*"} | Select-Object IPAddress, InterfaceAlias | Out-String | ForEach-Object { Write-Line $_ }
Write-Line "`nIP Publico:"
try {
$ipPublico = Invoke-RestMethod -Uri 'https://api.ipify.org' -UseBasicParsing
Write-Line $ipPublico
} catch {
Write-Line "Nao foi possivel obter o IP publico."
}
# 4. Versao do Outlook (Tratamento para New Outlook e Classic)
Write-Line "`n== VERSAO DO OUTLOOK (INSTALADO OU NAO) =="
$outlookStatus = "Outlook nao encontrado"
try {
$newOutlook = Get-AppxPackage -Name "Microsoft.OutlookForWindows" -ErrorAction SilentlyContinue
if ($newOutlook) {
$outlookStatus = "Nova Experiencia (New Outlook instalado via App)"
} else {
$classicOutlookPath = "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration"
if (Test-Path $classicOutlookPath) {
$officeSuite = Get-ItemProperty -Path $classicOutlookPath
if ($officeSuite.ProductReleaseIds -like "*Outlook*") {
$outlookStatus = "Experiencia Classica (Classic) instalada via ClickToRun"
}
} else {
$classicOutlookMSIPath = "HKCU:\Software\Microsoft\Office\16.0\Outlook\Setup"
if (Test-Path $classicOutlookMSIPath) {
$outlookStatus = "Experiencia Classica (Classic) instalada via MSI"
}
}
}
} catch {
$outlookStatus = "Erro ao verificar a versao do Outlook."
}
Write-Line $outlookStatus
# 5. DNS e Conectividade Microsoft 365
Write-Line "`n== DNS E CONECTIVIDADE COM MICROSOFT =="
Write-Line "Resolvendo outlook.office365.com:"
$proc = Start-Process nslookup -ArgumentList "outlook.office365.com" -NoNewWindow -RedirectStandardOutput nslookup_output.txt -PassThru
if ($proc.WaitForExit(5000)) {
Get-Content nslookup_output.txt | ForEach-Object { Write-Line $_ }
} else {
$proc.Kill()
Write-Line "Timeout no nslookup outlook.office365.com"
}
Remove-Item nslookup_output.txt -ErrorAction SilentlyContinue
Write-Line "Conexao com outlook.office365.com na porta 443:"
if (Test-Port -hostname "outlook.office365.com" -port 443) {
Write-Line "Porta 443 aberta e acessivel."
} else {
Write-Line "Falha ao conectar na porta 443."
}
Write-Line "Conexao com smtp.office365.com na porta 587:"
if (Test-Port -hostname "smtp.office365.com" -port 587) {
Write-Line "Porta 587 aberta e acessivel."
} else {
Write-Line "Falha ao conectar na porta 587."
}
# 6. Certificados instalados - validade
Write-Line "`n== CERTIFICADOS INSTALADOS (USER ROOT) =="
$certs = Get-ChildItem -Path Cert:\CurrentUser\Root | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(30) }
if ($certs.Count -eq 0) {
Write-Line "Todos os certificados estao atualizados (validos por pelo menos 30 dias)."
} else {
Write-Line "ATENCAO: Encontrados certificados expirando ou expirados:"
$certs | Select-Object Subject, NotAfter | Format-Table -AutoSize | Out-String | ForEach-Object { Write-Line $_ }
}
# 7. Teste de rota (tracert) com atualizacao em tempo real
Write-Line "`n== TESTE DE ROTA (TRACERT) =="
$hostsList = @(
@{Nome="antispam-01.seusite.com.br"; IP="192.0.2.10"; Ordem=10},
@{Nome="antispam-02.seusite.com.br"; IP="198.51.100.20"; Ordem=20}
)
function Run-TracertWithProgress {
param(
[string]$ip,
[string]$nome,
[int]$maxHops = 30
)
Write-Line "`n-> Traceroute para $nome [$ip]"
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo.FileName = "tracert.exe"
$proc.StartInfo.Arguments = $ip
$proc.StartInfo.UseShellExecute = $false
$proc.StartInfo.RedirectStandardOutput = $true
$proc.StartInfo.CreateNoWindow = $true
$proc.Start() | Out-Null
while (-not $proc.HasExited) {
while (!$proc.StandardOutput.EndOfStream) {
$line = $proc.StandardOutput.ReadLine()
Write-Line $line
if ($line -match '^\s*(\d+)') {
$hopCount = [int]$matches[1]
$percent = [int](($hopCount / $maxHops) * 100)
Write-Progress -Activity "Executando Tracert" -Status "Hop $hopCount de $maxHops" -PercentComplete $percent
}
}
Start-Sleep -Milliseconds 200
}
while (!$proc.StandardOutput.EndOfStream) {
$line = $proc.StandardOutput.ReadLine()
Write-Line $line
}
Write-Progress -Activity "Executando Tracert" -Completed
}
foreach ($servidor in $hostsList | Sort-Object Ordem) {
Run-TracertWithProgress -ip $servidor.IP -nome $servidor.Nome -maxHops 30
}
Write-Line "`n===== FIM DO DIAGNOSTICO ====="
# Abre o arquivo no final para inspecao imediata
Start-Process notepad.exe $arquivo
Lição prática para scripts no Windows#
A transição de aplicativos Win32 clássicos para aplicações modernas empacotadas muda a forma como o sistema armazena configurações. Ao criar coletores e rotinas em PowerShell, priorizar Get-AppxPackage antes de buscar chaves sob HKCU:\Software\Microsoft\Office evita falsos negativos e garante diagnósticos confiáveis em qualquer versão do Windows.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários