|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
x
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。* t0 f; F h% P4 I& a' H
) w2 b+ ]+ Y) C2 z
3 G7 h: c$ F+ j$ r! |7 w/ B1、添加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; }$ A4 M, n/ l8 q% i# S) R
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++类对接口的封装。
6 s- I% K9 G% S) {' {5 Z- K本文所导入的接口对应的类和头文件的说明如下所示:
7 a! `. I+ _! f4 E9 TExcel接口 | 导入类 | 头文件 | 说明 | _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 | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 |
* W) E2 g0 ?# I" p3 }2 Y4 h7 g8 G" U! p% T! p& b
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的相关库。 更改后的导入类型库的代码如下: ; o; L6 b& J0 k2 a9 d
/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office; / R* ?( V3 h' o. ~( l
/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE; N1 \: j% Z8 L( c3 L/ q4 l
/*导入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;
. U& _1 Q. |/ ^2 D1 h9 R, C& T6 | U编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。 1 D7 @* N% ?& ?/ {4 w
4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。
$ L( Y& }; J2 j. U: c0 A" R5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。
: |$ J; K2 L& J3 g* f6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。 * W% r4 f4 a7 i# k2 h1 A, R6 q q X
7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。 0 k. C4 c e" S, F$ D
m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);
$ i& q( L) {& F( G3 M8 L& z) c) {8 |6 {+ N8 ~
CApplication ExcelApp;$ k- O. N& {! i. M& j
CWorkbooks books;
7 F6 k# t6 R* c+ r c% X CWorkbook book;
+ _- f5 T) X: \ z CWorksheets sheets;
2 b* l4 Y% H+ Q7 b CWorksheet sheet;
, a& a7 `+ C) H$ n4 F CRange range;7 }& @0 L+ Y& m* i1 k
LPDISPATCH lpDisp = NULL;
0 m( O3 P0 C+ D" R3 h: @/ X+ Y4 F- R+ G. p) |' ~. m! A
//创建Excel 服务器(启动Excel)" s8 H! `5 V# v+ ?
if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))- H$ G5 ^, w( W1 J
{! a' e3 A) m5 N0 j4 Q& k( G
AfxMessageBox(_T("启动Excel服务器失败!"));. e+ B+ K; D0 K2 H& d' Q
return -1;
2 o8 ?% }) s6 s6 z* r }/ h3 \$ q# t8 q( P
9 `+ q+ R, K5 s) f" w
/*判断当前Excel的版本*/
/ |8 M; T# B% X$ g o CString strExcelVersion = ExcelApp.get_Version();
! A9 O1 l8 H7 [6 b! Q+ Y' P! R int iStart = 0;
9 I$ C& n3 L Z" C+ e7 G strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);* L+ l' U6 u# b
if (_T("11") == strExcelVersion), e T4 X' h* h/ m" A0 Y. A" A
{
" F6 K+ f4 J8 }- Q( ` AfxMessageBox(_T("当前Excel的版本是2003。"));
1 E+ m, O3 K4 H# a7 b }
0 w5 m2 ~( {* _: U% l. H- `' Z else if (_T("12") == strExcelVersion)5 c3 |5 Y. d2 L4 n3 O: t
{
' n9 t D) o; u8 x8 I4 j7 Z AfxMessageBox(_T("当前Excel的版本是2007。"));
1 p7 B0 D1 A8 q; S/ L5 V }
% B" H6 J3 r; n6 @" u& } else
( E* [" g7 A6 o1 q3 e* S' E v. P {
5 _3 K! y: Q, |. V2 b3 q- q) ~ AfxMessageBox(_T("当前Excel的版本是其他版本。"));
% c$ C' D$ h" Y( a, D }
. l! t& {% E8 m/ K
+ J8 k9 _4 B0 I: W+ q3 k3 s ExcelApp.put_Visible(TRUE);
: |) R4 h: `( s& O ExcelApp.put_UserControl(FALSE);
! {* O U- k/ w# W2 N. e& w* P* I' _, J& e& V9 J2 F$ Z
/*得到工作簿容器*/
8 Q0 H, I& y2 g3 J; m/ f books.AttachDispatch(ExcelApp.get_Workbooks());
$ K5 L; w& N) M C
" V7 E. x8 I) b6 z6 q: J" ? /*打开一个工作簿,如不存在,则新增一个工作簿*/7 F0 l( J2 @; v; `% L: h
CString strBookPath = _T("C:\\tmp.xls");
/ D( o4 x2 q0 t6 J) d: r& z! ^+ d+ n try
, y: E; f. \, j+ t/ ?# j* J0 \; r$ f3 v {
& A# t* \ Q" Z6 ^8 z /*打开一个工作簿*/
$ e. u: J, A- G+ [+ M! c4 x lpDisp = books.Open(strBookPath, 6 E: }' F0 g& E8 ?3 s
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
5 U1 V! L; O0 V7 p" } vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, 1 @* ?/ w! m1 W9 b) U5 E
vtMissing, vtMissing, vtMissing, vtMissing);7 S0 x. s8 n6 G' a# c. `
book.AttachDispatch(lpDisp);8 `2 \! Q$ s/ h! o4 ?
}1 f! ?) x1 a$ v* n! \
catch(...); J2 q" o! o' S* {
{3 ]. M2 ~% n8 w7 `$ |
/*增加一个新的工作簿*/( R: w R0 m; M/ U
lpDisp = books.Add(vtMissing);
6 V7 a0 `" J: j% Z book.AttachDispatch(lpDisp);- I( i, H1 r' B: f, o. ~- R
}
/ e$ o7 q: v* K2 v( B
/ J" ^- z7 {5 h' Q* K" S
% X" h5 ~6 W. J C# e /*得到工作簿中的Sheet的容器*/
; A0 {; d$ ]5 f9 j. r& S* R sheets.AttachDispatch(book.get_Sheets());
$ r# \. T5 g. Y3 j2 c$ p# y w3 W, z. K+ {2 g3 o7 C
/*打开一个Sheet,如不存在,就新增一个Sheet*/! t/ S7 j! I5 s- s9 r1 @( K
CString strSheetName = _T("NewSheet");
3 l2 x4 k, j# n- R* z( m8 J# m5 T9 F try% d+ z" `1 \% G+ y& R) I! P* e
{
3 y4 d' c; t- v4 h2 g$ G /*打开一个已有的Sheet*/
/ D7 G* R' o) U& n' X lpDisp = sheets.get_Item(_variant_t(strSheetName));& ?7 O5 l7 o6 ]8 i" [# i3 E
sheet.AttachDispatch(lpDisp);2 D5 S# r7 Q7 _' Z! `8 i% [
}
6 m b/ y% y L* O) A( h catch(...)
) g* k# U r, t: |, x% c0 l+ k1 X& w, z {9 a( o& N* R4 n# K8 m
/*创建一个新的Sheet*/
( {6 j" h: ^' |# E, T) L4 n lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
7 W5 a( j% h9 }' K* w% o4 V$ s sheet.AttachDispatch(lpDisp);" O' |6 l4 x( ^2 e& ?
sheet.put_Name(strSheetName);
' ^* z6 z) I; r2 T4 `) y/ | }# B9 `9 i; u2 @
) v3 s4 O# n# s+ ^2 l system("pause");
9 l6 O' O* z) X V, D# m; N" c
- C& q* d3 w! G8 a& k$ q /*向Sheet中写入多个单元格,规模为10*10 */; ?" c2 D, Z" ~* a. f) q0 u: Q
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));+ V" i, J. \" ^6 ~
range.AttachDispatch(lpDisp);
\8 _4 Z* _3 r( u; r# H9 \' I8 O& b( ]$ a; {6 f
VARTYPE vt = VT_I4; /*数组元素的类型,long*/7 ? c9 s3 r/ J
SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/7 l+ l( `1 R# m# x. W' E
sabWrite[0].cElements = 10;& E8 [) ]% a3 \, y: z. i9 H
sabWrite[0].lLbound = 0;
8 v/ h Z7 F! P sabWrite[1].cElements = 10;
1 v% F2 [2 A/ {: X sabWrite[1].lLbound = 0;
3 g2 L# S7 i- R c
; G& u1 H; H( V' Y+ I- A0 N COleSafeArray olesaWrite;$ w4 C) D2 `5 s C- p2 i
olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);# H( K5 P, p/ U) M9 M$ M
$ H! u# b: S* t9 S4 d6 _. X
/*通过指向数组的指针来对二维数组的元素进行间接赋值*/
# \8 c% X5 S/ A9 @. ~ long (*pArray)[2] = NULL;
1 h. c& x4 W6 ]7 d1 i olesaWrite.AccessData((void **)&pArray);; r4 ~1 T3 v: C8 D
memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
' R. e, Q) c d+ P
& L0 ]% A: P/ Q: Y z- e) R6 C0 x" G- k /*释放指向数组的指针*/
6 e8 D! G4 ~. {/ D! p$ p olesaWrite.UnaccessData();% F8 f0 H( d/ e5 v% E0 g! D( C
pArray = NULL;# I9 r5 ?. }; ?' f4 @! |5 A. `
X3 \: K+ o; ]4 K U /*对二维数组的元素进行逐个赋值*/% T% q; G! o9 k# n, Z* Y
long index[2] = {0, 0};* A+ ?* `! |& @& n7 ~
long lFirstLBound = 0;
" b' Z1 e0 k; D( B( }" a6 t7 P- P long lFirstUBound = 0;- r# j- ^0 |; {& r2 t
long lSecondLBound = 0;- b0 C3 t$ u) Q0 M4 B
long lSecondUBound = 0;6 u$ G7 M3 ]' s6 P+ G8 C: a
olesaWrite.GetLBound(1, &lFirstLBound);
1 A" w2 e" S0 o- ]2 i, t olesaWrite.GetUBound(1, &lFirstUBound);# W2 u2 O+ d8 v9 T; ~. g3 i; o' d
olesaWrite.GetLBound(2, &lSecondLBound);
7 o1 P0 g6 U6 g7 y# g olesaWrite.GetUBound(2, &lSecondUBound);
3 G/ @# D- q6 G! V' X9 d) T for (long i = lFirstLBound; i <= lFirstUBound; i++)# I' X! `% Z/ |2 x/ Z' f( P
{
1 ]# G e# ]9 y b( Y6 { index[0] = i;
% W0 H% \$ o0 o$ M( B for (long j = lSecondLBound; j <= lSecondUBound; j++)0 B* w5 z* z: W% i* M2 Y
{
& S1 Z8 G8 A6 T' g \. N3 v index[1] = j;4 V. a6 t: @; ~, |; Y- b" z
long lElement = i * sabWrite[1].cElements + j; ) u( a% I J, e e; s3 @ u
olesaWrite.PutElement(index, &lElement);
1 r' z# r g b/ G) l' r) A( B }
( F; p" S5 p. T( \2 W9 }( l, s }
! H" `; l0 ^+ ^. Z- a. i3 j! M
2 q* W7 ]; p6 C( e /*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/
$ G0 G) F6 M4 H8 c1 I VARIANT varWrite = (VARIANT)olesaWrite;
4 | Y/ h* e8 t- D: Y+ E- } range.put_Value2(varWrite);& w! X- m! K" r: }' t; b0 c: V
8 A" W3 z) ?/ r0 ]$ b% \
system("pause");
. O4 i8 r3 T; s s1 o2 w1 c
* H. q/ m) |/ Z( w5 n0 K /*根据文件的后缀名选择保存文件的格式*// |1 H* L+ I5 E0 K
CString strSaveAsName = _T("C:\\new.xlsx");
2 j/ k S, q( l CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));: x6 G4 T, a% C3 u$ U0 W
XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;
* }$ X1 s. ?' N if (0 == strSuffix.CompareNoCase(_T(".xls")))2 E/ B* @7 S6 O7 _( H
{/ @7 y. ?! m" t* `; ~" k
NewFileFormat = xlExcel8;. o' A1 y! `2 m. g0 Z+ {1 p
}" i6 e! y9 r4 u* I: q
book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing, I3 g( r. w- ~0 C$ ]. F! v
vtMissing, 0, vtMissing, vtMissing, vtMissing,
7 L! a# I& L, J vtMissing, vtMissing);* [/ ~. o! R7 C/ M
4 c, V5 H$ d0 E7 o& o1 b* ?
system("pause");
. x8 m F' c, k1 X; @# i* C2 O1 U- B) u5 `. {7 Y
/*读取Excel表中的多个单元格的值,在listctrl中显示*/$ O1 k* \, Y! s' `- w/ z, e: L
VARIANT varRead = range.get_Value2();
( E0 c( U( U g$ G* |; X) \ COleSafeArray olesaRead(varRead);
% x+ d% r) z- y6 F
& w6 t- @& B5 l6 z0 g( G8 B9 L8 ? VARIANT varItem;
- x, }* O3 }2 T5 \4 ^; S CString strItem;
3 W. P, s o8 V* J1 c lFirstLBound = 0;- Z. X8 G9 N9 a( {+ d9 e5 F- P# S1 b
lFirstUBound = 0;
Y* ^/ @7 B2 |0 m0 _ lSecondLBound = 0;
- ?; C4 [0 Z' g$ X0 ^; g lSecondUBound = 0;
4 X" \: z. B8 r* ]" l olesaRead.GetLBound(1, &lFirstLBound);
1 x( j; K2 Z" }3 [ olesaRead.GetUBound(1, &lFirstUBound);7 B+ }; S2 r7 H1 H! u9 |
olesaRead.GetLBound(2, &lSecondLBound);0 ]( y3 s q5 d) Y3 e3 K0 |
olesaRead.GetUBound(2, &lSecondUBound);% @* X% f; b1 c
memset(index, 0, 2 * sizeof(long));; Z+ S1 W D, z% ^# N1 d, R
m_ListCtrl.InsertColumn(0, _T(""), 0, 100);
% R& G5 j, r6 W0 e! x0 q, i for (long j = lSecondLBound; j<= lSecondUBound; j++)$ M: E3 N2 Z# ]* f1 p- a# }
{
1 g! C3 h: E1 D( T% t+ X CString strColName = _T("");/ Z" {9 U- T* U" `0 x) K2 {' ?
strColName.Format(_T("%d"), j);# M/ I' i, K4 B6 _) L) }
m_ListCtrl.InsertColumn(j, strColName, 0, 100);* ?8 h7 }$ H1 L; v7 e" _
}
8 C- ~. w: m9 m% M$ w for (long i = lFirstLBound; i <= lFirstUBound; i++)
. m/ l+ x( k1 t {9 }. }3 ]6 L8 S# F! w
CString strRowName = _T("");6 D* Y0 `& t6 o- m0 s$ k( k. A
strRowName.Format(_T("%d"), i);3 k) \" [- v1 I
m_ListCtrl.InsertItem(i-1, strRowName);
9 l) [# s7 L2 @0 z8 ^0 W) f+ h! j' Y, F
index[0] = i;
$ F7 q9 p" g5 v/ a/ a5 ^" o for (long j = lSecondLBound; j <= lSecondUBound; j++)
0 p* Z/ d Y- f2 o. O {) I6 `7 o% U7 t
index[1] = j;* g# @+ C) @9 N8 H6 d
olesaRead.GetElement(index, &varItem);% u0 I- L h. @2 _2 s) `( [2 C
3 f( e! N* M$ P- o h5 B- e. E! x switch (varItem.vt)1 e2 R. A: N' f! W- `8 z1 }: Z
{5 z" c7 T& P) c4 }( h
case VT_R8:
0 K: p$ S: p9 U* B6 u4 h! a) W# c* ? {( @& O' f$ m$ e0 H+ x$ q% [2 ]4 s
strItem.Format(_T("%d"), (int)varItem.dblVal);7 @4 |3 K3 ]/ |) b, N3 L
}( W, u% D" u. g
% L" l' u" y" H4 l) x3 Y& E5 Z4 c case VT_BSTR:
8 {8 @3 O+ g2 c, }+ t7 d {
% a) v9 k9 Z' d6 q y( l" W7 H strItem = varItem.bstrVal;
$ C0 I: }! S9 l9 k" h- L }
* T5 j: I, T3 s3 b0 f( M9 t; y$ v0 U) g# @6 i. H2 x' G+ J
case VT_I4:; @, I* ?9 d5 `4 ], n, J$ T7 w
{# ?$ z/ Q" w. |4 N7 s4 X3 o/ p
strItem.Format(_T("%ld"), (int)varItem.lVal);3 B" b$ O6 N6 T/ e* K
}% Q! B3 W% q& v* J2 x
/ I) ?; w+ K4 }- X- Q; j/ Q' l. r default:5 @' k% @ s5 P- b# f* P0 B1 ?$ R8 i0 D6 r
{- K8 T; h" i% U+ f& g# n3 |6 {
: P9 A" O6 [1 s+ O8 k4 W$ B
}' L y% P% ]4 D& M
}# Y n7 L: @0 H$ w d1 N
, Z2 L L2 I5 K+ V/ J/ J4 h, O
m_ListCtrl.SetItemText(i-1, j, strItem);
) e2 l. Z0 q: q% g- J( Y; E }7 X' ^8 o' v8 k2 J
}8 R0 e4 M' d$ ^# ?5 ?1 b
Q) j: s; q5 Y
& A0 ]. G3 H+ C2 \: { y V3 W/ m; [8 @' I5 B
/*释放资源*/
( h) k' ^ E9 ?/ Q sheet.ReleaseDispatch();2 F: ^) ^9 G' K! {" l I) }7 z7 G
sheets.ReleaseDispatch();
2 D$ D7 l ?: @! ` book.ReleaseDispatch();
: T4 y* c$ y7 \% h6 | books.ReleaseDispatch();* P4 n7 r. X4 i9 P& d% B' P
ExcelApp.Quit();8 ?; j3 D6 X, C
ExcelApp.ReleaseDispatch();[url=][/url]
6 l1 b) u% n0 s7 c) L6 t! Y/ T* |: p, ~) \+ N, K8 Y
|
|