如何从C#外部程序访问指定的Bentley MicroStation V8i实例?
问题描述
我有一个通过API访问MicroStation(V8i)的C#应用程序,当前遇到以下问题:
- 同时打开多个MicroStation实例时,仅能访问第一个启动的实例
- 尝试用AutoCAD场景下的《Accessing COM applications from the Running Object Table》方案解决,但无效
- 使用
MicroStationDGN.ApplicationObjectConnector筛选运行对象表(ROT)时,返回的IMoniker数量与实例数一致,但所有对象引用都指向同一个实例(类似单例) - 测试代码如下:
string progId = "MicroStationDGN.ApplicationObjectConnector"; List<object> instances = GetRunningInstances(progId); bool theSame = instances[0] == instances[1]; // 当存在至少2个实例时,返回true
请问是否有方法可以访问已打开的MicroStation实例中的指定实例?
解决方案
针对MicroStation V8i的COM访问特性,你可以通过以下方式区分并访问指定实例:
1. 通过进程ID关联实例
MicroStation每个实例对应独立的ustation.exe进程,你可以通过COM对象的ProcessID属性获取关联进程ID,再与系统进程匹配:
using System.Diagnostics; using Bentley.MicroStation.InteropServices; // 获取所有MicroStation进程 var msProcesses = Process.GetProcessesByName("ustation"); // 从ROT获取所有ApplicationObjectConnector实例 string progId = "MicroStationDGN.ApplicationObjectConnector"; List<object> instances = GetRunningInstances(progId); // 遍历实例,匹配进程ID区分目标实例 foreach (var instance in instances) { var app = instance as Application; if (app != null) { int processId = app.ProcessID; var targetProcess = msProcesses.FirstOrDefault(p => p.Id == processId); if (targetProcess != null) { // 可通过窗口标题、启动时间进一步筛选 Console.WriteLine($"实例进程ID: {processId}, 窗口标题: {targetProcess.MainWindowTitle}"); // 找到目标实例后执行操作 // app.MessageBox("已定位到目标实例"); } } }
2. 通过打开的设计文件路径区分
每个MicroStation实例通常对应不同的DGN文件,你可以通过ActiveDesignFile属性获取文件路径,以此识别目标实例:
using Bentley.MicroStation.InteropServices; string targetDgnPath = @"C:\Projects\TargetDesign.dgn"; string progId = "MicroStationDGN.ApplicationObjectConnector"; List<object> instances = GetRunningInstances(progId); Application targetInstance = null; foreach (var instance in instances) { var app = instance as Application; if (app != null && app.ActiveDesignFile.FullName.Equals(targetDgnPath, StringComparison.OrdinalIgnoreCase)) { targetInstance = app; break; } } if (targetInstance != null) { // 对目标实例执行操作 targetInstance.MessageBox("已找到目标实例"); }
3. 修正ROT实例的获取逻辑
原GetRunningInstances方法可能未正确解析IMoniker的唯一标识,你可以修改方法读取DisplayName来区分实例:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; public static List<object> GetRunningInstances(string progId) { List<object> instances = new List<object>(); IRunningObjectTable rot = null; IEnumMoniker enumMoniker = null; try { GetRunningObjectTable(0, out rot); rot.EnumRunning(out enumMoniker); enumMoniker.Reset(); IMoniker[] moniker = new IMoniker[1]; int fetched; while (enumMoniker.Next(1, moniker, out fetched) == 0 && fetched == 1) { IBindCtx bindCtx = null; CreateBindCtx(0, out bindCtx); string displayName; moniker[0].GetDisplayName(bindCtx, null, out displayName); if (displayName.Contains(progId)) { object instance; rot.GetObject(moniker[0], out instance); instances.Add(instance); } Marshal.ReleaseComObject(bindCtx); Marshal.ReleaseComObject(moniker[0]); } } finally { if (enumMoniker != null) Marshal.ReleaseComObject(enumMoniker); if (rot != null) Marshal.ReleaseComObject(rot); } return instances; } // 导入COM交互所需API [DllImport("ole32.dll")] private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); [DllImport("ole32.dll")] private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
说明
MicroStation V8i的ApplicationObjectConnector设计上可能返回共享代理对象,但通过获取实例的进程ID或关联文件路径,即可精准区分并访问指定实例。
内容的提问来源于stack exchange,提问作者Marek Praski
相关产品推荐
相关产品推荐

