本人在软件开发过程中遇到了标题所指的问题,在两篇相关讨论(见参考资料【1】【2】)的帮助下,想到了一个不错的解决方法。
当我们在程序中使用Excel.Application对象打开xls文件时,通常会使用如下的代码:(以VB为例)
- Private
xlApp As Excel.Application ' 声明 Excel 程序对象 - Private
xlBook As Excel.Workbook ' 声明 Excel 工作簿对象 -
- '''''''''''''''''''''''''
- '
打开Excel 工作簿 - '''''''''''''''''''''''''
- Private
Sub OpenXls() -
Set xlApp = New Excel.Application ' 创建 Excel 程序对象 -
Set xlBook = xlApp.Workbooks.Open("A.xls") ' 打开 Excel 工作簿 -
xlApp.Visible = False ' 设置 Excel 程序不可见(即不出现在任务栏) - End
Sub -
- '''''''''''''''''''''''''
- '操作Excel
工作簿 - '''''''''''''''''''''''''
- Private
Sub OperateXls() -
xlBook.Worksheets(0).Cells(1, 1) = "示例文字" -
xlBook.Worksheets(0).Cells(1, 1).Font.Color = RGB(255, 0, 0) -
' 更多其他操作...... - End
Sub -
- '''''''''''''''''''''''''
- '关闭Excel
工作簿 - '''''''''''''''''''''''''
- Private
Sub CloseXls() -
xlBook.Close ' 关闭 Excel 工作簿 -
xlApp.Quit ' 退出 Excel 程序 -
Set xlApp = Nothing ' 释放 Excel 程序对象 - End
Sub
在以上程序运行期间,如果没有手动从外部打开xls文件,就不会出现任何问题。但当程序已经打开A.xls时,如果手动打开B.xls文件(跟A.xls没有任何关系),这时A.xls就会出现在任务栏(即使已经设置为不可见)。
这时,如果继续运行程序,调用CloseXls(),那么A.xls和B.xls都会被关掉;
出现上述问题的原因是,当程序新建一个Excel.Application对象时,实际上在进程中新建了一个系统已经安装的Excel程序的实例(此时进程中会有一个EXCEL.EXE),这其实与手动启动Excel程序(注意,是启动Excel程序,不是打开xls文件)是没有分别的。只不过在程序中可以将Excel 对象设置为不可见。
运行了一个EXCEL.EXE之后,就可以打开xls文件了。在程序中,可以使用xlApp.Workbooks.Open来指定使用哪一个Excel 对象来打开xls文件;而在手动打开xls文件时,系统总是自动查找现有的EXCEL.EXE进程中最后建立的那个,并用它来打开xls文件。如果当前还没有EXCEL.EXE进程运行,系统就会新建一个EXCEL.EXE进程,这就是我们一般双击打开xls文件时的情况。
这里需要提到EXCEL.EXE程序的一个特性:一个EXCEL.EXE可以打开多个xls文件。(不信你可以手动打开很多xls文件,然后看看任务管理器里是不是只有一个EXCEL.EXE进程。)有意思的是,当一个EXCEL.EXE打开多个xls文件时,这些xls文件会各自独立出现在任务栏,好像它们源自不同的进程似的,容易给人错觉。
知道了这个特性,就可以将问题分为以下两种情况:
(1)先手动打开B.xls文件(系统会自动新建一个EXCEL.EXE进程打开B.xls),再用程序打开A.xls(程序会强制新建一个EXCEL.EXE进程打开A.xls),这两个文件的关闭就不会互相影响(不同的EXCEL.EXE进程)。
(2)先用程序打开A.xls(程序会强制一个EXCEL.EXE进程打开A.xls),再手动打开B.xls(系统会自动使用刚才那个EXCEL.EXE进程打开B.xls),两个文件的关闭就会互相影响(同一EXCEL.EXE进程)
问题进一步明晰了,程序打开xls文件之前,手动打开xls文件,不会造成影响;在程序打开xls文件之后,手动打开的xls文件,就会有影响。下一步就是在情况(2)下解决问题了。
现在,我们已经完全了解了这个问题的原理,要彻底解决它,就要加上一点点小聪明了。
上文中有一句话是一个突破口,“在手动打开xls文件时,系统总是自动查找现有的EXCEL.EXE进程中最后建立的那个,并用它来打开xls文件。”
既然系统总是查找现有EXCEL.EXE进程中最后打开的那一个,也就是说只有最后打开的那个EXCEL.EXE进程才会受影响,那么我们能不能在程序中,建立了用来打开xls文件的EXCEL.EXE之后,专门再用程序新建一个EXCEL.EXE,用来屏蔽手动打开xls文件时对前一个EXCEL.EXE的干扰呢?当然能,实现它并不难。
- Private
xlApp As Excel.Application ' 声明用来打开xls文件的 Excel 程序对象 - Private
xlAppFake As Excel.Application ' 声明用来屏蔽外部干扰的 Excel 程序对象 - Private
xlBook As Excel.Workbook ' 声明 Excel 工作簿对象 -
- '''''''''''''''''''''''''
- '
打开Excel 工作簿 - '''''''''''''''''''''''''
- Private
Sub OpenXls() -
Set xlApp = New Excel.Application ' 创建用来打开xls文件的 Excel 程序对象 -
Set xlAppFake = New Excel.Application ' 创建用来屏蔽外部干扰的 Excel 程序对象 -
Set xlBook = xlApp.Workbooks.Open("A.xls") ' 打开 Excel 工作簿 -
xlApp.Visible = False ' 设置 Excel 对象不可见 - End
Sub -
- '''''''''''''''''''''''''
- '
操作Excel 工作簿 - '''''''''''''''''''''''''
- Private
Sub OperateXls() -
xlBook.Worksheets(0).Cells(1, 1) = "示例文字" -
xlBook.Worksheets(0).Cells(1, 1).Font.Color = RGB(255, 0, 0) -
' 更多其他操作...... - End
Sub -
- '''''''''''''''''''''''''
- '
关闭Excel 工作簿 - '''''''''''''''''''''''''
- Private
Sub CloseXls() -
xlBook.Close ' 关闭 Excel 工作簿 -
xlApp.Quit ' 退出 Excel 程序 -
Set xlApp = Nothing ' 释放 Excel 程序对象 -
If xlAppFake.Workbooks.Count = 0 Then -
xlAppFake.Quit ' 退出 Excel 程序 -
Set xlAppFake = Nothing ' 释放 Excel 程序对象 -
End If - End
Sub
程序打开A.xls之后,正在对其进行读写操作时,如果双击打开B.xls,系统就用使用xlAppFake所指的EXCEL.EXE打开B.xls,这样两个文件的关闭就互不影响了。
程序运行在最后,也要关闭和回收xlAppFake,否则进程中会留下一个EXCEL.EXE,而且没运行一次程序,进程中便会多一个EXCEL.EXE。
但是,如果直接将xlAppFake关闭的话,也是不妥的,因为外部某个xls文件是用xlAppFake所指的EXCEL.EXE打开的。
因此,需要判断xlAppFake此时是否还有文件打开,如果没有,再关闭。
参考资料:
【1】http://topic.youkuaiyun.com/u/20080506/18/1e995e5e-eb61-483f-ab64-9138403a5836.html?seed=1604491313
【2】http://topic.youkuaiyun.com/t/20040814/13/3274124.html
转自:http://blog.sina.com.cn/s/blog_80b4f5d10100v2h8.html