Por que meu script PowerShell dizia que o Outlook não existia? Caçando o novo Outlook no Windows
Voltar para blog

Por que meu script PowerShell dizia que o Outlook não existia? Caçando o novo Outlook no Windows

28/09/2026 · 3 min · Infraestrutura

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:

  1. Primeira prioridade: Consultar o catálogo de pacotes de aplicativos modernos do Windows (Get-AppxPackage -Name "Microsoft.OutlookForWindows").
  2. Segundo passo (Fallback): Caso o pacote AppX não exista, inspecionar a instalação do Office tradicional (Click-to-Run no HKLM).
  3. 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:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários