适用场景
- 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 时记录HResult
和remoteHost
-
指标
- 成功率(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 流水线里,都可以快速集成这段代码。