# 检测 COM 服务器在线状态

适用场景

  • OPC DA/OPC AE 等基于 DCOM 的工业软件巡检
  • 自动化部署脚本中批量验证远程 COM 组件是否可用
  • Windows 服务开机自检

1. 背景

在工业控制与运维场景下,我们经常需要判断某台机器上的 COM / DCOM 服务器 (例如 OPC Server)是否存活,并在掉线时及时告警或自动重连。

.NET 自带的 System.Type.GetTypeFromProgID / Activator.CreateInstance 能直接拉起组件并建立 Secure Channel;结合异常捕获即可快速判定服务可达性。


2. 核心代码

csharp 复制代码
/// <summary>
/// 判断 COM 服务是否在线
/// </summary>
/// <param name="progId">ProgID,例如 "Kepware.KEPServerEX.V6"</param>
/// <param name="remoteHost">远程主机;可为 "localhost"/计算机名/IP,默认本机</param>
/// <returns>true = 在线;false = 离线/异常</returns>
public bool TestComOnline(string progId, string remoteHost = "localhost")
{
    try
    {
        // ① 获取 COM 类型(若未注册或无权访问将返回 null)
        var type = Type.GetTypeFromProgID(progId, remoteHost, true);
        if (type == null) return false;

        // ② 激活对象。默认 CLSCTX_INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER (0x15)
        var obj = Activator.CreateInstance(type, null);

        // ③ 立即释放,避免资源泄漏
        Marshal.ReleaseComObject(obj);
        return true;
    }
    catch (COMException ex) when (
        ex.HResult == unchecked((int)0x800706BA) ||  // RPC_S_SERVER_UNAVAILABLE
        ex.HResult == unchecked((int)0x800706BE))    // RPC_S_CALL_FAILED
    {
        _logger.LogError($"TestComOnline COMException: {ex.Message}");
        return false;   // 通道建立失败 → 服务端不在线 / 防火墙阻断
    }
    catch (Exception ex)
    {
        _logger.LogError($"TestComOnline Exception: {ex.Message}");
        return false;   // ProgID 不存在、权限不足等
    }
}

3. 关键实现要点

步骤 细节 容易踩的坑
GetTypeFromProgID 通过注册表 HKCR\<ProgID>\CLSID 定位 CLSID,再解析 DCOM Endpoint - 如果传入 remoteHost,要求目标机器打开 DCOM 并允许远程激活 - 若组件未注册,将抛 TypeLoadException
Activator.CreateInstance 触发 DCOM 激活;若远端进程未启动,会自动拉起 - 第一次拉起耗时较长,可考虑异步 - 若用户权限不足会抛 UnauthorizedAccessException
异常过滤 0x800706BA/BE → RPC 通道不可达 - 其它 COMException 仍可能是"离线"表现,视业务决定是否全捕获
资源释放 Marshal.ReleaseComObject 及时释放 RCW - 不释放会导致 OPC Server 中对象计数增加,严重时"卡死"

4. HRESULT 错误码对照

错误码 十进制 说明
0x800706BA -2147023174 RPC_S_SERVER_UNAVAILABLE:远程服务器不可用
0x800706BE -2147023166 RPC_S_CALL_FAILED:RPC 调用失败(会话被强制关闭)
0x800401F3 -2147221005 CO_E_CLASSSTRING:ProgID/CLSID 格式错误或不存在
0x80070005 -2147024891 E_ACCESSDENIED:DCOM 未授权、UAC 拦截

5. 日志与监控建议

  • 日志级别

    • Info:周期性巡检的成功日志可降级为 Debug,避免刷屏
    • Error:捕获到 COMException 时记录 HResultremoteHost
  • 指标

    • 成功率(Success / Total)
    • 平均连接耗时(Stopwatch 统计)
  • 告警

    • 连续 N 次失败后触发短信 / Email
    • 可结合 Prometheus + Grafana 进行可视化

6. DCOM 配置检查清单

位置 关键项 建议
dcomcnfg组件服务 目标 ProgID 所在的 应用程序 ID "安全性"里勾选 允许远程激活
Windows 防火墙 RPC Endpoint Mapper (135/TCP) + 组件动态端口 工控网段可考虑放行或固定端口
用户权限 运行账户需在目标机 Distributed COM Users 生产环境建议使用专用域服务账号

7. 完整调用示例

csharp 复制代码
var servers = new[]
{
    ("Kepware.KEPServerEX.V6", "10.1.17.5"),
    ("Matrikon.OPC.Simulation.1", "localhost")
};

foreach (var (progId, host) in servers)
{
    bool online = TestComOnline(progId, host);
    Console.WriteLine($"{host} / {progId} → {(online ? "在线" : "离线")}");
}

输出示例:

复制代码
10.1.17.5 / Kepware.KEPServerEX.V6 → 在线
localhost / Matrikon.OPC.Simulation.1 → 离线

8. 小结

  • TestComOnline 通过 DCOM 激活一次对象即可判断服务是否在线,轻量、准确。
  • 合理捕获典型 HRESULT,便于区分"服务器掉线"与"配置/权限"问题。
  • 在批量巡检、容器化部署或 CI/CD 流水线里,都可以快速集成这段代码。