You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#操作Excel跨工作表访问时出现NullReferenceException异常求助

问题描述

使用C#通过Excel Interop执行操作时,仅操作同一工作表程序运行正常,切换操作不同工作表时,会在按名称获取指定工作表的代码位置抛出空引用异常:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
复现代码如下:

private object e;
private Microsoft.Office.Interop.Excel.Workbook oWB;

private void GetActiveExcelFile()
{
    try
    {
        e = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
    }
    catch (Exception ex)
    {
        Type type = Type.GetTypeFromProgID("Excel.Application");
        e = System.Activator.CreateInstance(type);
    };
    Microsoft.Office.Interop.Excel.Application oExcel = (Microsoft.Office.Interop.Excel.Application)e;
    oWB = oExcel.ActiveWorkbook;
}

bool q1()
{    
    GetActiveExcelFile();
    try
    {   
        Excel.Worksheet esh=new Excel.Worksheet();     
        esh = oWB.Worksheets["sheet 1"]; // 异常抛出位置
        if (esh.ListObjects["Table1"].ShowTableStyleRowStripes)
            return true;
    }
    catch { return false; }
    return false;
}

bool q2()
{
    GetActiveExcelFile();
    try
    {
        Excel.Worksheet esh=new Excel.Worksheet();
        esh = oWB.Worksheets["sheet 2"];
        if (esh.ListObjects["Table2"].ListColumns["Total"].Range.NumberFormat.ToString() == "0.000" ||
            esh.Cells.Range["B2:B9"].NumberFormat.ToString() == "0.000" ||
            esh.Cells.Range["B1:B9"].NumberFormat.ToString() == "0.000")
            return true;
    }
    catch { return false; }
    return false;
}
问题根因
  • 工作表名称匹配逻辑错误:Excel Interop的Worksheets索引器对工作表名是完全精确匹配(区分大小写、匹配空格),默认新建工作表名为Sheet1/Sheet2(无空格、首字母大写),代码中写的"sheet 1"/"sheet 2"和实际表名不匹配时会直接返回null,后续对null对象操作自然触发空引用。单表操作正常通常是因为测试时刚好操作的是激活状态的默认表,或手动修改过表名和代码字符串一致,切换表后名称不匹配就触发异常。
  • 新建Excel实例逻辑有漏洞:当GetActiveObject获取不到运行中的Excel实例时,代码通过Activator.CreateInstance新建的Excel实例默认没有打开任何工作簿,此时oExcel.ActiveWorkbook本身就是null,后续访问oWB.Worksheets直接触发空引用,和是否切换工作表无关。
  • Interop对象实例化写法错误:Excel.Worksheet是COM接口,不能通过new直接实例化,Excel.Worksheet esh=new Excel.Worksheet();这行本身就会抛出互操作错误,只是被外层无差别catch吞掉了异常,没有被感知到。
  • 异常捕获逻辑掩盖真实错误:所有业务逻辑都被无条件catch包裹直接返回false,真实异常堆栈、具体null对象信息被完全吞掉,很容易误判异常触发位置。
修复方案
  1. 修正工作表名称写法,保证代码中传入的表名和Excel中实际表名完全一致,不确定名称时可以先遍历Worksheets集合打印所有表名核对,也可以用索引(如Worksheets[1])获取表。
  2. 补全新建Excel实例的逻辑,新启动实例后需要手动创建工作簿或打开目标文件,不能直接读取ActiveWorkbook。
  3. 删除错误的new Excel.Worksheet()写法,所有工作表、区域、表对象都从对应父集合中获取。
  4. 不要写无差别吞异常的catch,调试阶段至少要打印异常信息方便排查,访问集合元素前先做非空判断。

修复后的参考代码:

using Excel = Microsoft.Office.Interop.Excel;
using System.Diagnostics;

private Excel.Application oExcel;
private Excel.Workbook oWB;

private void GetActiveExcelFile()
{
    try
    {
        oExcel = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
        // 处理运行中实例无打开工作簿的场景
        if (oExcel.Workbooks.Count == 0)
        {
            oWB = oExcel.Workbooks.Add();
        }
        else
        {
            oWB = oExcel.ActiveWorkbook;
        }
    }
    catch
    {
        // 无运行中实例时新建实例并初始化工作簿
        Type type = Type.GetTypeFromProgID("Excel.Application");
        oExcel = (Excel.Application)System.Activator.CreateInstance(type);
        oExcel.Visible = true; // 按需设置Excel窗口是否可见
        oWB = oExcel.Workbooks.Add();
    }
}

bool q1()
{    
    GetActiveExcelFile();
    if (oWB == null) return false;
    try
    {   
        // 替换错误的new实例写法,显式用Item取集合元素
        Excel.Worksheet esh = oWB.Worksheets.Item["sheet 1"];
        if (esh == null) return false;
        // 判断表是否存在
        if (esh.ListObjects.Count == 0) return false;
        Excel.ListObject targetTable = esh.ListObjects.Item["Table1"];
        if (targetTable == null) return false;
        
        return targetTable.ShowTableStyleRowStripes;
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"q1执行错误: {ex.Message}");
        return false;
    }
}

bool q2()
{
    GetActiveExcelFile();
    if (oWB == null) return false;
    try
    {
        Excel.Worksheet esh = oWB.Worksheets.Item["sheet 2"];
        if (esh == null) return false;
        
        bool formatMatch = false;
        // 先判断Table2是否存在,避免直接索引返回null
        foreach (Excel.ListObject t in esh.ListObjects)
        {
            if (t.Name == "Table2")
            {
                Excel.ListColumn totalCol = t.ListColumns.Item["Total"];
                if (totalCol != null && totalCol.Range.NumberFormat.ToString() == "0.000")
                {
                    formatMatch = true;
                }
                break;
            }
        }
        // 检查单元格格式
        if (!formatMatch)
        {
            string rangeFormat1 = esh.Range["B2:B9"].NumberFormat.ToString();
            string rangeFormat2 = esh.Range["B1:B9"].NumberFormat.ToString();
            formatMatch = rangeFormat1 == "0.000" || rangeFormat2 == "0.000";
        }
        return formatMatch;
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"q2执行错误: {ex.Message}");
        return false;
    }
}
额外注意事项
  • 操作Excel Interop时,取集合元素尽量显式调用.Item[]方法,不要直接用方括号索引,避免不同版本PIA程序集的兼容性问题。
  • 操作完成后需要手动释放所有COM对象(Application、Workbook、Worksheet、Range等),否则Excel进程会在后台持续残留占用内存。
  • 如果不需要和用户已打开的Excel交互,优先用OpenXML、NPOI等第三方库操作Excel文件,不需要依赖本地安装的Office,也不会有进程残留、互操作异常的问题。

内容的提问来源于stack exchange,提问作者Van Le Thanh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:09:17