返回列表

帖子详情

经验分享

Y++实现Excel 对象类(COM 自动化)使用指南

作者:管理员发布:2026-08-11 01:51:53浏览:0
# 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 方法返回对象而使用 `调用` 得到空文本,应改成 `取对象`

评论