在一些企业内部系统中,Excel 宏仍然很常见,例如报表模板、财务表格、审批表、数据整理工具等。对于 C# 开发者来说,有时不仅需要生成普通 Excel 文件,还需要向 Excel 文件中写入 VBA 宏,或者读取、修改、删除已有文件中的宏代码。
通常我们会想到使用 Microsoft Excel Interop,但 Interop 需要本机安装 Excel,更适合桌面端自动化场景。在 Web 服务、后台任务、容器或服务器环境中,使用 Interop 可能会带来部署、权限和稳定性方面的问题。
本文介绍一种不依赖 Excel Interop 的处理方式,演示如何在 C# 中操作 Excel VBA 宏,主要内容包括:
目录
[一、Excel VBA 宏工程和模块简介](#一、Excel VBA 宏工程和模块简介)
[二、Excel 宏文件格式说明](#二、Excel 宏文件格式说明)
[四、使用 C# 在 Excel 中添加 VBA 宏](# 在 Excel 中添加 VBA 宏)
[五、使用 C# 读取 Excel 中的 VBA 宏](# 读取 Excel 中的 VBA 宏)
[六、使用 C# 修改 Excel 中的 VBA 宏代码](# 修改 Excel 中的 VBA 宏代码)
[七、使用 C# 删除 Excel 中的 VBA 宏](# 删除 Excel 中的 VBA 宏)
[八、C# 操作 Excel 宏的实用建议](# 操作 Excel 宏的实用建议)
一、Excel VBA 宏工程和模块简介
在 Excel VBA 中,宏代码并不是直接存放在工作表单元格中,而是存放在 VBA 工程(VBA Project)中的不同模块里。不同模块适合不同类型的代码场景。
1. 标准模块
标准模块是最常用的 VBA 模块类型。平时录制宏、编写通用工具代码、自定义函数时,通常都会使用标准模块。
标准模块常用于:
-
存放普通
Sub宏过程 -
编写通用数据处理逻辑
-
编写报表生成或格式化代码
-
编写可在单元格中调用的自定义函数
例如:
Sub FormatReport()
MsgBox "开始格式化报表"
End Sub
如果希望宏能够在 Excel 的"宏"窗口中直接查看和运行,通常会把它放在标准模块中。
2. 其他 VBA 模块类型
除了标准模块,Excel VBA 中还包括其他模块类型。本文不会重点展开这些模块,但在实际开发中需要了解它们的用途。
| 模块类型 | 常见用途 |
|---|---|
| 类模块(Class Module) | 用于封装对象、属性、方法和事件,适合较复杂的 VBA 逻辑 |
| 工作表模块(Worksheet Module) | 用于编写 Worksheet_Change、Worksheet_SelectionChange 等工作表事件。这类模块是 Excel 自带且无法删除的。 |
| 工作簿模块(Workbook Module) | 用于编写 Workbook_Open、Workbook_BeforeClose 等工作簿事件。这类模块是 Excel 自带且无法删除的。 |
| 用户窗体模块(UserForm Module) | 用于处理窗体和控件事件,例如按钮点击、文本框输入等 |
如果只是添加一个可手动运行的普通宏,通常优先使用标准模块。如果要让某段代码在工作表数据变化时自动触发,才需要考虑工作表模块。
二、Excel 宏文件格式说明
保存带 VBA 宏的 Excel 文件时,必须使用支持宏的文件格式。常见格式如下:
-
.xlsm:现代 Excel 宏启用工作簿,推荐使用 -
.xls:Excel 97-2003 工作簿,旧版格式 -
.xlsb:Excel 二进制工作簿,适合较大的文件 -
.xltm:Excel 宏启用模板
不要将包含 VBA 宏的文件保存为 .xlsx。.xlsx 格式不支持 VBA 宏代码,如果保存格式不正确,宏代码可能无法保留。
本文示例统一使用 .xlsm 格式保存文件。
三、安装所需库
要在 C# 中添加、读取、修改和删除 Excel VBA 宏,可以安装 Spire.XLS for .NET。该库支持在 .NET 程序中创建、读取和编辑 Excel 文件,不依赖 Microsoft Excel Interop,因此运行环境中无需安装 Microsoft Excel。
可以通过 NuGet 安装:
Install-Package Spire.XLS
VBA 宏工程操作支持于 Spire.XLS for .NET 16.3.6 及以上版本。如果你使用的是较早版本,请先升级到最新版本:
Update-Package Spire.XLS
除了通过 NuGet 安装,也可以手动添加 DLL 引用。下载并解压 Spire.XLS for .NET 安装包后,在 Visual Studio 中右键项目,选择 添加引用,然后选择与项目目标框架匹配的 DLL 文件。
四、使用 C# 在 Excel 中添加 VBA 宏
下面的示例演示如何创建一个 Excel 工作簿,并添加一个 VBA 标准模块及宏代码。
示例中的标准模块名为 ReportTools,其中包含一个 FormatSalesReport 宏。这个宏用于格式化当前工作表中的销售报表,例如设置标题行样式、格式化销售额列、自动调整列宽并生成销售额合计。
假设工作表中的数据结构如下:
| A 列 | B 列 | C 列 | D 列 |
|---|---|---|---|
| 日期 | 客户 | 产品 | 销售额 |
示例代码如下:
cs
using Spire.Xls;
using System;
namespace AddVbaMacro
{
internal class Program
{
static void Main(string[] args)
{
string outputFile = "添加宏.xlsm";
Workbook workbook = new Workbook();
// 设置工作表名称,便于用户打开文件后查看
Worksheet worksheet = workbook.Worksheets[0];
worksheet.Name = "SalesReport";
// 写入简单测试数据,便于打开文件后直接运行宏查看效果
worksheet.Range["A1"].Text = "日期";
worksheet.Range["B1"].Text = "客户";
worksheet.Range["C1"].Text = "产品";
worksheet.Range["D1"].Text = "销售额";
worksheet.Range["A2"].Text = "2026/06/01";
worksheet.Range["B2"].Text = "客户A";
worksheet.Range["C2"].Text = "产品A";
worksheet.Range["D2"].NumberValue = 1200;
worksheet.Range["A3"].Text = "2026/06/02";
worksheet.Range["B3"].Text = "客户B";
worksheet.Range["C3"].Text = "产品B";
worksheet.Range["D3"].NumberValue = 1850;
worksheet.Range["A4"].Text = "2026/06/03";
worksheet.Range["B4"].Text = "客户C";
worksheet.Range["C4"].Text = "产品C";
worksheet.Range["D4"].NumberValue = 960;
// 获取 VBA 工程
IVbaProject vbaProject = workbook.VbaProject;
vbaProject.Name = "SalesReportVbaProject";
// 设置代码页,避免中文注释或中文提示出现乱码
vbaProject.CodePage = 936;
// 添加标准模块
IVbaModule module = vbaProject.Modules.Add("ReportTools", VbaModuleType.Module);
// 写入普通宏过程
module.SourceCode = @"
Sub FormatSalesReport()
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ActiveSheet
' 根据 A 列获取最后一行
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox ""当前工作表没有可处理的数据。"", vbExclamation, ""提示""
Exit Sub
End If
' 设置标题行格式
With ws.Range(""A1:D1"")
.Font.Bold = True
.Interior.Color = RGB(220, 230, 241)
.HorizontalAlignment = xlCenter
End With
' 设置销售额列格式
ws.Range(""D2:D"" & lastRow).NumberFormat = ""#,##0.00""
' 自动调整列宽
ws.Columns(""A:D"").AutoFit
' 添加汇总信息
ws.Range(""F1"").Value = ""销售额合计""
ws.Range(""F2"").Formula = ""=SUM(D2:D"" & lastRow & "")""
ws.Range(""F1:F2"").Font.Bold = True
MsgBox ""销售报表格式化完成。"", vbInformation, ""完成""
End Sub";
// 保存为支持宏的 xlsm 格式
workbook.SaveToFile(outputFile, FileFormat.Xlsm);
workbook.Dispose();
Console.WriteLine("已创建包含 VBA 宏的 Excel 文件。");
}
}
}
运行代码后,会生成一个名为 添加宏.xlsm 的文件。打开该文件后,可以进入 VBA 编辑器查看 ReportTools 模块代码,也可以在 Excel 的"宏"窗口中直接运行 FormatSalesReport。

五、使用 C# 读取 Excel 中的 VBA 宏
读取宏时,比较常见的需求是导出 VBA 工程信息和标准模块代码,便于代码审查、版本归档或批量检查。
下面的示例会读取工作簿中的 VBA 工程信息,并导出所有标准模块的名称、类型和源代码。
cs
using Spire.Xls;
using System;
using System.IO;
namespace ReadVbaMacro
{
internal class Program
{
static void Main(string[] args)
{
string inputFile = "添加宏.xlsm";
string outputFile = "VbaModules.txt";
Workbook workbook = new Workbook();
workbook.LoadFromFile(inputFile);
IVbaProject vbaProject = workbook.VbaProject;
string text = string.Empty;
text += "VBA 工程信息" + Environment.NewLine;
text += "工程名称:" + vbaProject.Name + Environment.NewLine;
text += "工程描述:" + vbaProject.Description + Environment.NewLine;
text += "是否受保护:" + vbaProject.IsProtected + Environment.NewLine;
text += "代码页:" + vbaProject.CodePage + Environment.NewLine;
text += Environment.NewLine + "标准模块代码" + Environment.NewLine;
foreach (IVbaModule module in vbaProject.Modules)
{
if (module.Type == VbaModuleType.Module)
{
text += Environment.NewLine;
text += "模块名称:" + module.Name + Environment.NewLine;
text += "模块类型:" + module.Type + Environment.NewLine;
text += "源代码:" + Environment.NewLine;
text += module.SourceCode + Environment.NewLine;
}
}
File.WriteAllText(outputFile, text);
workbook.Dispose();
Console.WriteLine("VBA 标准模块代码已导出。");
}
}
}
读取结果:

这个示例读取的是标准模块,而不是工作表模块。对于普通宏代码,例如 Sub FormatSalesReport()、Sub ExportData() 这类过程,通常都可以通过遍历标准模块来读取。
如果需要读取工作表事件代码,例如 Worksheet_Change,才需要获取具体工作表对应的对象模块。参考代码:
cs
Worksheet ws = wb.Worksheets[0];
IVbaProject vbaProject = wb.VbaProject;
// 获取指定工作表关联的 VBA 模块
IVbaModule mod = vbaProject.Modules.GetWorksheetModule(ws);
// 获取模块代码等详细信息......
六、使用 C# 修改 Excel 中的 VBA 宏代码
修改宏时,比较常见的场景是:模板中已经存在某个标准模块,现在需要更新其中的宏逻辑。
下面的示例会打开已有的 .xlsm 文件,查找名为 ReportTools 的标准模块。如果该模块存在,就替换其中的宏代码;如果不存在,则新建一个同名标准模块。
更新后的模块中包含两个宏:
-
FormatSalesReport:格式化销售报表 -
CheckMissingSalesAmount:检查销售额列是否存在空值,并标记出来
cs
using Spire.Xls;
using System;
namespace ModifyVbaMacro
{
internal class Program
{
static void Main(string[] args)
{
string inputFile = "添加宏.xlsm";
string outputFile = "修改宏.xlsm";
string moduleName = "ReportTools";
Workbook workbook = new Workbook();
workbook.LoadFromFile(inputFile);
IVbaProject vbaProject = workbook.VbaProject;
// 更新 VBA 工程说明,便于后续维护
vbaProject.Description = "用于格式化和检查销售报表的宏";
// 查找标准模块
IVbaModule module = FindStandardModule(vbaProject, moduleName);
// 如果模块不存在,则新建一个标准模块
if (module == null)
{
module = vbaProject.Modules.Add(moduleName, VbaModuleType.Module);
}
// 替换模块中的宏代码
module.SourceCode = @"
Sub FormatSalesReport()
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ActiveSheet
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox ""当前工作表没有可处理的数据。"", vbExclamation, ""提示""
Exit Sub
End If
With ws.Range(""A1:D1"")
.Font.Bold = True
.Interior.Color = RGB(217, 225, 242)
.HorizontalAlignment = xlCenter
End With
ws.Range(""D2:D"" & lastRow).NumberFormat = ""#,##0.00""
ws.Columns(""A:D"").AutoFit
ws.Range(""F1"").Value = ""销售额合计""
ws.Range(""F2"").Formula = ""=SUM(D2:D"" & lastRow & "")""
ws.Range(""F1:F2"").Font.Bold = True
MsgBox ""销售报表格式化完成。"", vbInformation, ""完成""
End Sub
Sub CheckMissingSalesAmount()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Dim missingCount As Long
Set ws = ActiveSheet
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
MsgBox ""当前工作表没有可检查的数据。"", vbExclamation, ""提示""
Exit Sub
End If
For i = 2 To lastRow
If Len(Trim(CStr(ws.Cells(i, 4).Value))) = 0 Then
missingCount = missingCount + 1
ws.Cells(i, 4).Interior.Color = RGB(255, 230, 153)
End If
Next i
If missingCount > 0 Then
MsgBox ""发现 "" & missingCount & "" 行缺少销售额,已用颜色标记。"", vbExclamation, ""检查结果""
Else
MsgBox ""销售额数据检查完成,未发现缺失值。"", vbInformation, ""检查结果""
End If
End Sub";
workbook.SaveToFile(outputFile, FileFormat.Xlsm);
workbook.Dispose();
Console.WriteLine("VBA 标准模块已更新。");
}
private static IVbaModule FindStandardModule(IVbaProject vbaProject, string moduleName)
{
foreach (IVbaModule module in vbaProject.Modules)
{
if (module.Name.Equals(moduleName, StringComparison.OrdinalIgnoreCase)
&& module.Type == VbaModuleType.Module)
{
return module;
}
}
return null;
}
}
}
修改结果:

七、使用 C# 删除 Excel 中的 VBA 宏
删除宏时,建议按模块名称删除,而不是按索引删除。按索引删除虽然简单,但模块顺序可能会变化,容易误删其他模块。
下面的示例会检查是否存在名为 ReportTools 的标准模块。如果存在,则删除该模块;如果不存在,则保留文件并输出提示信息。
cs
using Spire.Xls;
using System;
namespace DeleteVbaMacro
{
internal class Program
{
static void Main(string[] args)
{
string inputFile = "添加宏.xlsm";
string outputFile = "删除宏.xlsm";
string moduleName = "ReportTools";
Workbook workbook = new Workbook();
workbook.LoadFromFile(inputFile);
IVbaProject vbaProject = workbook.VbaProject;
IVbaModule module = FindStandardModule(vbaProject, moduleName);
if (module != null)
{
vbaProject.Modules.Remove(module.Name);
Console.WriteLine("已删除标准模块:" + module.Name);
}
else
{
Console.WriteLine("未找到指定标准模块:" + moduleName);
}
workbook.SaveToFile(outputFile, FileFormat.Xlsm);
workbook.Dispose();
Console.WriteLine("文件已保存。");
}
private static IVbaModule FindStandardModule(IVbaProject vbaProject, string moduleName)
{
foreach (IVbaModule module in vbaProject.Modules)
{
if (module.Name.Equals(moduleName, StringComparison.OrdinalIgnoreCase)
&& module.Type == VbaModuleType.Module)
{
return module;
}
}
return null;
}
}
}
如果确实需要清除整个 VBA 工程中的模块,也可以使用 vbaProject.Modules.Clear()。不过在实际业务中应谨慎使用,因为这会删除工程中的所有模块代码。对于模板文件或历史报表文件,建议先备份,再执行批量删除操作。
八、C# 操作 Excel 宏的实用建议
1. 文件保存格式要正确
包含宏的文件应保存为 `.xlsm`、`.xls`、`.xlsb` 或 `.xltm`。如果保存为 `.xlsx`,宏代码不会被保留。
推荐 .xlsm:
cs
workbook.SaveToFile("output.xlsm", FileFormat.Xlsm);
如果确实需要保存为旧版 `.xls`,可以使用:
cs
workbook.SaveToFile("output.xls", FileFormat.Version97to2003);
2. 中文内容建议设置 CodePage
如果 VBA 代码中包含中文注释、中文字符串或中文提示信息,可以设置:
cs
vbaProject.CodePage = 936;
常见编码包括:
- 936:简体中文
- 950:繁体中文
- 932:日文
- 949:韩文
- 1252:西欧语言
3. 配置宏安全规则
C# 写入 VBA 宏代码后,并不表示 Excel 会自动运行宏。用户打开文件时,Excel 可能会根据宏安全设置阻止宏运行。
在企业环境中,如果宏文件来自不受信任位置,Excel 可能会提示启用内容。实际使用时,应根据企业安全策略配置受信任位置、数字签名或宏安全规则。
6. 操作宏代码不等于执行宏
本文示例主要演示的是 VBA 宏工程的创建、读取、修改和删除。也就是说,C# 代码是在处理宏内容,而不是执行宏逻辑。如果需要运行宏,通常需要在 Excel 中手动运行,或者在支持执行 Excel 宏的环境中处理。
九、总结
通过 C#,开发者可以在不依赖 Microsoft Excel Interop 的情况下,对 Excel 文件中的 VBA 宏进行自动化处理。本文介绍了添加、读取、修改和删除 VBA 宏代码的基本方法,并补充说明了 VBA 模块类型、宏启用文件格式以及实际开发中的注意事项。掌握这些内容后,就可以更灵活地在后台服务、批处理任务或自动化系统中管理 Excel 宏文件。