|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
x
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。% j" D) j* }/ y, ^ }2 C
% s3 L4 F2 r! K+ W' b
! H% |8 e2 R* I% \1、添加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; }" ^4 a: U- ]: U! b& Y1 ~
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++类对接口的封装。
8 d) d. m* O( `+ y% o* d本文所导入的接口对应的类和头文件的说明如下所示: * B1 P2 D* s! H4 I5 i' L
Excel接口 | 导入类 | 头文件 | 说明 | _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 | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 |
- }- n J# q" Z( d7 B' I5 t5 ]1 q+ u: {( B8 \
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的相关库。 更改后的导入类型库的代码如下:
: b8 |) r6 G$ s; x2 j# e2 J/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office; & |: n$ D- r1 \2 d' ~, j" B
/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE; 0 |7 m. ~# d) g3 \. S& x1 Z% w$ V
/*导入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;
R" K. e, s& W! B编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。 $ n1 c7 W& V) C: `" C8 R6 X0 s- ?
4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。
4 p6 q- C# e( b. Z4 _* r5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。 8 v4 N" l' o; p; l# C
6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。
u2 B1 Q3 \, v* ^7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。
0 z- Z3 H5 ?! p- g, B m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);1 l/ r3 D9 N$ P1 r' }0 v
& P1 W8 C/ C- b CApplication ExcelApp;
# B% m' [) i- F CWorkbooks books;" V& n) ~% ~) [ P6 x/ q: Q
CWorkbook book;
) I5 r; y( a# M: M6 e CWorksheets sheets;
- \9 k3 r9 n8 z2 J5 {7 Z+ [ CWorksheet sheet;2 L, P4 c( l- [; k6 O
CRange range;
" M) p& k& A2 E/ a; C+ s LPDISPATCH lpDisp = NULL;% X! j C. l3 @8 ?% Y! Y$ `
& ` T& c8 I* f3 P7 v$ |; a //创建Excel 服务器(启动Excel)
4 v4 y* D, A: p! T if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))
# n7 i# D& h' B7 o) `3 r {
8 L: Z/ {1 r" U$ f3 q AfxMessageBox(_T("启动Excel服务器失败!"));
# j. {1 K% ~& }8 _" ~1 a return -1;
8 t% ~* R8 j) p# o7 Z" b* @ }
' r- V3 o- V! S. I4 z7 a6 v
+ U; S+ e; f0 X- g /*判断当前Excel的版本*/
+ y/ }* a$ ]! P' w CString strExcelVersion = ExcelApp.get_Version();, X5 Q- G% d0 Y+ D# g4 ^1 T4 D" Y( v
int iStart = 0;
4 F9 D6 d: m+ O8 b" R( }: j9 J+ ]- V strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);8 g; Q6 m- p4 ?5 t! p
if (_T("11") == strExcelVersion): k+ K( b8 {8 U! s/ O& C9 U+ J
{
- z b+ H$ _' ]8 L AfxMessageBox(_T("当前Excel的版本是2003。"));" n# C( G; s5 R
}
" p. K8 \# \; q4 C8 M$ f else if (_T("12") == strExcelVersion)
: l( J% U/ L& t p {
' H0 Q( g* @" s8 ]4 S0 ]/ `8 G AfxMessageBox(_T("当前Excel的版本是2007。"));" ?% e0 Q1 z8 z
}% q9 c$ g1 E8 l/ q" u
else
! l4 l5 s2 j9 T7 X' J$ m { I a4 o) N/ E6 i4 ~, Y! j$ |) l- p
AfxMessageBox(_T("当前Excel的版本是其他版本。"));
- A7 `9 @& R4 P% m% F }* P' g/ H; U- |( \+ Z) x" A
8 B f4 T' [4 K, L- G ExcelApp.put_Visible(TRUE);
1 s' w- \- g" s$ z) c9 @ ExcelApp.put_UserControl(FALSE);
$ N$ S; T1 H7 M# z+ X7 A8 y8 }0 H% K( w6 R0 u) i n
/*得到工作簿容器*/2 I, x) @+ N1 R; b2 t
books.AttachDispatch(ExcelApp.get_Workbooks());7 ^/ G' L5 v+ n, a
& [8 F* ~& h9 k" y% R+ t0 }9 | /*打开一个工作簿,如不存在,则新增一个工作簿*/
, j% E# m. B8 n* K5 ?' X' f CString strBookPath = _T("C:\\tmp.xls");$ R& t" `3 L: P, ^* p
try5 [9 L6 D3 k: q. B# O
{' g9 M' N7 c- m* e0 f
/*打开一个工作簿*/6 b0 k J h4 g
lpDisp = books.Open(strBookPath,
: o# |- b6 }5 Z1 d3 S5 h vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
) }" @' t5 \% T7 ^% u, f8 ]" L vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
% M# d$ j* I$ v R9 K* u7 A vtMissing, vtMissing, vtMissing, vtMissing);
/ g3 F; [% e3 E book.AttachDispatch(lpDisp);# j8 N! B$ S* S A$ |
}" ~7 d, z! ?* Y
catch(...)
8 t. U0 \0 P% M! z% n& ]+ b {
, Y3 T4 e5 O6 k/ b- ] /*增加一个新的工作簿*/
# q# H# _5 o' d8 g( T lpDisp = books.Add(vtMissing);3 x3 T/ b: [3 ~; T7 n; B
book.AttachDispatch(lpDisp);
$ ]. F& I/ N0 ^ R4 T6 f }
: V, ?3 Q3 m6 [4 g& }. ?6 G
9 e+ V$ i3 K2 N4 s' G- Y4 H
% M* y# K( g+ ?, A& Q* m0 z /*得到工作簿中的Sheet的容器*/
3 h6 A2 \4 U( I sheets.AttachDispatch(book.get_Sheets());
! ]' r M# c" b! u+ ?9 n' y; p% x
/*打开一个Sheet,如不存在,就新增一个Sheet*/
% T3 @2 q. V3 a/ z, ~ CString strSheetName = _T("NewSheet");
* i4 N7 ]* ]& i2 ?6 C try
' V' p' ^4 S' d/ J; R q% D {
8 K' r# F, P0 u! Y3 Z /*打开一个已有的Sheet*/; R% N9 E. C1 h! N. r! C1 k* q1 q
lpDisp = sheets.get_Item(_variant_t(strSheetName));( s% D( Z: R6 B" g
sheet.AttachDispatch(lpDisp);) b/ i& f) w4 a; w, Y6 ?1 s
}
! Y/ p8 p0 W) @7 L! }2 Q3 m4 Y catch(...)
7 W' N0 A ~- ]! Y7 @ {
* b( e- l+ W8 z) e% i8 S /*创建一个新的Sheet*/% o$ l% c, @6 R- Q9 U7 K; t
lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
% H# [, u& ~& q. w sheet.AttachDispatch(lpDisp);
7 f4 Y8 A2 ~! l sheet.put_Name(strSheetName);
, |* u$ f- V9 `) i1 V }- h) P1 N6 Z) l
6 y- m7 N3 J" E, t# j$ ]8 t system("pause");, P2 E2 H. W4 f) U& M ], ? t W
7 E1 U9 p- z* E% l7 Z
/*向Sheet中写入多个单元格,规模为10*10 */3 t& e7 Q9 }. V$ l. X6 ]
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));
2 S% I5 \0 k5 V9 v& d range.AttachDispatch(lpDisp);2 `: e6 v3 b: L3 \ p
* W) J2 ?& e. m; C, K" q VARTYPE vt = VT_I4; /*数组元素的类型,long*/9 G1 F5 B- F6 ?! Z, J/ \3 O' k( w
SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/$ ?: w) N9 I1 X2 S( Q5 Y8 X
sabWrite[0].cElements = 10;
/ W6 |& `9 S, g( u2 i sabWrite[0].lLbound = 0;% y* F; u- k6 ]5 j! Q, ~
sabWrite[1].cElements = 10;/ F A) [( [: [2 q- r
sabWrite[1].lLbound = 0;
2 e4 S( G' O2 d. y5 C. a$ i$ \4 h
t) K- A6 g5 G" z COleSafeArray olesaWrite;" K! }5 W& _( o0 b4 U% }9 M; `) v
olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);9 s8 P$ m8 X/ @ }8 N; e
) Z, X$ [5 O: L+ I) g z /*通过指向数组的指针来对二维数组的元素进行间接赋值*/
) D8 }4 p- }/ B long (*pArray)[2] = NULL;8 A3 b7 y# n; o9 d+ v
olesaWrite.AccessData((void **)&pArray);
4 w6 q M H' _9 ~: E: s1 E' @8 W memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
! C% I$ o' J% e8 k$ X9 E/ A, h4 K: J' Y" ]: C% Z: C! ^( |
/*释放指向数组的指针*/7 l3 v2 p3 r' M2 g% C
olesaWrite.UnaccessData();* B, r) d. O+ V% N1 h; o; W. b
pArray = NULL;
* K k2 a4 h% ?: r6 e+ Y1 j9 B8 b2 S1 ~ n/ d( Z# o, F
/*对二维数组的元素进行逐个赋值*/' ^! |3 e1 h9 J, P+ e/ U7 _
long index[2] = {0, 0};
4 K& o( l; s3 J# R7 L long lFirstLBound = 0;" {% k! c1 L' w, L; K/ A* x8 u
long lFirstUBound = 0;
$ _2 A+ |+ q/ x8 R7 k* f R long lSecondLBound = 0;
9 `6 H% j& @, q long lSecondUBound = 0;
+ r0 @/ b4 m, {9 t+ U$ a, j! \9 Y) \ olesaWrite.GetLBound(1, &lFirstLBound);. ^- P# J) X0 a. ^5 C/ {
olesaWrite.GetUBound(1, &lFirstUBound);& ^3 Z9 j) ]8 }0 k( R0 ]3 u
olesaWrite.GetLBound(2, &lSecondLBound);
; h+ E5 k* T6 c# j8 ^$ c/ z3 Y olesaWrite.GetUBound(2, &lSecondUBound);
( Q* Z! G8 @: u; r+ `; u for (long i = lFirstLBound; i <= lFirstUBound; i++)
+ X# E% _+ F+ d( Y# z$ B. E {+ i8 K( [* `) P3 ^; W
index[0] = i;
; m& _+ v- {4 h; A; U8 ?1 O: L for (long j = lSecondLBound; j <= lSecondUBound; j++)6 ^/ O6 D0 C; i7 U2 t7 V
{, l! f3 G6 h; L/ Q
index[1] = j;7 J" R; A- q7 C% E
long lElement = i * sabWrite[1].cElements + j; 1 d7 _( q- N1 `2 M7 i, e* d% M
olesaWrite.PutElement(index, &lElement);
; s6 ^+ \% D- a) s }% |" y% E5 o! }" K Q% q$ G
}7 `% D6 }* u! I
X) h/ S5 l& h# m- k% C /*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/; |+ |; ?0 K0 h/ q6 T- M
VARIANT varWrite = (VARIANT)olesaWrite;
- c& ]- u% ^- F3 [# o range.put_Value2(varWrite);+ |4 `( P- j! [" _& S8 O" \1 i
! A1 T* S3 Z) C0 t: C
system("pause");
. X. Q: | W# f4 o: W p
: ~. ^7 S; Q2 U& U/ t a /*根据文件的后缀名选择保存文件的格式*/
' ^0 G# [. K: U8 {5 q CString strSaveAsName = _T("C:\\new.xlsx");
! `" D1 T: P4 z$ n1 @ CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));
- _5 C3 Z* [$ Q9 r: {0 F XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;' v( e+ Y& t) _0 u
if (0 == strSuffix.CompareNoCase(_T(".xls")))
( R! {( X9 t( H0 I* m. J& a2 b V$ e; o {
$ @# T8 @; [ x/ ~ NewFileFormat = xlExcel8;- |, Y0 X% t4 q0 A% j, w
}
7 d. \4 `* ]. n* r: \8 r& B% ^ book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing,
4 `, k% h) F4 ~% O vtMissing, 0, vtMissing, vtMissing, vtMissing,
7 F1 o w3 j- p3 ]6 p/ g3 X; N: c: D; e+ E vtMissing, vtMissing);6 p% O2 m9 C& q2 W
5 p5 J: o9 B/ }# u" q& b system("pause");
5 c3 K, t6 l- [' g0 P) P! Q7 m8 D( o9 g0 T
/*读取Excel表中的多个单元格的值,在listctrl中显示*/
/ B& h h, z& E/ A6 c f+ ]* j% m VARIANT varRead = range.get_Value2();
% F% O3 E2 Q' z. n2 x: l COleSafeArray olesaRead(varRead);7 q- l4 ?2 i" b) u
1 Z# P" M/ S* ?3 u$ f- S- j VARIANT varItem;5 q# `4 K( ~! L5 o) ]
CString strItem;
$ A) g6 |5 |, J* x6 _; f lFirstLBound = 0;' Z; Q' l* z" b2 Z
lFirstUBound = 0;
6 K0 w/ e1 p0 N8 J) T' Z# L9 l: O lSecondLBound = 0;9 |! c" ?% F' a# R
lSecondUBound = 0;3 ?- e, I% U6 K* s C4 n6 P
olesaRead.GetLBound(1, &lFirstLBound);
# R/ x# i2 A, F& v olesaRead.GetUBound(1, &lFirstUBound);
4 B; i" B9 D4 C& F8 Z7 s6 V1 s, D. O olesaRead.GetLBound(2, &lSecondLBound);
+ n! @9 z& u) {1 c; Y# j9 i- h7 C olesaRead.GetUBound(2, &lSecondUBound);0 T9 O' y* M# l2 C
memset(index, 0, 2 * sizeof(long));
2 G; A; l8 V- `) r& T m_ListCtrl.InsertColumn(0, _T(""), 0, 100);; |$ V: X/ i4 a
for (long j = lSecondLBound; j<= lSecondUBound; j++)
8 @' u+ _6 z1 J* {3 O$ w {/ r! c4 C# [* Y6 f/ ^2 V2 E6 l
CString strColName = _T("");% J# j9 ^. U' t6 z# [
strColName.Format(_T("%d"), j);. y# v6 w( }) d9 s( w8 R3 W2 m
m_ListCtrl.InsertColumn(j, strColName, 0, 100);
8 i# E$ J0 a0 c9 g+ \' u }
8 U3 x' z9 r: E9 T$ P. ]: _: v2 W for (long i = lFirstLBound; i <= lFirstUBound; i++)8 ?" _& p9 o* m$ }8 T2 g1 |
{
# }' a* x8 p) G. H/ [' z; u CString strRowName = _T("");
. F4 Q$ T! q- P; j# Y0 H+ ` strRowName.Format(_T("%d"), i);
7 E; M5 C0 l" q1 ^7 G6 O v8 ~* A m_ListCtrl.InsertItem(i-1, strRowName);/ |+ {9 G6 u3 j- v$ N! l* b
' C$ H) ^4 {3 l5 z
index[0] = i;
/ J, y( @$ F& z0 ^ for (long j = lSecondLBound; j <= lSecondUBound; j++)
/ `' T" [$ @3 \3 i& i {
1 }8 [2 X1 t- @1 r& o index[1] = j;
6 H: s) d2 R1 m5 Q2 s olesaRead.GetElement(index, &varItem);
! J6 `1 r: h, }: E. T& \8 I ]6 C, M3 i9 p
switch (varItem.vt)8 Q0 g: I( H Z3 ~
{% _- ?" A9 Z, l- `' J
case VT_R8:# J3 b9 S( l+ d( X. `) h# L
{7 P" D; S1 K# X+ m8 Z
strItem.Format(_T("%d"), (int)varItem.dblVal);1 N5 j; D4 h+ L% k, G$ d9 }4 e
}& X/ D& i! g6 Q ~1 F
( h+ \5 ]( J* V R8 ?. i5 ~0 \. e case VT_BSTR:- U0 L3 P" k& @% S" d; s
{
* l' d, v$ E; ~* t+ H strItem = varItem.bstrVal;
' _' y8 h5 y- o1 B, V }
! R/ B( [) P4 E% W5 S6 I4 \- {
8 C( `. u8 a0 |9 L2 i# b case VT_I4:
0 q6 Z/ t. y5 }( A$ i {
7 Z5 u+ P- s! C3 G9 n. l" o( Q strItem.Format(_T("%ld"), (int)varItem.lVal);
7 l& y8 p$ [: g( \$ @7 ? }) a: z7 j2 ?4 f% r; s# n
" y! s5 `5 Y0 l; Q
default:
9 u3 a, ~" c. u {
7 Y/ T L( A7 Y6 e+ V9 U$ D. E
% b& y0 |' G) {8 i }2 t' a- F+ W& E+ R
}4 i) b! ~7 r; y* s8 \! ^8 {) q
: a' v! U2 V# W; ]5 u& z0 f2 E m_ListCtrl.SetItemText(i-1, j, strItem);# y: V+ S' x _& h+ Y1 o$ O
}
' G( p" q- N. Z& q. u }/ X: _" o7 h( B; Z
# p2 l# Z; d9 p" ^# ]6 E3 @, W
! i$ O% m8 { \" @: B5 O
' l+ I& O0 O, K- j) g$ K /*释放资源*/; {) z& V5 r# A4 p8 a$ s
sheet.ReleaseDispatch();
7 Y6 M/ v8 h4 h' ? B/ x- | sheets.ReleaseDispatch();
! j# l# F: X2 F- V" w, a4 l book.ReleaseDispatch();$ E, }0 Q: i H" r2 F6 \' t
books.ReleaseDispatch();, _/ v' u: G+ W% U3 {) n& J) V
ExcelApp.Quit();
]$ h( K; ^# M1 A$ V ExcelApp.ReleaseDispatch();[url=][/url]
% ?; a& y& ^3 a( x
" k- k3 X9 n4 A |
|