|
|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
×
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。- o6 P h2 t+ s# K9 p- |
' G' x, G1 q! ^6 h, x) A8 R [
. |$ M$ u" ^1 t% D) f v- W0 @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; }: f1 c4 Y' p8 D8 I. x( S
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++类对接口的封装。
! }1 h: W9 i d9 o. j本文所导入的接口对应的类和头文件的说明如下所示: 5 M% K1 D2 A; a' T" W
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 | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 |
$ J: {0 J+ J5 M+ {
, P. x( i3 B' A* T% V) A3、导入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 P$ H l2 I7 R& F: _/ }, f/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office; * u8 H0 g$ \2 v
/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE;
( [5 w1 v, ?" B% |! H/*导入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; ! g7 C7 A& c! c2 T: s& O2 e! o
编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。
4 R( T2 p$ x0 P- N; K$ x% h4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。 ! h2 T& m5 ], F
5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。
( ?9 q9 S( K4 G6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。
& k6 P+ ]% s7 F% H7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。 5 I, e! v" F5 G. B ~9 E
m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);+ N. N( q. A" ^8 L& X! a
& y8 ` p1 ~3 m6 f% z2 t
CApplication ExcelApp;
2 c& w6 }: m, D- Z) W CWorkbooks books;( |. A) Z7 s: r& `
CWorkbook book;
5 T8 Z6 ~+ \/ @6 U& [5 X4 r CWorksheets sheets;
" L- c! ^$ s) }$ }- k# A% o CWorksheet sheet;
7 Y6 g5 f4 w, R% H4 [3 v CRange range;6 A8 ~) l3 g5 r2 h5 p# E
LPDISPATCH lpDisp = NULL;) `* d. z6 y' @/ @$ c
' i7 Y6 \+ z+ v) z: G1 ~- \% k
//创建Excel 服务器(启动Excel)
- V) ]& `; T( q4 `+ Q if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))
K( N8 Y6 C* t2 H4 E! g D5 A {/ j7 w7 V- N' _/ p( _4 K
AfxMessageBox(_T("启动Excel服务器失败!"));
' ?& j( O( f+ ?8 x/ { return -1;0 E; `% W( k$ ]
}( L4 F2 F, G9 \( |8 |
' J0 E; ?+ }8 F) [; { /*判断当前Excel的版本*/
, i: z3 p9 U" |( j) \: K( ^5 d! C CString strExcelVersion = ExcelApp.get_Version();
; ~6 c2 l! [/ A3 d2 j int iStart = 0;
6 y' N$ q% ^) N; D strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);8 n: ~8 r7 v/ M
if (_T("11") == strExcelVersion)+ F2 r; [/ I) u+ j& _ k
{
( g3 c( b% r3 x6 P0 e u AfxMessageBox(_T("当前Excel的版本是2003。"));
6 n& o: i+ T9 {" z! ] }
. f S" G k2 S) X else if (_T("12") == strExcelVersion)# V- t) e W% n' A, v! H
{3 J& e# Z+ A* }: i- v& f, m, {3 y
AfxMessageBox(_T("当前Excel的版本是2007。"));
P( {; E3 D! w% A: h }
+ W, A# ^& O5 G& o* w: ~ else
2 l7 Y: M, f1 B6 ?" Y {) X) G( q6 B0 K" l8 C3 C
AfxMessageBox(_T("当前Excel的版本是其他版本。"));
8 C+ G# B2 M: q. U r; u }) M) t* }2 j) s# i* V/ N
5 a! W `' [ z6 S: z8 S7 k ExcelApp.put_Visible(TRUE);
/ {9 {; t8 h* ]/ f: d+ ^# c" b ExcelApp.put_UserControl(FALSE);
2 A. n% _; q# j( ]- @
- e' c" G) r& a7 a9 v) t /*得到工作簿容器*/+ T7 d' ~ \9 {/ [
books.AttachDispatch(ExcelApp.get_Workbooks());- d; r# {2 d% r" N9 q3 v% t( G1 l" y" _
$ Y. t; d8 d' @$ `3 g7 y
/*打开一个工作簿,如不存在,则新增一个工作簿*/
, Z, u: w z* X' l CString strBookPath = _T("C:\\tmp.xls");" |6 g/ n) M7 c+ b9 d2 F9 n0 a
try- K9 V' w% R7 O0 u" ]4 Z. p
{) N6 @3 Q# p: ^
/*打开一个工作簿*/
( K' c1 U7 @8 ~) q2 R( E+ x( M lpDisp = books.Open(strBookPath, / X1 \4 y1 X# K0 w* k- W
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
6 d( ~* }/ [+ ^( | vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
; `) b+ X* Q) g% f vtMissing, vtMissing, vtMissing, vtMissing);$ U, e" R) s) t
book.AttachDispatch(lpDisp);$ X3 U" Y [, p7 a4 @4 t
}
8 f4 e# P* q$ [# S2 [ catch(...)0 X- M. ]3 F( a$ H t8 e, ^1 N
{- W5 n# T2 z- C( Z' H- a
/*增加一个新的工作簿*/
1 G: R( C- s R* L3 p lpDisp = books.Add(vtMissing);3 Z5 x- x7 J( @- D+ v# k$ {* B
book.AttachDispatch(lpDisp);- q* }: m2 o& {1 S
}
1 @, M0 f3 x" p! B5 k9 W
# ~! g4 R0 z2 p5 J5 w7 d& {6 h1 h6 }! P8 R$ _2 K
/*得到工作簿中的Sheet的容器*/
% _- \: O# \2 W' F' E sheets.AttachDispatch(book.get_Sheets());$ Q, J8 Q3 N. ^9 U$ A) o
8 f" Z j& S9 j3 ?: |# ]* ^
/*打开一个Sheet,如不存在,就新增一个Sheet*/
2 d2 W/ S2 ^ ?: t& x: f# K CString strSheetName = _T("NewSheet");* ] k( w2 c$ B! C2 W, Z4 s: n K v, b
try S# g6 R) C# f, h! Z& ^3 P
{
: D1 ^7 l, w# E4 }: x /*打开一个已有的Sheet*/' z& T' j. `: |# Z/ V
lpDisp = sheets.get_Item(_variant_t(strSheetName));3 Q) E0 k; J- E' X
sheet.AttachDispatch(lpDisp);/ T; D/ @3 p4 w! k @3 a |0 s
} b+ h2 [# ]+ a* s# x
catch(...)
" m( v1 \) P& `9 p3 b4 ~' J! B4 S) M! O+ r {/ m9 ~& i$ ?- D! |4 C+ _" B
/*创建一个新的Sheet*/& w1 X8 o1 Q" ?" @, k8 o! i/ u' _
lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
- `- T3 D8 j. a8 W& P" ]; c, J( A8 I sheet.AttachDispatch(lpDisp);
% k- K: b6 ~" R0 w" U sheet.put_Name(strSheetName);' k5 X8 K( `; ^0 H* Q$ s
}
0 g& K9 U( O4 I+ R7 o4 Q. w
0 R, V5 o, j6 r* r/ \ system("pause");/ N1 |. t: ~. F% f: F
( Z; i. T) K- ~
/*向Sheet中写入多个单元格,规模为10*10 */: k) n! |5 f6 C; e
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));8 }/ q' n8 N i: O3 I' w: E# c" _" n
range.AttachDispatch(lpDisp);
) \: ~ H+ w) E. O; D
6 x% G; I6 X5 { VARTYPE vt = VT_I4; /*数组元素的类型,long*/
: F9 d5 G' c0 M+ p SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*// ]8 j/ V+ J& d( o& E4 N5 l
sabWrite[0].cElements = 10;$ o: ~- P7 p2 Y" {' f) ]1 D
sabWrite[0].lLbound = 0;
; a5 V, R( h) S/ Y( _ sabWrite[1].cElements = 10;
; [9 g) a; t# ]0 K& }) ? sabWrite[1].lLbound = 0;
; I& [. T0 ~. C! c# C8 a4 L# x9 j, s) ~% n6 [
COleSafeArray olesaWrite;
% r, K3 R# W( n5 x( V# q+ g; W9 x4 Q olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);3 l% L( I2 G) A/ F% l* Q; k
- E: u9 l, l" z4 |4 g /*通过指向数组的指针来对二维数组的元素进行间接赋值*/ q% m+ w6 v3 }; L! c
long (*pArray)[2] = NULL;+ F& T/ B# j/ n) z! b0 I& }) t3 ^
olesaWrite.AccessData((void **)&pArray);6 ?$ j7 C, W6 }
memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
7 W$ V8 x9 u7 |0 _' `6 o# W+ F( n k0 ?, ]$ ]" a
/*释放指向数组的指针*/
; D0 ?1 y% j8 |2 @3 f; `' c' q, ? olesaWrite.UnaccessData();
; N# S6 v+ b8 r5 {" Y pArray = NULL;
7 r; Y4 ~' D$ g# ?7 \! d* F i
/*对二维数组的元素进行逐个赋值*/' n, Y( U2 i. d7 C0 C( r/ K6 ^
long index[2] = {0, 0};4 `+ v ^. T/ P
long lFirstLBound = 0;
5 `! k; ^* n; }0 I5 R. o# M long lFirstUBound = 0;
2 L; p# |- ?3 r9 e! `1 E' o long lSecondLBound = 0;
, _( S% V2 X2 H' y long lSecondUBound = 0;
. \2 [& d4 y+ r: I5 \ olesaWrite.GetLBound(1, &lFirstLBound);- w4 T1 y3 i/ g% w ?, u4 [1 [
olesaWrite.GetUBound(1, &lFirstUBound);# y0 w9 m& b9 c
olesaWrite.GetLBound(2, &lSecondLBound);
% g. `2 }$ S7 ~" u5 c olesaWrite.GetUBound(2, &lSecondUBound);9 W5 i1 Q- L n- R+ g: O
for (long i = lFirstLBound; i <= lFirstUBound; i++)8 E' k. F6 X" C
{5 W7 L; P* n( ]9 ~8 v) L
index[0] = i;: T; { y5 j7 H% s s* ~
for (long j = lSecondLBound; j <= lSecondUBound; j++)8 B- C) z1 ?% R! a& o8 j
{3 A/ B; E2 P- @" n# d( X" w
index[1] = j;
7 z" T; M$ V/ A- U/ h, n long lElement = i * sabWrite[1].cElements + j;
3 Q0 V# X3 T0 n+ ` olesaWrite.PutElement(index, &lElement);
' F5 m% [$ [$ w0 V) i; q } p( J+ t' E4 i$ m: Z
}8 \, q9 Z( j0 N+ t! J
# J- w1 W! a* F0 }9 I /*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/. d8 J4 q3 K+ S( A7 p0 x, n3 |3 F
VARIANT varWrite = (VARIANT)olesaWrite;
) u8 P. E& V* a1 A range.put_Value2(varWrite);. x6 \2 g7 S* l* J1 M8 X5 u
8 ?& y' g' t- U$ x: L% J system("pause");
/ I0 y7 P+ `! M' Q; B* o. e
) q* h7 N$ I6 h6 w+ L8 d /*根据文件的后缀名选择保存文件的格式*/
1 G# |& c$ D1 f) @* y5 T CString strSaveAsName = _T("C:\\new.xlsx");
$ m q& S* d5 {# B" a CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));
& i+ N' s$ V3 j/ L* X* g g XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;5 i: S2 D9 ]; I6 B
if (0 == strSuffix.CompareNoCase(_T(".xls")))
5 i8 S! ~, P9 w4 t" y, }. x6 R* t9 Q {
# E) r1 v; c' a Z- j9 ~ NewFileFormat = xlExcel8;
& O4 K! S; E% D, i# k7 |5 w5 m5 o8 N7 r }
/ O" [2 v6 x$ g5 M book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing, w |. q1 w$ I9 F
vtMissing, 0, vtMissing, vtMissing, vtMissing, 9 j5 a8 [3 k e4 ^7 x
vtMissing, vtMissing);
& o2 ?- S! ?* X j: L, r9 ]; v6 [) J1 |
system("pause");3 r' {/ L) ~) j! Z+ k* Q
/ c0 G" |1 v. g* T& e2 a1 k7 w8 p: o5 H
/*读取Excel表中的多个单元格的值,在listctrl中显示*/+ |) R6 b4 t S) ]; u
VARIANT varRead = range.get_Value2();
4 o% [4 f; {" ` COleSafeArray olesaRead(varRead);) k* U7 ]6 n- q3 k4 U* Q
1 s* ?) t3 v/ v' |% m( r @
VARIANT varItem;
- {* D, E U9 ^, u5 G CString strItem;
, `: U7 P. ]8 M' H/ T; X8 y8 S lFirstLBound = 0;: d4 Q/ y# P5 [
lFirstUBound = 0;
+ h* Y. e1 O# T0 M1 F lSecondLBound = 0;0 u% p8 {# o0 [3 ?7 h. b6 Z, }
lSecondUBound = 0;
* T, G; n( T; I- A1 X olesaRead.GetLBound(1, &lFirstLBound);
5 E, R( P+ |: I olesaRead.GetUBound(1, &lFirstUBound);! N8 \ Y5 m) s4 N1 D; k" j) [& V
olesaRead.GetLBound(2, &lSecondLBound);
3 O+ |# z1 b& p8 T olesaRead.GetUBound(2, &lSecondUBound);
/ C& v A1 a' D ?8 K memset(index, 0, 2 * sizeof(long));4 \" o% Q; B+ L/ f
m_ListCtrl.InsertColumn(0, _T(""), 0, 100);
8 m" t+ n% \( I. f0 B+ X for (long j = lSecondLBound; j<= lSecondUBound; j++)& ]' P# r+ l& h8 C; l$ t$ A! T% N
{( h$ w c( g' [7 E J, g& A
CString strColName = _T("");
[% O, V% h/ \' m strColName.Format(_T("%d"), j);5 q: f8 m" I$ ^6 W
m_ListCtrl.InsertColumn(j, strColName, 0, 100);
+ n, j9 O2 o; w& f) A1 \: b }
* N0 B$ |! v( K" h7 h8 m for (long i = lFirstLBound; i <= lFirstUBound; i++)# b* d! g) Q+ G: V1 i! _8 B
{1 m2 p0 @4 \0 y. y. N1 k* t# m
CString strRowName = _T("");7 {: ^7 [5 P' `; L
strRowName.Format(_T("%d"), i);
- [; y* I! j7 s0 D4 o Y m_ListCtrl.InsertItem(i-1, strRowName);* T1 h/ S6 ]0 p( s( V p8 p4 @- g
8 y9 o) S j! H# z) Z
index[0] = i;
0 F1 l/ a6 Y1 `7 g- i for (long j = lSecondLBound; j <= lSecondUBound; j++): J% L6 ]1 W4 @) W3 h Z* |
{
' G' R2 w- n7 e" n, k index[1] = j;. x6 ^% K2 R, Z5 Q9 {, R6 R* b8 M1 E
olesaRead.GetElement(index, &varItem);4 i; r: J4 W! @0 t
# J# i) m/ a! X. }! U" T% Y& g switch (varItem.vt)
% t* S$ z: l5 f- v3 M! n a {
: F% d8 `; e$ H2 D, [( C case VT_R8:
, R3 V" Y, \( D {
0 A( q: M' z' Z( u4 t3 @ strItem.Format(_T("%d"), (int)varItem.dblVal);
- ~6 K5 K/ O8 t, E7 D- P0 L }
1 u# W# c* p4 Q2 @
% d# R- G' \8 L0 Z+ g4 | case VT_BSTR:
7 l$ _/ c( |) U$ W! ?5 y- z { `& X8 h7 a+ n$ L* j
strItem = varItem.bstrVal;
3 V/ [) A8 D ]& A4 p1 n }$ E; e0 { e0 P2 ^6 }5 @, x
, ^5 M' Y4 N; L8 k; W case VT_I4:
* S7 d; m( B1 H" d6 p5 e% ]0 ?: } {5 s) G8 L- H& X% L; |
strItem.Format(_T("%ld"), (int)varItem.lVal);
+ t& G" S; T, a4 W, {, |$ j H- a }8 R* f2 X0 F- q3 H! U1 z/ y
* |4 K! K3 n0 ]% p4 ^
default:0 S s x: e0 s G4 L/ C: e, e! ?3 H
{5 m( n: [: v4 K, J
0 Y3 s) g$ ~6 d1 r, A
}9 c3 u8 e9 t' Q- E0 j% J
}" j1 I" Z J9 O" T6 Q$ B; o
9 [$ r& R ^; N8 E+ G7 B
m_ListCtrl.SetItemText(i-1, j, strItem);
( _, N9 v# i! H, d9 X- h }! ~: r. p) N5 D! }* M' r
}2 Z7 u! w: k* k& B
$ ?( W# |; T+ y$ k. }( d0 E4 C) C& Y1 f2 u
" Q+ u A! H- G9 x /*释放资源*/
/ L4 S% x7 C+ a9 P& Q sheet.ReleaseDispatch();8 k I5 X" b" f8 i4 d" Z
sheets.ReleaseDispatch();2 p/ ~3 q' i2 D0 z% Y% ~* J3 [
book.ReleaseDispatch();
: H$ L0 A& @: X books.ReleaseDispatch();
4 B& ^1 l v. f, w ExcelApp.Quit();9 T8 Q) ?0 W& K! c' @4 y. w
ExcelApp.ReleaseDispatch();[url=][/url]
/ e3 d4 D. e0 @0 o
2 q. ?3 w8 Q; T: K! @ n' |; z" ~+ X |
|