|
|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
×
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。" k4 Y4 K7 ]: G6 g
8 A% `9 ~5 Z) O
9 [6 w$ @- _0 _* U/ v! V& c& O& W y1、添加OLE/COM支持。 首先,应用程序必须添加对OLE/COM的支持,才能导入OLE/COM组件。 本文使用的是MFC对话框程序,在创建工程的向导中选中Automation选项即可为程序自动添加相应的头文件和OLE库初始化代码。 通过查看源代码,可以知道在stdafx.h的头文件中,添加了OLE/COM很多类所需添加的头文件。 #include <afxdisp.h> // MFC 自动化类 同时,在应用程序类的InitInstance函数中,添加了OLE/COM的初始化代码,如下所示: // 初始化 OLE 库 if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; }
- _2 W: u/ i7 z: [: I 2、导入并封装Excel中的接口 Excel作为OLE/COM库插件,定义好了各类交互的接口,这些接口是跨语言的接口。VC可以通过导入这些接口,并通过接口来对Excel的操作。 由于本文只关心对Excel表格中的数据的读取,主要关注几个_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range等几个接口。Excel的各类接口的属性、方法可以通过MSDN的Office Development进行查询。 VS2010导入OLE/COM组件的接口的步骤为:Project->Class Wizard->Add Class->MFC Class From TypeLib,先选择要导入的组件所在的路径,即Excel.exe所在的路径,然后再选择 要导入的Excel类型库中的接口。 在完成接口导入后,VS2010将自动为导入的接口创建相应的实现类,用于对接口属性和方法的实现。由于标准的C++没有属性访问器,只能添加一个两个存取函数来实现对属性的访问,通过在属性名称前加上get_和put_前缀分别实现对属性的读写操作。即,由VC自动完成C++类对接口的封装。 ; K, ]( [* X) @% K/ l3 m1 }( w
本文所导入的接口对应的类和头文件的说明如下所示:
2 m% s( W; Z* o% zExcel接口 | 导入类 | 头文件 | 说明 | _Application | CApplicaton | Application.h | Excel应用程序。 | Workbooks | CWorkbooks | Workbooks.h | 工作簿的容器,里面包括了Excel应用程序打开的所有工作簿。 | _Workbook | CWorkbook | Workbook.h | 单个工作簿。 | Worksheets | CWorksheets | Worksheets.h | 单个工作簿中的Sheet表格的容器,包括该工作簿中的所有Sheet。 | _Worksheet | CWorksheet | Worksheet.h | 单个Sheet表格。 | Range | CRange | Range.h | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 | % S- b, T4 U( ?' V$ s" q( I
& t0 W$ h4 d3 ]3、导入Excel的整个类型库 接口对应类只是对接口的属性和方法进行了封装,而Excel中的数据类型,如枚举类型却并为并不能使用,因此,为了更方便的操作Excel,还需要导入Excel的数据类型。 通过查看导入接口对应的头文件可以发现,在所有导入接口的头文件中,都会有这么行: #import "D: \\Program Files\\Microsoft Office\\Office12\\EXCEL.EXE" no_namespace 这行代码的作用是导入Excel整个类型库到工程中。 由VS2010自动产生的导入代码存在以下几个问题: (1)如果导入了多个接口,每个头文件都会把类型库导入一次,如果引用多个头文件,会导致类型库重复导入。 (2)Excel类型库中有些类型会跟MFC类库的某些类型冲突。 (3)Excel类型库的某些类型跟其他Office和VB的某些库相关,如果不导入相关库,将导致这些类型无法使用。。 以上三点问题的解决方法如下: (1)仅在_Application接口对应头文件中导入Excel类型库。 (2)对冲突的类型进行重命名。 (3)在导入Excel类型库之前,先导入Office和VB的相关库。 更改后的导入类型库的代码如下: 4 q9 F) Q- Q' X/ |, _4 V
/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office; * R& h6 H) ^, \3 ?
/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE; " n$ Y, H; E, ?/ F% \5 K+ B
/*导入Excel的类型库*/ #import "D: \\Program Files\\Microsoft Office\\Office12\\EXCEL.EXE" \ rename("DialogBox", "ExcelDialogBox") \ rename("RGB", "ExcelRGB") \ rename("CopyFile", "ExcelCopyFile") \ rename("ReplaceText", "ExcelReplaceText") \ no_auto_exclude Using namespace Excel; - H6 n7 _. Q! L4 {
编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。
' G* S9 o0 l2 D8 ?& a8 K4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。 \. P* Q& W9 a" J1 A
5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。 / f( H. ^3 S6 c
6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。
0 \' @; V$ D. D4 Z) N1 M7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。 & q& ^; f3 D* Q! V. B, x1 R
m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);+ Y6 Q! m+ f4 [
8 [* D: U' B# l+ P$ X+ D
CApplication ExcelApp;
1 s1 z! V" u0 }: |6 M9 _1 ~1 f CWorkbooks books;
' \5 i+ c2 P" n; a CWorkbook book; j( s* W3 j: e
CWorksheets sheets;/ X+ F5 H2 U: v1 d: s' h$ J
CWorksheet sheet;
+ D/ U8 W( _, A E8 ], u9 p CRange range;) p( t& e7 @( O# u- S* D9 w5 Z- S
LPDISPATCH lpDisp = NULL;
, q f( [: D) O M
E6 f) ?9 \2 U2 f! u+ S$ c( m% R; F //创建Excel 服务器(启动Excel)& l9 M1 m2 x! m. ^- ~5 }" \
if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))
* I. c# x1 i9 {) u' u {5 K1 G) E' o! b
AfxMessageBox(_T("启动Excel服务器失败!"));
1 F4 b9 o! B" {* e# t4 j; k& W( N return -1;
! U( F# A, i0 H1 m }- N- @7 h2 ? y4 ^
9 H" ]0 w* z2 F' w8 I$ F; ]
/*判断当前Excel的版本*/4 t* Y: j% K2 M, p$ e% Z5 F
CString strExcelVersion = ExcelApp.get_Version();
- p+ {! ^& W( A1 r2 V9 ]# Y0 D& M( V int iStart = 0;# [3 _ |$ u: E* h
strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);
W1 {7 s7 H) S" v if (_T("11") == strExcelVersion)* I/ U* k% b2 \) T* a4 Z& ~
{; |& v @7 B, H4 M: F) h
AfxMessageBox(_T("当前Excel的版本是2003。"));" z1 \: R7 L& H: o1 I
} V- X% t7 h& {5 a
else if (_T("12") == strExcelVersion)+ U; z% A2 h0 k* T
{, @+ o4 x- {% u$ U- Q! u% T
AfxMessageBox(_T("当前Excel的版本是2007。"));
; |1 |1 ]6 T+ d, ~( T# k }1 @7 O3 A, Z% x0 |% @6 v
else
1 ~& D% T) x: S( R* H {
- Q( m+ R5 l$ T. Q AfxMessageBox(_T("当前Excel的版本是其他版本。"));
; d: M3 n- k/ |1 T! j, ^ }
( Y/ F# E5 @! A) e& s4 L
" e" \% X' P7 Z! j' u2 S ExcelApp.put_Visible(TRUE);/ k6 ]; |( L( W5 V/ `/ C$ a) J
ExcelApp.put_UserControl(FALSE);: e1 [+ {- Q( A j
0 K8 M% ^9 N2 E# g' I- ?
/*得到工作簿容器*/+ e. P! x) { }, n t/ y, `3 ~% c
books.AttachDispatch(ExcelApp.get_Workbooks());& C( A* U e% g& a4 S& t
& C2 I/ V. d; G; m+ d /*打开一个工作簿,如不存在,则新增一个工作簿*/3 c3 G# N9 k& e- q8 W. p/ v; X
CString strBookPath = _T("C:\\tmp.xls");
; d$ i# M% w* O1 [ try) P( F8 [7 F8 a7 V1 q
{
* o4 [5 e( N Q9 ^, q% ^ /*打开一个工作簿*/
0 Y0 n# p4 |; W" }& z lpDisp = books.Open(strBookPath, , m, M- D! b% ^- j/ `( \
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,- x% z) D* J9 L+ O. T& p8 A; Y
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
8 A/ U# ?6 D& u# q& `( m% ] vtMissing, vtMissing, vtMissing, vtMissing);: E F# S& q' C8 s* ~9 g- B8 p/ f7 ~
book.AttachDispatch(lpDisp);4 n1 @0 g4 ]# I6 u1 @( \1 O6 o* n& o
}
, ]: i @) U- O# P catch(...)* \; X0 M* F0 ^' C
{
0 W, {9 R2 o' o' v5 L# K: E. J6 N /*增加一个新的工作簿*/
! W, Q7 I9 d) f0 D9 r lpDisp = books.Add(vtMissing);; ~3 j; J y% Q* H3 i
book.AttachDispatch(lpDisp);& C" F5 `! A" l, j
}; c$ ]# ~1 j7 r4 M
5 A6 t8 Z: M" F" e- G! s# E" I0 Y6 F' o: d/ L
/*得到工作簿中的Sheet的容器*/
" F% i* A/ G# A/ S sheets.AttachDispatch(book.get_Sheets());. p4 c+ s* [3 |' h3 x2 C$ h
+ E1 _8 F9 o, u* p /*打开一个Sheet,如不存在,就新增一个Sheet*/
3 X8 r; t; K( Y, r' V CString strSheetName = _T("NewSheet");
1 \7 ?8 l/ P/ h: D4 V try
# e# l9 E* p3 g" [4 _7 e; B6 X+ ` {! M% X' Z1 ^$ f# c
/*打开一个已有的Sheet*/
) F$ L' U# F- v! s% ` lpDisp = sheets.get_Item(_variant_t(strSheetName));* j" G1 o# q& g6 `
sheet.AttachDispatch(lpDisp);
' Q7 d; F( Q5 u* K! z4 A \ }
8 U" x5 t' K( L" y4 u! @1 R catch(...)
. k5 V6 U- f8 t' @ {& u1 b# T% \+ j( V" I- ~" F) X; |9 N
/*创建一个新的Sheet*/2 s3 r. t4 m6 [/ M! L8 j
lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
. o& U* _$ |! o' u2 y, ^) S sheet.AttachDispatch(lpDisp);0 Y, J8 a- I1 M' J- Y) u' B5 f V
sheet.put_Name(strSheetName);, R# w, k$ V% J9 G5 _$ H! J
}
$ V8 Z# s7 k9 Y# I" V* ?- p; y- G/ a# K1 O, w/ J! K+ @
system("pause");& H! Z y ^' r4 P3 m6 x# M0 q
+ @% F* f8 d" d+ [ U4 H$ v /*向Sheet中写入多个单元格,规模为10*10 */
0 l6 I5 |& [) H, H; R lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));* Q* ^6 j" U+ B0 d, W5 S
range.AttachDispatch(lpDisp);
9 k0 h' X1 C6 `% E" K
/ y" y* L5 l/ V1 W2 \ VARTYPE vt = VT_I4; /*数组元素的类型,long*/
# e) Q* ] o/ O+ o) q SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/# S9 c! I! P) `" O$ t3 W& x
sabWrite[0].cElements = 10;& F# Y4 _' t- ]& O
sabWrite[0].lLbound = 0;- z8 J0 B0 K- q" k( J
sabWrite[1].cElements = 10;, c- }8 D8 n S U2 V$ X# [
sabWrite[1].lLbound = 0;
h/ ?: B+ ?0 L9 v
# s- Y R; v8 I! k- ~6 d4 _ COleSafeArray olesaWrite;- Y: y6 w0 l6 ^! v/ @+ c
olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);5 k% ]& c$ I! _" C- u c
' Z/ L8 V: @1 b$ j
/*通过指向数组的指针来对二维数组的元素进行间接赋值*/
1 Y8 D/ L, v$ G4 i long (*pArray)[2] = NULL;5 g8 x1 e2 @7 H! Q8 L
olesaWrite.AccessData((void **)&pArray);
& o p& O" R) r' k1 L, | memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));) P# r- B. v( _, e6 c2 J
) Q5 M/ r& k& ^% J* @ /*释放指向数组的指针*/+ f2 V- D8 Y# b6 \0 g9 i& g; a! {
olesaWrite.UnaccessData();# Q; c0 ^0 O& L
pArray = NULL;' F3 c! v; `3 F
. c Y) {% K \( A- u0 B3 E /*对二维数组的元素进行逐个赋值*/6 ~: }0 I+ f" H% {% l! i8 k2 G- ~5 J
long index[2] = {0, 0};
+ {, z0 ?9 x: u0 P2 w long lFirstLBound = 0;
' i1 Z# U: M) M0 {7 z3 j% d" e long lFirstUBound = 0;
: h- s! {, I: T2 `7 r; O long lSecondLBound = 0;
" g4 x7 O( N" f7 C5 ?' P" H% \) C long lSecondUBound = 0;
! _+ E+ }; S# O( M* w3 M olesaWrite.GetLBound(1, &lFirstLBound);
5 c* s' N) M e4 s! {* x olesaWrite.GetUBound(1, &lFirstUBound);
/ w/ V+ K7 i+ G9 A3 H4 {0 d9 u B olesaWrite.GetLBound(2, &lSecondLBound);
+ U$ V( U$ v/ \2 {8 B9 K olesaWrite.GetUBound(2, &lSecondUBound);
( f2 P6 m# \, T2 }. [8 x# P for (long i = lFirstLBound; i <= lFirstUBound; i++)
4 M! g2 J' J, R$ r. K' }9 M z% g {
9 C9 P$ K9 H Y; c) A index[0] = i;
) x# `; `4 ^3 k9 I: I% _ e, |6 K for (long j = lSecondLBound; j <= lSecondUBound; j++)$ p, J3 R0 Y) i- k% I; a
{- i6 t" t# O' L" s' b
index[1] = j;1 \" n8 \# Q+ ?3 {# U5 T
long lElement = i * sabWrite[1].cElements + j; * t2 N8 B2 C2 Q( m$ o8 i9 D
olesaWrite.PutElement(index, &lElement);: U: D1 Z( l6 d, ?% U) u
}
9 k: { i5 m. r: F: X# Z% L" ^6 O }
$ e8 a. M" y: x( a$ j3 @( }
! O" d/ _" Q) r2 n /*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/
: X! |6 Z4 x8 W. D+ X VARIANT varWrite = (VARIANT)olesaWrite;# A" i! t" U9 `; h0 Q
range.put_Value2(varWrite);3 d. M2 `8 A8 f0 d+ x
) s* I" ]2 P4 \1 Q* g; P
system("pause");
, b/ u5 `8 T! s9 m# g6 Q) F7 t+ N( S# U1 U& u* }5 S6 z" [7 ]& X( ]
/*根据文件的后缀名选择保存文件的格式*/
& O d( H( h9 n; {8 y$ I CString strSaveAsName = _T("C:\\new.xlsx");! O3 q6 w- h. H
CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));8 t1 T4 _- u' I0 E% @
XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;
7 ?6 _& @: ]0 a# l" ~ if (0 == strSuffix.CompareNoCase(_T(".xls")))5 N8 P5 q# d. A* g) F
{5 V4 E* s( d8 g1 L0 j# M% d
NewFileFormat = xlExcel8;. U1 T4 X- Q5 n/ Z Q* B; Q
}# v9 P# p$ G+ w% r O0 |2 o- _ Z
book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing, 3 R N$ f5 }/ M, G
vtMissing, 0, vtMissing, vtMissing, vtMissing, 4 C! {4 J0 P# |& ?+ y$ z: P" h
vtMissing, vtMissing);( h% }8 u" C7 V
( `0 s7 _) b6 q/ {$ U }8 I8 z+ i
system("pause");
0 W+ H4 J% L Y) R. {) @
; H! }6 I9 P3 }4 s /*读取Excel表中的多个单元格的值,在listctrl中显示*/
0 J# Z" {" [0 I3 a& U1 Z( n VARIANT varRead = range.get_Value2();
: e, x! W( Q% O [ COleSafeArray olesaRead(varRead);$ L0 \! B# ]0 g1 M: B
, H M% e2 J( G# j) i: B VARIANT varItem;
) X$ w7 O. h- U) A. J/ V CString strItem;
. @! l. a7 p- T% f, }+ _ lFirstLBound = 0;
- S% k" Z- G; R& ^! j2 Z6 c lFirstUBound = 0;
' x% Z$ K7 ]9 @9 A8 e) m1 R3 m lSecondLBound = 0;( J: h, e/ e+ h' c3 q+ Q7 [
lSecondUBound = 0;
* m( l! t$ O! Q olesaRead.GetLBound(1, &lFirstLBound);
9 b/ U/ e- `7 k1 u5 R! L" D4 Z olesaRead.GetUBound(1, &lFirstUBound); V7 }. B8 n4 l, R3 _4 h! U
olesaRead.GetLBound(2, &lSecondLBound);
% E8 h2 O6 [8 t' X/ ?1 K4 v0 y olesaRead.GetUBound(2, &lSecondUBound);+ [$ c( {! e! G# Z/ Q
memset(index, 0, 2 * sizeof(long));5 R; J" H# @) I/ S
m_ListCtrl.InsertColumn(0, _T(""), 0, 100);! d" w" a! m F! \& A: j: y
for (long j = lSecondLBound; j<= lSecondUBound; j++)
" p- n; S" Y. A" o2 ?* W ] {
- `. a5 _5 @2 ] CString strColName = _T("");
; M" }/ ~( i, ^; W2 ]9 S; H' _ strColName.Format(_T("%d"), j);
& c- E, S) I L, \ m_ListCtrl.InsertColumn(j, strColName, 0, 100);
+ O. `3 t" n' b S2 r }2 p6 i, l' k; G: o( }5 v- f
for (long i = lFirstLBound; i <= lFirstUBound; i++)* V$ n4 X4 e8 Z8 V" Z
{
. e9 X+ p$ ^" C. ~; p" t% u CString strRowName = _T("");
! Z* U# v% M6 T! u' \5 U strRowName.Format(_T("%d"), i);7 p r# `0 ?$ k& t) z
m_ListCtrl.InsertItem(i-1, strRowName);! m ?$ j% Y' I E" a
3 H- n' S T" W5 J1 U index[0] = i;6 ~; x" ?) {# O" H1 g& ~! t) t
for (long j = lSecondLBound; j <= lSecondUBound; j++)$ l$ a1 j9 V9 l# R- v9 ~2 x
{
# d, @( Z3 ]$ X( F4 z index[1] = j;
/ |0 C4 {" G, L' q# `- I( A+ R olesaRead.GetElement(index, &varItem);" [! {# u% W8 U: Z3 M* C9 s$ r; [& u
6 r4 w) q) _; ?+ K( R. |; s
switch (varItem.vt)9 A; r- I( i9 ^
{7 N- @, [/ _( R
case VT_R8:+ n' v. |& J; V, o
{" d% `, M0 k& ~' m9 T/ b+ U4 x6 A
strItem.Format(_T("%d"), (int)varItem.dblVal);
+ V! m' P- e) K }" I) O1 S" N+ ^0 D- Q9 G" F- b: W
1 q t P" n9 K; K9 Z. T" | case VT_BSTR:2 y3 Q ~ X# p6 l/ X
{- _" l5 i( X. U2 A/ b1 Q) w
strItem = varItem.bstrVal;% x/ P. f: @4 @& l: p! S
}) Z2 K! v3 s. ]9 q ^+ w
3 J' r0 N- B' F; j% T6 S( _ case VT_I4:$ ]2 j X# F1 ~5 {5 a! D) S; t
{1 B$ T* o. e( @' V! y
strItem.Format(_T("%ld"), (int)varItem.lVal);% i# p+ K, N" H. n- {2 x1 X+ p0 M8 x
}. k: c, _+ ^7 n' a
! X, |6 ^4 K" U# e4 r' O1 F
default:
" u1 h, t# v& [7 t/ } {8 I& i/ [* H. y
# x) [5 V1 p3 m' M } }
+ l8 T$ w3 ]2 X }
; c7 D; s- N3 K8 @0 Y# \4 ^0 n9 @( }/ l9 b
m_ListCtrl.SetItemText(i-1, j, strItem);
4 l* u4 H$ h- G1 @9 }0 W }
: ~8 e. b1 B% h; |5 f. m. T% Z8 J }
9 N2 M( K# P% ~" d1 W- P! d/ w8 L E3 \$ m3 ^% o! p: E3 n* D
- Y$ ?6 ?1 @4 I
+ L, w4 N/ T# t1 b /*释放资源*/
& ], n( [+ ^, t* H sheet.ReleaseDispatch();" b% o {) d& b1 s. N7 M* c+ `
sheets.ReleaseDispatch();0 |) T$ G( _8 M. x' l' f& W# Z
book.ReleaseDispatch();
+ R) H- U# X/ {$ X6 o, A books.ReleaseDispatch();! [/ n4 k6 x6 ?2 q7 |9 T
ExcelApp.Quit();
1 l* ?: Q4 T6 Q6 q) L6 p% Z& ^3 }# z: }) M ExcelApp.ReleaseDispatch();[url=][/url]+ f' O4 i' r/ V4 K) O3 A
' o3 Y' @0 Y) U% ^- z C! t
|
|