# Excel 对象类(COM 自动化)使用指南
对象类型:**对象类**。适用于 Windows 上通过 COM 自动化操作已安装的 Microsoft Excel。
> 前提:运行程序的电脑必须安装 Excel。`Excel.Application` 是 Excel 注册的 COM ProgID,不是文件路径。
---
## 1. 对象类与 Excel 对象树
常用对象关系:
```text
Excel.Application
└─ Workbooks
└─ Workbook
└─ Worksheets
└─ Worksheet
├─ Range / Cells
├─ Rows / Columns
├─ Font / Interior / Borders
├─ PageSetup
└─ ChartObjects
```
对象类的通用成员:
| Y++ 写法 | COM 含义 | 示例 |
|---|---|---|
| `创建(ProgID)` | 创建 COM 服务器 | `Excel.创建("Excel.Application")` |
| `连接(ProgID)` | 连接正在运行的 COM 服务器 | `Excel.连接("Excel.Application")` |
| `取对象(名称, 参数...)` | 读取对象属性,或调用返回对象的方法 | `表.取对象("Range", "A1:D10")` |
| `取属性(名称, 参数...)` | 读取普通值属性 | `表.取属性("Name")` |
| `设置属性(名称, 值)` | 设置属性 | `单元格.设置属性("Value", "内容")` |
| `调用(名称, 参数...)` | 调用方法 | `工作簿.调用("Save")` |
| `是否有效` | 是否持有有效 COM 对象 | `如果 (!工作表.是否有效)` |
| `错误信息` | 最近一次 COM 错误 | `输出(工作表.错误信息)` |
| `释放()` | 释放当前 COM 引用 | `区域.释放()` |
| `设置可见(逻辑)` | 设置 Excel `Visible` | `Excel.设置可见(真)` |
| `查看()` | 等价于 `设置可见(真)` | `Excel.查看()` |
| `设置显示警告(逻辑)` | 设置 `DisplayAlerts` | `Excel.设置显示警告(假)` |
| `退出()` | 调用 Excel `Quit` 并释放 | `Excel.退出()` |
参数目前推荐使用:**文本、整数、长整数、小数、逻辑**。当前不要把另一个 `对象类` 直接作为 COM 方法参数;可改用 `Range`、公式或目标对象自己的方法完成。
---
## 2. 最小示例:启动 Excel
```yc
函数 按钮3.单击(整数 X, 整数 Y) {
对象类 Excel
如果 (Excel.创建("Excel.Application")) {
Excel.查看()
Excel.设置显示警告(假)
} 否则 {
输出("启动 Excel 失败:", Excel.错误信息)
}
}
```
`查看()` 的含义是显示 Excel 主窗口,不是弹窗显示对象内容。
---
## 3. 创建工作簿并写入单元格
```yc
函数 新建Excel文件() {
对象类 Excel
对象类 工作簿集合
对象类 工作簿
对象类 工作表
对象类 单元格
如果 (!Excel.创建("Excel.Application")) {
输出(Excel.错误信息)
返回
}
Excel.设置可见(真)
Excel.设置显示警告(假)
工作簿集合 = Excel.取对象("Workbooks")
工作簿 = 工作簿集合.取对象("Add")
工作表 = Excel.取对象("ActiveSheet")
单元格 = 工作表.取对象("Cells", 1, 1)
单元格.设置属性("Value", "你好,Excel")
工作表.取对象("Cells", 2, 1).设置属性("Value", "数量")
工作表.取对象("Cells", 2, 2).设置属性("Value", 10)
工作簿.调用("SaveAs", "D:\\YPlusPlus_示例.xlsx", 51)
}
```
Excel 的行列序号从 **1** 开始:
```text
Cells(1, 1) = A1
Cells(2, 1) = A2
Cells(1, 2) = B1
```
`SaveAs` 的常用文件格式编号:
| 编号 | 格式 |
|---:|---|
| `51` | `.xlsx` |
| `52` | 含宏 `.xlsm` |
| `56` | 旧版 `.xls` |
| `6` | `.csv` |
---
## 4. 完整销售报表
```yc
函数 生成销售报表() {
对象类 Excel
对象类 工作簿
对象类 工作表
对象类 区域
对象类 字体
对象类 背景
如果 (!Excel.创建("Excel.Application")) {
输出("创建失败:", Excel.错误信息)
返回
}
Excel.设置可见(真)
Excel.设置显示警告(假)
工作簿 = Excel.取对象("Workbooks").取对象("Add")
工作表 = Excel.取对象("ActiveSheet")
工作表.设置属性("Name", "销售报表")
// 标题
区域 = 工作表.取对象("Range", "A1:D1")
区域.调用("Merge")
区域.设置属性("Value", "2026 年销售统计表")
区域.设置属性("HorizontalAlignment", -4108)
区域.设置属性("VerticalAlignment", -4108)
区域.设置属性("RowHeight", 34)
字体 = 区域.取对象("Font")
字体.设置属性("Name", "微软雅黑")
字体.设置属性("Size", 18)
字体.设置属性("Bold", 真)
字体.设置属性("Color", 16777215)
背景 = 区域.取对象("Interior")
背景.设置属性("Color", 10053120)
// 表头
工作表.取对象("Range", "A2").设置属性("Value", "商品")
工作表.取对象("Range", "B2").设置属性("Value", "单价")
工作表.取对象("Range", "C2").设置属性("Value", "数量")
工作表.取对象("Range", "D2").设置属性("Value", "销售额")
区域 = 工作表.取对象("Range", "A2:D2")
区域.设置属性("HorizontalAlignment", -4108)
区域.设置属性("RowHeight", 26)
区域.取对象("Font").设置属性("Bold", 真)
区域.取对象("Font").设置属性("Color", 16777215)
区域.取对象("Interior").设置属性("Color", 12611584)
// 数据
工作表.取对象("Cells", 3, 1).设置属性("Value", "键盘")
工作表.取对象("Cells", 3, 2).设置属性("Value", 199.5)
工作表.取对象("Cells", 3, 3).设置属性("Value", 12)
工作表.取对象("Cells", 3, 4).设置属性("Formula", "=B3*C3")
工作表.取对象("Cells", 4, 1).设置属性("Value", "鼠标")
工作表.取对象("Cells", 4, 2).设置属性("Value", 89.9)
工作表.取对象("Cells", 4, 3).设置属性("Value", 25)
工作表.取对象("Cells", 4, 4).设置属性("Formula", "=B4*C4")
工作表.取对象("Cells", 5, 1).设置属性("Value", "显示器")
工作表.取对象("Cells", 5, 2).设置属性("Value", 1299)
工作表.取对象("Cells", 5, 3).设置属性("Value", 6)
工作表.取对象("Cells", 5, 4).设置属性("Formula", "=B5*C5")
// 合计与格式
工作表.取对象("Range", "C6").设置属性("Value", "合计")
工作表.取对象("Range", "D6").设置属性("Formula", "=SUM(D3:D5)")
工作表.取对象("Range", "C6:D6").取对象("Font").设置属性("Bold", 真)
工作表.取对象("Range", "B3:B5").设置属性("NumberFormat", "¥#,##0.00")
工作表.取对象("Range", "D3:D6").设置属性("NumberFormat", "¥#,##0.00")
工作表.取对象("Columns", "A:A").设置属性("ColumnWidth", 18)
工作表.取对象("Columns", "B:D").设置属性("ColumnWidth", 14)
工作表.取对象("Range", "A1:D6").取对象("Font").设置属性("Name", "微软雅黑")
工作簿.调用("SaveAs", "D:\\销售统计表.xlsx", 51)
输出("销售报表生成完成")
}
```
---
## 5. 打开、读取、修改和关闭已有文件
```yc
函数 修改已有文件() {
对象类 Excel
对象类 工作簿
对象类 工作表集合
对象类 工作表
对象类 单元格
如果 (!Excel.创建("Excel.Application")) {
输出(Excel.错误信息)
返回
}
Excel.设置可见(真)
Excel.设置显示警告(假)
工作簿 = Excel.取对象("Workbooks").取对象(
"Open", "D:\\原始数据.xlsx"
)
如果 (!工作簿.是否有效) {
输出("打开失败:", 工作簿.错误信息)
Excel.退出()
返回
}
工作表集合 = 工作簿.取对象("Worksheets")
工作表 = 工作表集合.取对象("Item", 1)
输出("工作表:", 工作表.取属性("Name"))
单元格 = 工作表.取对象("Range", "A1")
输出("A1 原值:", 单元格.取属性("Value"))
单元格.设置属性("Value", "已由 Y++ 修改")
工作簿.调用("Save")
工作簿.调用("Close", 真)
Excel.退出()
}
```
连接用户已经打开的 Excel:
```yc
对象类 Excel
如果 (Excel.连接("Excel.Application")) {
输出("已经连接到运行中的 Excel")
} 否则 {
输出(Excel.错误信息)
}
```
---
## 6. 工作表管理
```yc
函数 工作表管理() {
对象类 Excel
对象类 工作簿
对象类 工作表集合
对象类 新表
对象类 指定表
Excel.创建("Excel.Application")
Excel.设置可见(真)
Excel.设置显示警告(假)
工作簿 = Excel.取对象("Workbooks").取对象("Add")
工作表集合 = 工作簿.取对象("Worksheets")
输出("工作表数量:", 工作表集合.取属性("Count"))
新表 = 工作表集合.取对象("Add")
新表.设置属性("Name", "产品资料")
新表.取对象("Range", "A1").设置属性("Value", "产品编号")
指定表 = 工作表集合.取对象("Item", "产品资料")
指定表.取对象("Range", "A2").设置属性("Value", "P-001")
// 删除表前关闭 Excel 确认提示
Excel.设置显示警告(假)
// 指定表.调用("Delete")
}
```
复制整个工作表最稳妥的方式是先打开模板文件,然后另存工作簿;当前版本不推荐使用需要把另一个 COM 对象作为 `Before/After` 参数的 `Worksheet.Copy` 写法。
---
## 7. Range、Cells、Rows 与 Columns
```yc
对象类 区域
区域 = 工作表.取对象("Range", "A1:D20")
区域.设置属性("HorizontalAlignment", -4108)
区域.设置属性("VerticalAlignment", -4108)
区域.设置属性("WrapText", 真)
工作表.取对象("Rows", "1:1").设置属性("RowHeight", 30)
工作表.取对象("Columns", "A:A").设置属性("ColumnWidth", 22)
工作表.取对象("Columns", "B:D").调用("AutoFit")
// 清空内容但保留格式
工作表.取对象("Range", "A2:D100").调用("ClearContents")
// 内容和格式全部清空
工作表.取对象("Range", "A2:D100").调用("Clear")
// 删除整行/整列
工作表.取对象("Rows", "5:5").调用("Delete")
工作表.取对象("Columns", "C:C").调用("Delete")
```
查找最后使用行的常见写法需要读取 `Cells(Rows.Count,1).End(-4162).Row`。可逐级取得对象:
```yc
对象类 所有行
对象类 最底单元格
对象类 最后单元格
所有行 = 工作表.取对象("Rows")
整数 最大行 = 到整数(所有行.取属性("Count"))
最底单元格 = 工作表.取对象("Cells", 最大行, 1)
最后单元格 = 最底单元格.取对象("End", -4162)
整数 最后一行 = 到整数(最后单元格.取属性("Row"))
输出("最后一行:", 最后一行)
```
`-4162` 是 Excel 常量 `xlUp`。
---
## 8. 字体、背景、对齐与数字格式
```yc
对象类 区域
对象类 字体
对象类 背景
区域 = 工作表.取对象("Range", "A1:D10")
字体 = 区域.取对象("Font")
字体.设置属性("Name", "微软雅黑")
字体.设置属性("Size", 12)
字体.设置属性("Bold", 真)
字体.设置属性("Italic", 假)
字体.设置属性("Underline", 2)
字体.设置属性("Color", 255)
背景 = 区域.取对象("Interior")
背景.设置属性("Color", 65535)
区域.设置属性("HorizontalAlignment", -4108)
区域.设置属性("VerticalAlignment", -4108)
区域.设置属性("WrapText", 真)
区域.设置属性("ShrinkToFit", 假)
```
常用数字格式:
```yc
工作表.取对象("Range", "B2:B100").设置属性("NumberFormat", "0")
工作表.取对象("Range", "C2:C100").设置属性("NumberFormat", "0.00")
工作表.取对象("Range", "D2:D100").设置属性("NumberFormat", "0.00%")
工作表.取对象("Range", "E2:E100").设置属性("NumberFormat", "¥#,##0.00")
工作表.取对象("Range", "F2:F100").设置属性("NumberFormat", "yyyy-mm-dd")
工作表.取对象("Range", "G2:G100").设置属性("NumberFormat", "yyyy-mm-dd hh:mm:ss")
工作表.取对象("Range", "H2:H100").设置属性("NumberFormat", "@")
```
`@` 表示文本格式,适合身份证号、长编号和前导零数据。
---
## 9. 边框
边框编号:`7=左`、`8=上`、`9=下`、`10=右`、`11=内部竖线`、`12=内部横线`。
```yc
函数 设置区域边框(对象类 区域) {
对象类 边框集合
对象类 边框
边框集合 = 区域.取对象("Borders")
边框 = 边框集合.取对象("Item", 7)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
边框.设置属性("Color", 0)
边框 = 边框集合.取对象("Item", 8)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
边框 = 边框集合.取对象("Item", 9)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
边框 = 边框集合.取对象("Item", 10)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
边框 = 边框集合.取对象("Item", 11)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
边框 = 边框集合.取对象("Item", 12)
边框.设置属性("LineStyle", 1)
边框.设置属性("Weight", 2)
}
```
调用:
```yc
对象类 表格区域
表格区域 = 工作表.取对象("Range", "A2:D20")
设置区域边框(表格区域)
```
---
## 10. 公式与计算
```yc
工作表.取对象("Range", "D2").设置属性("Formula", "=B2*C2")
工作表.取对象("Range", "D3").设置属性("FormulaR1C1", "=RC[-2]*RC[-1]")
工作表.取对象("Range", "D10").设置属性("Formula", "=SUM(D2:D9)")
工作表.取对象("Range", "E2").设置属性("Formula", "=IF(D2>=1000,\"达标\",\"未达标\")")
工作表.取对象("Range", "F2").设置属性("Formula", "=TODAY()")
// 强制计算
Excel.调用("Calculate")
```
不同语言版本的 Excel 对 `FormulaLocal` 可能使用本地函数名;为了跨机器,优先使用英文函数名和 `Formula`。
---
## 11. 查找与替换
`Range.Find` 返回对象,因此使用 `取对象`:
```yc
对象类 搜索区域
对象类 找到的单元格
搜索区域 = 工作表.取对象("Range", "A1:D1000")
找到的单元格 = 搜索区域.取对象("Find", "鼠标")
如果 (找到的单元格.是否有效) {
输出("找到位置:", 找到的单元格.取属性("Address"))
找到的单元格.设置属性("Value", "无线鼠标")
} 否则 {
输出("没有找到:", 搜索区域.错误信息)
}
```
替换:
```yc
// LookAt:1=完全匹配,2=部分匹配
工作表.取对象("Range", "A1:D1000").调用(
"Replace", "旧文本", "新文本", 2
)
```
---
## 12. 筛选、排序与冻结窗格
筛选:
```yc
对象类 数据区域
数据区域 = 工作表.取对象("Range", "A1:D100")
// 启用筛选
数据区域.调用("AutoFilter")
// 第二列大于 100
数据区域.调用("AutoFilter", 2, ">100")
// 清除筛选箭头
工作表.设置属性("AutoFilterMode", 假)
```
简单排序可对目标列调用 `Sort`:
```yc
// 1=升序,2=降序
工作表.取对象("Range", "A2:D100").调用("Sort", 1)
```
Excel 的复杂排序 API 常要求把 Range 对象作为参数;当前建议在工作表中建立辅助列,再用简单排序,或通过公式完成。
冻结首行:
```yc
对象类 窗口
工作表.取对象("Range", "A2").调用("Select")
窗口 = Excel.取对象("ActiveWindow")
窗口.设置属性("FreezePanes", 真)
```
取消冻结:
```yc
Excel.取对象("ActiveWindow").设置属性("FreezePanes", 假)
```
---
## 13. 数据验证、超链接和批注
数据验证对象:
```yc
对象类 验证
验证 = 工作表.取对象("Range", "B2:B100").取对象("Validation")
验证.调用("Delete")
// 3=序列验证,1=停止样式,1=介于;最后一个参数是选项列表
验证.调用("Add", 3, 1, 1, "待处理,处理中,已完成")
验证.设置属性("IgnoreBlank", 真)
验证.设置属性("InCellDropdown", 真)
```
超链接建议使用公式,避免传入 Range 对象参数:
```yc
工作表.取对象("Range", "A10").设置属性(
"Formula",
"=HYPERLINK(\"https://www.example.com\",\"打开网站\")"
)
```
添加批注:
```yc
对象类 批注
批注 = 工作表.取对象("Range", "A2").取对象(
"AddComment", "该数据由 Y++ 自动生成"
)
```
删除批注:
```yc
对象类 批注
批注 = 工作表.取对象("Range", "A2").取对象("Comment")
如果 (批注.是否有效) {
批注.调用("Delete")
}
```
---
## 14. 图表
下面使用 `Shapes.AddChart2`,参数全部是普通数值,不需要传递 COM 对象:
```yc
函数 添加销售图表(对象类 工作表) {
对象类 图形集合
对象类 图表形状
对象类 图表
对象类 数据系列集合
对象类 数据系列
图形集合 = 工作表.取对象("Shapes")
// 201=图表样式,51=簇状柱形图;后面是 Left、Top、Width、Height
图表形状 = 图形集合.取对象("AddChart2", 201, 51, 420, 30, 520, 300)
图表 = 图表形状.取对象("Chart")
// 新建数据系列并用公式地址指定数据源,避免把 Range 对象作为参数
数据系列集合 = 图表.取对象("SeriesCollection")
数据系列 = 数据系列集合.取对象("NewSeries")
数据系列.设置属性("Name", "='销售报表'!$D$2")
数据系列.设置属性("XValues", "='销售报表'!$A$3:$A$5")
数据系列.设置属性("Values", "='销售报表'!$D$3:$D$5")
图表.设置属性("HasTitle", 真)
图表.取对象("ChartTitle").设置属性("Text", "商品销售额")
图表.设置属性("HasLegend", 真)
}
```
不同 Excel 版本对 `AddChart2` 和图表数据源接口存在差异。如果 `SetSourceData` 报错,可先在模板文件中做好图表,再只修改图表引用的数据区域。模板方式在生产环境最稳定。
常见图表类型:
| 编号 | 类型 |
|---:|---|
| `51` | 簇状柱形图 |
| `4` | 折线图 |
| `5` | 饼图 |
| `65` | 面积图 |
| `74` | 散点图 |
---
## 15. 页面设置、打印和 PDF
```yc
对象类 页面设置
页面设置 = 工作表.取对象("PageSetup")
// 1=纵向,2=横向
页面设置.设置属性("Orientation", 2)
页面设置.设置属性("Zoom", 假)
页面设置.设置属性("FitToPagesWide", 1)
页面设置.设置属性("FitToPagesTall", 1)
页面设置.设置属性("CenterHeader", "销售统计报表")
页面设置.设置属性("CenterFooter", "第 &P 页,共 &N 页")
页面设置.设置属性("PrintArea", "$A$1:$D$20")
// 打印预览
工作表.调用("PrintPreview")
// 直接打印
// 工作表.调用("PrintOut")
// 0=PDF,1=XPS
工作表.调用(
"ExportAsFixedFormat",
0,
"D:\\销售统计表.pdf"
)
```
Excel 页边距通常使用“磅”而不是厘米。可让 Excel 换算:
```yc
小数 左边距 = 到小数(Excel.调用("CentimetersToPoints", 1.5))
页面设置.设置属性("LeftMargin", 左边距)
页面设置.设置属性("RightMargin", 左边距)
```
---
## 16. 保护工作表和工作簿
```yc
// 锁定区域
工作表.取对象("Range", "A1:D20").设置属性("Locked", 真)
// 允许编辑输入区
工作表.取对象("Range", "B2:C20").设置属性("Locked", 假)
// 保护/取消保护工作表
工作表.调用("Protect", "123456")
工作表.调用("Unprotect", "123456")
// 保护工作簿结构
工作簿.调用("Protect", "123456", 真)
工作簿.调用("Unprotect", "123456")
```
密码保护不能替代文件加密;敏感文件还应使用 Excel 的“用密码进行加密”。
---
## 17. CSV 导入导出
另存为 CSV:
```yc
Excel.设置显示警告(假)
工作簿.调用("SaveAs", "D:\\数据.csv", 6)
```
打开 CSV:
```yc
对象类 工作簿
工作簿 = Excel.取对象("Workbooks").取对象("Open", "D:\\数据.csv")
```
CSV 只保存当前工作表,不支持格式、公式结果之外的工作簿功能。中文乱码时,优先使用 `.xlsx`,或者在 Excel 中选择 UTF-8 CSV 格式。
---
## 18. 错误处理模板
不要只判断最外层 `Excel.创建`。关键对象和关键写入都应检查:
```yc
函数 安全写入单元格(对象类 工作表, 文本 地址, 文本 内容) {
对象类 单元格
单元格 = 工作表.取对象("Range", 地址)
如果 (!单元格.是否有效) {
输出("取得单元格失败:", 地址, ",", 单元格.错误信息)
返回
}
如果 (!单元格.设置属性("Value", 内容)) {
输出("写入失败:", 地址, ",", 单元格.错误信息)
}
}
```
完整清理模板:
```yc
函数 Excel任务() {
对象类 Excel
对象类 工作簿
如果 (!Excel.创建("Excel.Application")) {
输出(Excel.错误信息)
返回
}
Excel.设置显示警告(假)
工作簿 = Excel.取对象("Workbooks").取对象("Add")
如果 (!工作簿.是否有效) {
输出(工作簿.错误信息)
Excel.退出()
返回
}
// ...执行业务...
工作簿.调用("Close", 假)
工作簿.释放()
Excel.退出()
}
```
---
## 19. 性能建议
大量写入时先关闭屏幕刷新、事件和自动计算:
```yc
Excel.设置属性("ScreenUpdating", 假)
Excel.设置属性("EnableEvents", 假)
// -4135 = xlCalculationManual
Excel.设置属性("Calculation", -4135)
// 批量写入...
// -4105 = xlCalculationAutomatic
Excel.设置属性("Calculation", -4105)
Excel.设置属性("EnableEvents", 真)
Excel.设置属性("ScreenUpdating", 真)
Excel.调用("Calculate")
```
建议:
1. 尽量使用一个连续 `Range`,少做成千上万次单元格调用。
2. 生产报表优先使用预制 `.xlsx` 模板,只填数据和少量格式。
3. 操作过程中关闭 `ScreenUpdating`,结束时务必恢复。
4. 不需要用户看到 Excel 时,不要调用 `设置可见(真)`。
5. 后台任务结束时关闭工作簿并调用 `Excel.退出()`,避免残留 EXCEL.EXE。
---
## 20. 常用 Excel 常量
Y++ 可直接传对应整数:
| 名称 | 值 | 说明 |
|---|---:|---|
| `xlCenter` | `-4108` | 居中 |
| `xlLeft` | `-4131` | 左对齐 |
| `xlRight` | `-4152` | 右对齐 |
| `xlTop` | `-4160` | 顶部对齐 |
| `xlBottom` | `-4107` | 底部对齐 |
| `xlUp` | `-4162` | 向上查找/移动 |
| `xlDown` | `-4121` | 向下查找/移动 |
| `xlToLeft` | `-4159` | 向左 |
| `xlToRight` | `-4161` | 向右 |
| `xlAscending` | `1` | 升序 |
| `xlDescending` | `2` | 降序 |
| `xlPortrait` | `1` | 纵向打印 |
| `xlLandscape` | `2` | 横向打印 |
| `xlCalculationManual` | `-4135` | 手动计算 |
| `xlCalculationAutomatic` | `-4105` | 自动计算 |
| `xlOpenXMLWorkbook` | `51` | xlsx |
| `xlOpenXMLWorkbookMacroEnabled` | `52` | xlsm |
| `xlExcel8` | `56` | xls |
| `xlCSV` | `6` | CSV |
| `xlTypePDF` | `0` | PDF |
---
## 21. 常见问题
### 调用任意第三方 COM 插件/ActiveX 服务
`对象类` 不只用于 Excel。只要第三方组件公开了 Automation/IDispatch 接口并已在 Windows 注册,就可以通过它的 ProgID 调用。
假设第三方文档给出的 ProgID 为 `Vendor.Device`,对象树为 `Device → Session → Result`:
```yc
对象类 插件
对象类 会话
对象类 结果
如果 (!插件.创建("Vendor.Device")) {
输出("创建第三方 COM 失败:" + 插件.错误信息)
返回
}
插件.设置属性("Visible", 假)
插件.设置属性("Timeout", 5000)
会话 = 插件.取对象("OpenSession", "COM3", 115200)
如果 (!会话.是否有效) {
输出(会话.错误信息)
插件.释放()
返回
}
会话.调用("WriteText", "STATUS?")
结果 = 会话.取对象("ReadResult")
如果 (结果.是否有效) {
整数 状态码 = 结果.取属性("Code")
文本 内容 = 结果.取属性("Text")
输出(状态码, 内容)
}
会话.调用("Close")
结果.释放()
会话.释放()
插件.释放()
```
以上成员名只是第三方 COM 文档的示意;实际必须使用供应商类型库/接口手册中的 ProgID、属性和方法名。
连接已运行实例:
```yc
对象类 插件
如果 (!插件.连接("Vendor.Device")) {
输出("没有运行中的实例:" + 插件.错误信息)
}
```
接入检查清单:
1. 使用供应商安装程序或 `regsvr32` 正确注册 COM 组件;不要只复制 DLL。
2. Y++ 程序位数必须与 COM In-Proc DLL 位数一致;x86 DLL 用 x86 目标,x64 DLL 用 x64 目标。EXE 型 COM 服务器通常可跨位数。
3. ProgID 可在供应商文档、OLE/COM Object Viewer 或注册表 `HKEY_CLASSES_ROOT` 查询。
4. `创建` 对应 `CoCreateInstance`;`连接` 对应取得已运行对象。
5. 返回子对象的方法用 `取对象`,普通返回值用 `取属性`,无返回动作使用 `调用`。
6. 如果第三方 DLL 只导出 C 函数、没有 IDispatch/类型库,它不是可供 `对象类` 使用的 COM Automation 组件,应改用 `.ypi` 声明 DLL API。
7. ActiveX 控件若要求嵌入窗口、事件接收或许可证,除了 `对象类` 调用外还可能需要专用宿主;以厂商要求为准。
### `创建("Excel.Application")` 失败
- 检查电脑是否安装 Excel。
- 检查程序和 Office 的安装是否正常;COM 注册损坏时修复 Office。
- 输出 `Excel.错误信息`,其中包含 HRESULT 和失败成员。
### 弹出“COM object”而不是 Excel
这是旧运行库把 `查看()` 写成提示框的行为。新版 `查看()` 等价于 `设置可见(真)`。完全退出旧版 YStudio 后,用新版重新编译项目。
### Excel 进程一直不退出
- 关闭工作簿:`工作簿.调用("Close", 假)`。
- 释放区域、工作表、工作簿等对象引用。
- 最后调用 `Excel.退出()`。
- 避免仍有全局 `对象类` 保存着某个 Range/Worksheet。
### 调用成功但文件没有保存
- `Save` 需要工作簿已经有路径。
- 新工作簿使用 `SaveAs` 并传绝对路径。
- Windows 路径中的反斜杠写成 `\\`,例如 `"D:\\报表.xlsx"`。
### 方法到底用 `调用` 还是 `取对象`
- 返回 Excel 对象:用 `取对象`,如 `Workbooks`、`Add`、`Range`、`Font`。
- 只执行动作:用 `调用`,如 `Save`、`Close`、`Delete`、`AutoFit`。
- 返回文本/数字/逻辑属性:用 `取属性`。
- 写属性:用 `设置属性`。
如果某个 Excel 方法返回对象而使用 `调用` 得到空文本,应改成 `取对象`。
评论