65.9K
CodeProject 正在变化。 阅读更多。
Home

通过自动化在 Excel 中放置图像

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (13投票s)

2004年9月28日

8分钟阅读

viewsIcon

233013

downloadIcon

8299

如何以编程方式在 Excel 中放置图片

引言

插入文本和公式到 Excel 电子表格的示例非常丰富(参见参考文献)。所以当我面临将图片插入电子表格的任务时,我想一个合适的示例不会难找。哎呀,我错了。这可能是因为我对于搜索引擎来说是个新手。或者,也许对其他人来说,这很容易做到。但是去年一位 CP 朋友发表的一个评论给了我一些信心,认为本文可能还有一些价值。我花了痛苦的两天时间才弄清楚这一切,我希望其他读者也能避免同样的挫折。

本文演示了如何使用 Excel 自动化 API 将一组图片插入 Excel 电子表格。演示应用程序允许用户选择一个 Excel 工作簿、一组图像文件以及要插入图片的其中一个工作表。只需单击一下,图片就会在工作表中垂直堆叠,每张图片都带有一个一像素的完成边框。然后,用户可以选择保存工作簿或关闭它。

注意:该项目是用 Visual Studio .NET 2002 编写并在 WinXP 上测试的。它应该适用于 MS Office 2000 和 XP 版本。使用不同版本的 Visual Studio 和 MS Office 是留给读者的练习。

使用 Excel 自动化 API

在我们开始之前,请允许我说明一件事:我在自动化、COM 及其他此类棘手话题方面是个新手。我所知道的是,自动化是一个花哨的词,代表一个相当简单的概念。借用另一位 CP 朋友提供的一份简单解释的说法,自动化是一个编程接口——更具体地说,是应用程序公开的一个 COM 接口——允许另一个应用程序与之交互。如果您在阅读本节后有兴趣深入探讨自动化或 COM,请从这里开始。

Excel 方便地提供了一个自动化接口,让您可以控制其众多功能。在本例的上下文中,Excel 是自动化服务器,而我的程序是自动化客户端。Excel 的自动化接口封装在随 Excel 分发的类型库(也称为对象库)中。类型库包含类型定义,例如类名,它们描述了自动化/COM 接口。(可以将其想象成一个花哨的头文件。)根据您使用的 Excel 版本,有不同版本的类型库。我拥有并使用 Excel 2000,它使用 9.0 版的类型库(Excel XP 也一样)。早期版本的 Excel 使用 8.0 版的类型库。老实说,我不知道这些版本之间是否存在向后兼容性。但请不要担心:对于许多基本功能,类型库并没有实质性差异。有很多示例(其中一些在参考文献部分有提及)可以帮助您修改我的代码。如果我拥有早期版本的 Excel,我会自己去做。

导入类型库

有了类型库,您可以通过两种方式使用它,将库的内容转换为一组 C++ 类。第一种方法是使用 #import 指令,它看起来像……

#import "C:\Program Files\Microsoft Office\Office\EXCEL9.OLB" \
  rename("DialogBox","_DialogBox") \
  rename("RGB","_RGB") \
  exclude("IFont","IPicture")

这是一种完全可行的方法。然而,令我烦恼的是,在编译时您必须知道类型库的位置。如果我在 Excel 安装在其他地方的机器上构建此代码,那么我就必须去寻找类型库。虽然这可能只需要一个不同的驱动器号,但我很懒惰,当我编写的代码失败时我会感到沮丧。

第二种方法是使用 Visual Studio 中的“添加类”向导。我不会在这里详细介绍,因为 .NET 用户可以在一篇 MSDN 文章中找到步骤,而 Visual Studio 6.0 用户可以在另一篇 MSDN 文章中找到步骤。简而言之,“添加类”向导允许您选择一个类型库(在 .NET 中是预编译列表;在 6.0 中,您仍然必须手动查找库,但只需一次,即在项目首次创建时)。Visual Studio 将愉快地解析该库并生成一组包含类定义的头文件,这些文件可用于创建您的 Excel 自动化客户端。文件生成后,就不需要再次生成它们了。(当然,除非您开始使用不同版本的类型库。)这些文件在我的示例项目中出现在“Spreadsheet”子目录下。看看这些类文件。它们不会透露太多信息,因为它们实际上只是一个巨大的函数查找表。但它们仍然值得一看。

顺便说一句,我想更清楚地说明我的偏见。虽然我有我喜欢的,但我并不想贬低从类型库中提取类定义的第一种方法。事实上,我第一次了解到自动化 Excel 是从一篇CP 文章中学到的,它使用了这种方法。我提到这两种方法是因为我想让人们意识到他们有选择。我自己的选择主要基于我的个人审美,而您可能不认同。

通过包含生成的头文件即可使用类定义。Excel API 中可能有很多类,就像应用程序中的功能一样。(嗯,不完全是,因为单个类可以封装一组通用功能。但确实很多类。)以下是我在此示例中使用的类的简要摘要。

CApplication

提供对活动 Excel 进程的句柄。允许您控制 Excel 是否可见,用户是否可以直接控制它等。

CWorkbooks 此类表示可用于处理一组工作簿的函数。更具体地说,它用于打开单个工作簿。
CWorkbook 已打开工作簿的句柄。
CWorksheets 顶级工作表管理器,类似于 CWorkbooks,但用于工作表。
CWorksheet 特定工作表对象的句柄。
CRange 工作表中单元格范围的句柄。
CPictures 顶级图片管理器。
CPicture 特定图片对象的句柄。
CBorder 边框对象的句柄。

关于演示程序

我将简要总结演示程序的流程。与其在文章中粘贴大段代码,不如直接指出工作所在的位置。有兴趣的读者可以查看注释过的源代码,了解幕后细节。

初始化 COM 库

构建自动化客户端的第一步是初始化 COM 库。必须通过调用 CoInitialize() 显式加载 COM 库,并在之后通过调用 CoUnitialize() 卸载。一个明智的放置位置是在应用程序的 InitInstance() 函数中。

BOOL CExcelImagesApp::InitInstance()
{
  ...
  
  CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);

  // main dialog goes modal

  CoUnitialize ();
  
  ...
}

启动 Excel(自动化服务器)进程

我决定在主对话框的 OnInitDialog() 函数中启动 Excel 进程。您也可以在初始化 COM 库后立即启动它。但由于应用程序的整个生命周期都不需要它,所以没有必要让进程闲置,寻找麻烦。

BOOL CExcelImagesDlg::OnInitDialog();

打开工作簿

从主对话框,用户可以选择要放置图片的电子表格。(我已在演示项目中包含了一个空白电子表格和一组图片,供那些希望立即获得满足感的人使用。)选择后,将通过调用 OpenWorkbook()(从 OnPostBrowse() 函数)打开工作簿。

void CExcelImagesDlg::OnPostBrowse(NMHDR *pNMHDR, LRESULT *pResult);
void CExcelImagesDlg::OpenWorkbook (CString szWorkbook);

如果工作簿打开成功,将检索单个工作表的名称,并用于填充对话框的组合框。然后,用户可以选择其中一个工作表来放置图片。

放置图片

用户浏览选择一系列图片(任何类型都可以),然后按“放置图片”按钮。这会通过调用 InsertPictures()(通过 OnBnClickedPlaceImages())来调用。InsertPictures 通过计算每张图片在行单位的高度来在工作表中垂直堆叠图片。每张图片都带有一个一像素的边框进行修饰。

void CExcelImagesDlg::OnBnClickedPlaceimages();
void CExcelImagesDlg::InsertPictures ();

关闭工作簿

当选择新工作簿或用户关闭对话框时,将关闭已打开的工作簿。这由 CloseWorkbook() 函数处理。

void CExcelImagesDlg::CloseWorkbook();

关闭 Excel

当用户关闭对话框时,Excel 进程最终关闭。这发生在对话框的 OnDestroy() 函数中。

void CExcelImagesDlg::OnDestroy();

参考文献

致谢

感谢 PJ Arends 的文件编辑控件。当您浏览工作簿和图像文件时,您会看到它的作用。

其他关于与 MS Office 交互的有趣 CP 文章

历史

  • 版本 1.0 (2004 年 9 月 22 日):它活了……它活了!
© . All rights reserved.