|
|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
x
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。/ F+ S# H, j4 |
8 L4 l# H/ R& F( x4 c5 s
% s5 ?5 P) B. S2 v7 i; p# z1、添加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; }
% e3 L# m8 N9 a2 p% A 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++类对接口的封装。
( C$ ^; }$ u4 |/ V本文所导入的接口对应的类和头文件的说明如下所示:
; H" @ W. Y' ~' o; b+ e0 QExcel接口 | 导入类 | 头文件 | 说明 | _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 | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 |
$ r) A$ S2 {8 g3 J5 w m" R
% ~) ]* d+ H* P$ }' c3、导入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的相关库。 更改后的导入类型库的代码如下:
- ~' q; ]" l! m7 G0 w/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office;
/ p: X0 J1 _5 F# {; M/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE;
6 i0 B8 F7 w! a7 Y% 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;
7 w- b- Q7 }) ~- a1 i! `9 E编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。
: L" D- x2 h; Q b4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。 1 _$ U' i% M* T5 S2 w& e% ]" e
5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。
: u' `+ w3 s/ F% q' m6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。
$ J3 N: b0 l, W8 g# t% V; b$ i" Y7 A7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。 1 p, X$ c# L) P- ~0 C
m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);
( K/ U) _: T9 C' D; `- {
/ B# k/ o* g% q/ {! m$ }" U4 U/ O1 c CApplication ExcelApp;
0 x0 h4 W. F8 p4 J+ y CWorkbooks books;
% f- H7 q$ {! p) I CWorkbook book;
7 }1 s6 v6 E! H5 g CWorksheets sheets;
6 z, t2 u) O7 } CWorksheet sheet;+ B- W& t6 E. u( P
CRange range;) ^8 M* `; F& v- A8 B* k8 v( _
LPDISPATCH lpDisp = NULL;7 H# J! r- ]( Z8 b3 P3 ?" G7 N
3 {* \' e. [- X- o# p) e% K& q
//创建Excel 服务器(启动Excel)
+ C( j4 I& q" |5 |; j# B if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))
8 g# d) S& {8 s5 z {
% Q6 k" f( k3 f6 ~2 y1 X2 x1 I4 z3 t AfxMessageBox(_T("启动Excel服务器失败!"));% i7 X1 |* m3 C) l* d
return -1;
- H3 k1 w+ i# E. F! d1 m }5 A4 r( {* }: A4 M8 c. c
. b% r# n7 m& |. ^3 ? |" o /*判断当前Excel的版本*/- @* w6 G, `8 b
CString strExcelVersion = ExcelApp.get_Version();9 g1 D1 s9 m& l! w1 M
int iStart = 0;" F. a0 }" B: j
strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);
' N7 Z/ l+ ?2 T( i( V if (_T("11") == strExcelVersion)& d0 T, r* a$ u' Y v9 f
{
, v A0 l0 K( o X AfxMessageBox(_T("当前Excel的版本是2003。"));( \0 \% L" T! ^! P$ T
}
" K' U) N' e3 ]: Z W9 d+ v else if (_T("12") == strExcelVersion)
$ ?. x; H% j j% K5 R {7 w- |( h" a# g0 ]5 k- `
AfxMessageBox(_T("当前Excel的版本是2007。"));' p$ ]; e! }' j8 F9 N
}2 p' C; r0 L. E- U0 u: [! Y
else# p: {7 }" U7 D
{
. n' `# v; j: K3 j3 B N AfxMessageBox(_T("当前Excel的版本是其他版本。"));# X. M" o8 |5 b, v7 z2 y' c- {% O
} y0 I3 O) y' m. [- B3 u+ X6 _
& D7 M2 Z* w, s3 k( a. b
ExcelApp.put_Visible(TRUE);
: Q6 w2 H$ e+ Z: q1 t! _ ExcelApp.put_UserControl(FALSE);
/ a- ^5 f; X$ ]4 H# M6 y
5 ~7 W' ^% r5 O1 [0 m6 k /*得到工作簿容器*/6 H6 a9 ]# L2 i" Y
books.AttachDispatch(ExcelApp.get_Workbooks());9 A$ Y. Y/ n0 X r5 {1 R6 O
! m0 a& w, z# L. _$ j- ]7 A /*打开一个工作簿,如不存在,则新增一个工作簿*/
) q; D& c% Y* Z `$ L- d CString strBookPath = _T("C:\\tmp.xls");/ s5 f k3 D3 Y
try
9 ~+ n& ^) k6 U3 D. p0 x {6 l9 K0 | }! Q: x4 d: b) x
/*打开一个工作簿*/
' J, v6 q+ t! P$ A) q# |2 @+ C lpDisp = books.Open(strBookPath,
/ T/ T+ Y. f) u' j0 C" _ vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
1 ], f+ u% f* r4 R( O+ n. ^ vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
& w6 r# F0 W; y+ i3 r3 h; P) K vtMissing, vtMissing, vtMissing, vtMissing);6 d. x) l9 l8 h! I" Q+ N* X
book.AttachDispatch(lpDisp);
8 U. A4 Y7 A' P N; p ~! s }# b: N$ B: y# ~ d! m- R% D h
catch(...)* |3 D4 q4 Z& ^4 @$ J) {! E: G
{! U2 U; D2 F# Y% L. K
/*增加一个新的工作簿*/( h7 D# S. x. h, Y9 I) P: e) N
lpDisp = books.Add(vtMissing);! M) ]% d' @! @
book.AttachDispatch(lpDisp);
& Y% U5 u+ b% g& _5 G0 ~1 X% V }& B* E" D& c5 y4 b
1 J( P! h& F% g1 d
% q% V* k- w/ Y /*得到工作簿中的Sheet的容器*/
) S6 W, m1 B( C$ t; k sheets.AttachDispatch(book.get_Sheets());
+ e$ j: o! w! m* m) G& I" e( {: I% m! n& L9 q' e, C
/*打开一个Sheet,如不存在,就新增一个Sheet*/
+ G4 `- W, t+ E3 K CString strSheetName = _T("NewSheet");. ], V4 Q5 I2 @: b' E+ w
try# Z0 X- S X1 ]/ |
{
& z% Y& K7 f; N- `3 h6 u$ I /*打开一个已有的Sheet*/2 c4 u9 _! h% j7 W% w7 P6 \1 |
lpDisp = sheets.get_Item(_variant_t(strSheetName));% r% F' h0 D, D# D; H% c# R& M
sheet.AttachDispatch(lpDisp);
( K" }7 m/ E8 u0 d" Y0 n T }
! m* b& k. A" u7 o" H7 T' I catch(...)
% t4 w, ?6 W6 B* { {* C/ B1 U6 h# F# H3 \5 }# D1 }: U {
/*创建一个新的Sheet*/4 a4 h% r. g0 R, c+ \
lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
" m$ X4 ~/ U2 V5 G sheet.AttachDispatch(lpDisp);
, y% v/ E6 @% X% t- o sheet.put_Name(strSheetName);; Q8 F4 q/ q5 \( I' u3 Z
}
* G9 {4 A* ?/ l7 \: U3 q
2 s+ K' G8 j/ F9 T. a; `+ w system("pause");7 M( P% ]5 c1 ^/ y8 a3 \
" O% ?( v% j# B& K& O y, r' T /*向Sheet中写入多个单元格,规模为10*10 */" s$ b8 t) k* f
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));- [, K. W: M5 B
range.AttachDispatch(lpDisp);
}- N: I& B; L# T# I/ N! L& S6 \, O; v+ y n2 }8 Q* \
VARTYPE vt = VT_I4; /*数组元素的类型,long*/
! _6 J; ]) Z% _! m) ] SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/9 y. N& P/ k; F$ y' e6 H+ e
sabWrite[0].cElements = 10;
/ W% u: c3 A& ]1 E, ^ sabWrite[0].lLbound = 0;+ d3 i5 F* w0 C) v/ j
sabWrite[1].cElements = 10;
6 b( ~3 H, ^, q8 M" ` sabWrite[1].lLbound = 0;
: r: p: |' r+ D# f1 T/ \( r7 c2 u* {. P' J. n8 D% } W
COleSafeArray olesaWrite;* T P$ s8 ] I* G
olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);0 L% m9 K4 P0 j4 h9 N; H
: j0 `1 v- O; t9 W. |6 R
/*通过指向数组的指针来对二维数组的元素进行间接赋值*/) N% j A6 y: X5 d1 F; ^( w( L
long (*pArray)[2] = NULL;
! ?! o2 U, S; } olesaWrite.AccessData((void **)&pArray);6 s z. Y% A) K; B
memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
0 F) I- M, v' J! ]; n* m: B7 x2 B! x% [7 r# q7 L
/*释放指向数组的指针*/
8 ~' N. ]4 k: Y( b% e olesaWrite.UnaccessData();, B4 M3 h+ X5 L3 j$ ~4 U
pArray = NULL;
# T( ]0 W6 e: u. _ R4 l2 |5 x: C2 a, q5 }) c' H- r: n0 H
/*对二维数组的元素进行逐个赋值*/7 H0 H6 _ t( d ^1 _9 H7 D% `% v
long index[2] = {0, 0};
% }5 M8 ?& C, {, L/ O5 f. B long lFirstLBound = 0;( P- b6 b* |" [. {2 E/ P# |1 e
long lFirstUBound = 0;
4 @' n2 D# G- b long lSecondLBound = 0;( `4 q/ ~ I0 Y+ F% y% a
long lSecondUBound = 0;3 U3 ?" W" N# J7 o7 _" q
olesaWrite.GetLBound(1, &lFirstLBound);! h% j& D4 _: F* q9 C) X+ ~
olesaWrite.GetUBound(1, &lFirstUBound);) M2 E/ k$ x9 r, W0 S* o) b$ I
olesaWrite.GetLBound(2, &lSecondLBound);# z4 q) h1 b% J1 i! }$ x$ l
olesaWrite.GetUBound(2, &lSecondUBound);& k0 l- I1 t. S# ~/ v
for (long i = lFirstLBound; i <= lFirstUBound; i++)
6 s# o, B- C( l T1 h {
7 @7 k2 y$ L( A) W index[0] = i;1 g I& c5 C% R4 M6 o0 R, y
for (long j = lSecondLBound; j <= lSecondUBound; j++)
2 F4 v$ c4 F6 v4 O6 Z {# p( Q' H$ K, x* W& ^5 e9 e
index[1] = j;
; Z4 B4 i& y( A. k' { long lElement = i * sabWrite[1].cElements + j; 7 F5 j" ^2 `6 N8 \( f5 ]+ H' D" |
olesaWrite.PutElement(index, &lElement);" s; E$ B5 W6 Q9 @
}& U- f' p4 A/ l
}
$ M4 P4 z' [5 [* j4 O& g n) J. m( K2 @
/*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/
4 T) T, {) Z E8 X0 @ VARIANT varWrite = (VARIANT)olesaWrite;
3 \( m; [& N2 L0 n/ G1 { range.put_Value2(varWrite);
" B- v* W( e, t
6 L% B5 p* v5 F3 b& [" N% j system("pause");
" a) f# B8 {: z2 K6 }8 K
+ X0 c" o9 f! C1 C* I /*根据文件的后缀名选择保存文件的格式*/
1 }1 g1 @ \8 o+ R/ J" k CString strSaveAsName = _T("C:\\new.xlsx");
[ C4 m# X* ]5 C; q" R CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));, M1 b6 ?1 q: N+ ]2 t: B- Z( L
XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;
2 q# t/ r) F6 p/ ^ if (0 == strSuffix.CompareNoCase(_T(".xls")))7 v5 y! m) u. N1 X3 g# A! @' Y
{
+ s' x" k4 `8 s8 @2 E3 Z7 s; V NewFileFormat = xlExcel8;3 U2 \8 p, q/ E$ C% i* f' z
}1 G9 ?0 S7 V8 Z1 u* c6 H$ b( m0 B5 W
book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing,
5 b% H9 B( ^$ }. i/ g5 V: z vtMissing, 0, vtMissing, vtMissing, vtMissing, , b" _2 C0 m+ V- p+ t
vtMissing, vtMissing);
1 W* C0 v( ]+ ?2 } x) F
$ U; [( s5 Q4 W1 x system("pause");8 n1 }* y2 `5 t# o# V# }
& ^2 I# J3 E6 X- n" ]; c+ S' T6 A7 \ /*读取Excel表中的多个单元格的值,在listctrl中显示*/
, x: X6 m$ N' }- \8 J7 u VARIANT varRead = range.get_Value2();
- j' v) V$ e9 _7 u3 F COleSafeArray olesaRead(varRead);% q8 \- j& z" x1 W
1 N1 M( n q& _4 t/ O6 m
VARIANT varItem;; b$ c. q/ w" x0 v" n* E8 A% q; P4 S' q
CString strItem;
7 k/ Q A. F/ g) u# d lFirstLBound = 0;' F5 x1 s* I1 C1 L- D: U7 ?" I5 |' K
lFirstUBound = 0;/ Q; z/ e$ \3 h' d5 _* R) e% `5 `2 T
lSecondLBound = 0;
4 S _4 F! j4 k5 i6 A/ q lSecondUBound = 0;
9 T. g6 p2 b' P olesaRead.GetLBound(1, &lFirstLBound);, p: X7 A9 v. k& g
olesaRead.GetUBound(1, &lFirstUBound);
, d9 q- W {4 ]; z- [' Y olesaRead.GetLBound(2, &lSecondLBound);7 U6 A" i8 W: Z" n8 ^
olesaRead.GetUBound(2, &lSecondUBound);
0 `+ d' ^& Z' ^3 I- ~ memset(index, 0, 2 * sizeof(long));
J% y5 p# W3 N- Z- _ m_ListCtrl.InsertColumn(0, _T(""), 0, 100);
, w* V: `3 L) X! O: ?! v/ @1 t4 b for (long j = lSecondLBound; j<= lSecondUBound; j++)
8 ]$ A9 C& y3 \ `# @7 t( l) K {3 @, Q* Z" @1 p$ i' X
CString strColName = _T(""); a$ d4 Q! _8 S6 v/ S \" K! n2 w
strColName.Format(_T("%d"), j);+ C& w7 V% }! E! p' ]" O
m_ListCtrl.InsertColumn(j, strColName, 0, 100);
8 `" ?' C* H& N }4 T( I7 X; W0 d8 o
for (long i = lFirstLBound; i <= lFirstUBound; i++)0 y( ?- v: t' L' b' B$ K3 n
{% k( t+ P% J( z
CString strRowName = _T("");1 z8 }* r p8 c. B9 v( l9 w$ E7 M2 r- Y
strRowName.Format(_T("%d"), i);
/ f. y! U+ ] B' G1 p- O6 S m_ListCtrl.InsertItem(i-1, strRowName);( d, s4 z9 H4 ` @
$ J7 |& D, U- g8 A
index[0] = i;* ~6 F2 r/ y4 W6 ]; s3 A3 a9 h
for (long j = lSecondLBound; j <= lSecondUBound; j++)+ i; V2 `) Q. r! i% }% @2 l9 G
{
/ m8 [6 q) x9 ^6 U2 H5 ]3 V$ z" V index[1] = j;
8 x( P" c7 H! h0 X# t y8 m olesaRead.GetElement(index, &varItem);
2 j) l# d0 \) x7 q7 h7 [" q0 P5 \3 e8 d; b! s7 s
switch (varItem.vt) s7 r; w3 w2 M7 z) d2 V
{* @# D3 l9 t+ H% {" O3 H+ \* A3 z
case VT_R8:' M( J% X" O" |, n0 N$ E' x
{
) p# p5 R7 O. g; J6 ~+ P; O. T7 r strItem.Format(_T("%d"), (int)varItem.dblVal);4 f) e1 y% x4 a. h+ w
}1 x4 S+ n/ A! Q) |& Q0 R" |% Y! F
) N7 \0 S6 @0 I* |3 L- p case VT_BSTR:
, X/ w2 h. n/ l6 L4 H5 {1 }% S$ _ {
: ]! G' }: `3 \2 W strItem = varItem.bstrVal;
$ Z+ ]' @3 V& U$ p2 k0 g: L }
* y. I& q- d8 U! \ c9 r: f) X- j, M0 J" t5 i( X: e
case VT_I4:9 J* j) ]* X3 Y+ S
{6 M% F2 E% }6 J% z# q3 [
strItem.Format(_T("%ld"), (int)varItem.lVal);
5 x/ Z1 v+ h; K& s% u }2 Z* E2 X. W5 @" y& i4 w
, E0 g! |. Q% M
default:1 a! \; H: l$ q
{6 ?9 o: b/ c" K
2 j! S! Z, x6 `9 h% Z }: F; ^5 i, |1 q" i
}% Q8 H1 W2 S: F; l/ _: H4 K& _
. B5 x$ e& W3 X: Z' q: j% s& x
m_ListCtrl.SetItemText(i-1, j, strItem);, ?: M8 U6 k4 D7 J
}9 ^/ X" O4 A1 M, N4 D- Z# N
}& {& C5 {0 |* Q! ^+ [ M! Q
I: ]: T+ R; [% e3 n4 n$ N9 X& m. ~- ^6 m. L3 [% e' ]! ]
, _5 R2 M+ x" j0 r
/*释放资源*/; y+ U0 g" N- d0 X1 v; x5 e: t# N
sheet.ReleaseDispatch();
' O4 C4 W& d0 D7 x, r) t* d sheets.ReleaseDispatch();
B+ d3 y! `; B7 b9 j book.ReleaseDispatch();9 k7 ` b; u9 `4 @ n9 Y
books.ReleaseDispatch();- f" M, y3 H& n$ I4 L; o- |
ExcelApp.Quit();
* H$ L- y/ Q; p6 z& \ ExcelApp.ReleaseDispatch();[url=][/url]4 O: p; G$ S0 v$ ]3 G4 i& X. J v
( i1 i( w0 D4 j8 o |
|