C# Selenium如何读写本机已打开的Excel文件数据
结论
完全可以直接连接用户设备上已经打开的Excel文件完成读写,不需要提前知道文件存储路径,也不需要强制关闭或重启Excel进程重新打开目标文件,该逻辑可以直接集成在C# Selenium自动化项目中使用。
实现原理
所有被Excel程序打开的工作簿对应的COM实例都会注册到系统运行对象表(ROT)中,我们不需要启动新的Excel进程,只需要遍历ROT中所有运行的Excel实例,匹配已知的目标文件名即可定位到对应工作簿对象,后续读写逻辑和常规Excel Interop操作完全一致。
注意:网上常见的
Marshal.GetActiveObject("Excel.Application")方法仅能获取系统中第一个注册的Excel实例,当用户打开多个Excel窗口/多个工作簿时大概率出现匹配错误,必须通过全量遍历ROT的方式实现精准匹配。
前置准备
- 在项目依赖中添加COM引用,选择和本机安装Excel版本匹配的
Microsoft Excel XX.X Object Library,将该引用的「嵌入互操作类型」属性设置为True即可,无需额外引入第三方NuGet包。
完整实现代码
首先引入所需命名空间:
using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Excel = Microsoft.Office.Interop.Excel;
核心方法:遍历ROT匹配目标工作簿
[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); /// <summary> /// 根据文件名获取已打开的Excel工作簿对象 /// </summary> /// <param name="targetFileName">目标文件名,例如"业务统计.xlsx"</param> /// <returns>匹配到的工作簿,未找到返回null</returns> public static Excel.Workbook GetOpenedWorkbook(string targetFileName) { IRunningObjectTable rot = null; IEnumMoniker monikerEnumerator = null; try { GetRunningObjectTable(0, out rot); rot.EnumRunning(out monikerEnumerator); IMoniker[] monikers = new IMoniker[1]; IntPtr fetched = IntPtr.Zero; while (monikerEnumerator.Next(1, monikers, fetched) == 0) { IBindCtx bindCtx = null; CreateBindCtx(0, out bindCtx); monikers[0].GetDisplayName(bindCtx, null, out string objectName); if (objectName.StartsWith("!Microsoft Excel")) { rot.GetObject(monikers[0], out object comObj); if (comObj is Excel.Application excelApp) { foreach (Excel.Workbook wb in excelApp.Workbooks) { if (wb.Name.Equals(targetFileName, StringComparison.OrdinalIgnoreCase)) { return wb; } } Marshal.ReleaseComObject(excelApp); } } Marshal.ReleaseComObject(monikers[0]); Marshal.ReleaseComObject(bindCtx); } return null; } finally { if (monikerEnumerator != null) Marshal.ReleaseComObject(monikerEnumerator); if (rot != null) Marshal.ReleaseComObject(rot); } }
读写操作调用示例:
// 匹配名为"测试数据.xlsx"的已打开文件 Excel.Workbook targetWb = GetOpenedWorkbook("测试数据.xlsx"); if (targetWb == null) { throw new Exception("未检测到处于打开状态的目标Excel文件,请确认文件已正常打开"); } // 读取操作:获取第一个工作表A1单元格的值 Excel.Worksheet activeWs = targetWb.Worksheets[1]; var cellValue = activeWs.Range["A1"].Value2; // 写入操作:向B2单元格写入指定内容 activeWs.Range["B2"].Value2 = "Selenium自动化流程写入内容"; // 操作完成后按创建逆序释放COM对象,避免进程残留 Marshal.ReleaseComObject(activeWs); Marshal.ReleaseComObject(targetWb);
注意事项
- 匹配逻辑仅校验文件名,不需要提供文件的完整存储路径,文件名匹配大小写不敏感。
- 所有COM对象必须按创建顺序的逆序手动释放,禁止依赖GC回收,否则会导致后台残留Excel进程,即使用户手动关闭Excel窗口,进程也会持续驻留占用文件。
- 如果目标Excel是以管理员权限启动的,自动化程序也需要以管理员权限运行,否则无法枚举到ROT中的对应实例,会出现找不到目标文件的问题。
- 如果文件处于受保护视图状态,需要先启用编辑才能执行写入操作,否则会抛出权限异常。
- 连接已打开文件执行读写不会中断用户当前的Excel操作,写入完成后用户可以直接看到修改内容,不需要重新加载文件。
内容的提问来源于stack exchange,提问作者SenatorUppercut
相关产品推荐
相关产品推荐

