|
|
请使用QQ关联注册PLM之家,学习更多关于内容,更多精彩原创视频供你学习!
您需要 登录 才可以下载或查看,没有账号?注册
×
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。
+ P: W1 Q. t1 C# z1 s) h1 l1 i' J; J5 Q) h! K+ I/ x% P
1 P5 `9 k* E: N* K3 s
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; }' q1 ^) C# Z3 J R4 B% 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++类对接口的封装。 # q8 `% z- Z% d% d3 u* L: S* D9 g
本文所导入的接口对应的类和头文件的说明如下所示:
4 _1 }+ ?) }2 v0 V- |& n* ~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 | 一定数量的单元格,可对单元格进行单个或多个单元格进行操作。 |
3 u" R6 k( N: A: C% t
$ n G; X1 U# {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的相关库。 更改后的导入类型库的代码如下:
0 z# {1 P0 I# @7 y$ k2 i! I7 h/*导入Office的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \ rename("RGB", "MSORGB") \ rename("DocumentProperties", "MSODocumentProperties") using namespace Office; & y) w* H, w5 o: m+ _- Z
/*导入VB的类型库*/ #import "C: \\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" using namespace VBIDE; ! m% F z$ X. d. B* ^9 N" j
/*导入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;
, a* f7 F7 r. B" E% E编译程序后,会在Deb UG或Release目录下生成三个文件mso.tlh、vbe6ext.tlh和excel.tlh。通过打开文件可知,该三个文件的命名空间分别是Office、VBIDE和Excel。导入了Excel的整个类型库后,就可以使用Excel中的所有类型了。
# d5 k/ I4 t4 i) \4、操作Excel步骤 操作Excel的主要步骤如下: (1)创建一个Excel应用程序。 (2)得到Workbook的容器。 (3)打开一个Workbook或者创建一个Workbook。 (4)得到Workbook中的Worksheet的容器。 (5)打开一个Worksheet或者创建一个WorkSheet。 (6)通过Range对WorkSheet中的单元格进行读写操作。 (7)保存Excel。 (8)释放资源。
& X- d! [6 A- }' w& `5、批量处理Excel表格 VC通过OLE/COM操作Excel,是通过进程间的组件技术。因此,每次读写Excel中的单元格时,都要进行进程间的切换。当数据量大,如果一个单元格一个单元格的读取,主要的时间都花费在进程切换中。因此读取多个单元格,将可有效的提高程序的运行效率。 对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。 VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。 其中,VARIANT中实现二维数据的方法可参考 当然,在对CRange类进行操作之前,要设置CRange类对应的单元格。
5 f7 |, n2 C) P" H3 j" J, T6、Excel表格的保存 (1)如果要保存打开的工作簿,使用CWorkbook类的Save函数就可以保存工作簿,原文件将被覆盖。 (2)如果是新创建的工作簿,或者是要另存为,可使用CWorkbook类的SaveAs函数。 SaveAs的参数比较多。其中,第1个参数是设置要保存文件的路径;第2个参数是设置文件的格式,可在MSDN中查看枚举类型XlFileFormat来了解Excel的文件格式。经过测试,在本文所用的测试环境中,Excel2003的文件格式是xlExcel8,Excel2007的文件格式是xlExcel4。 ( y8 m3 C+ o4 }3 Y) h# e, ~9 ~; b
7、获取当前Excel的版本 可以通过CApplication的get_Version函数来获得Excel的版本,其中,Excel2007的主版本号是12,Excel2003的主版本号是11。
+ F! _6 ]' u5 ~) C m_LisTCtrl.SetExtendedStyle(LVS_REPORT | LVS_EX_FULLROWSELECT);8 K) c4 O& p; ]# ?9 Q
4 i# Z; l" z. a K CApplication ExcelApp;
. u4 O; E8 f- y5 y CWorkbooks books;/ q" a9 J$ m/ {- @+ a0 |& d7 [
CWorkbook book;
( y! b+ e9 \+ |! y2 w* f: k CWorksheets sheets;
9 j; o) B/ v% T0 i5 o/ _7 O CWorksheet sheet;1 L9 A8 _9 p" k" w. I& k: _5 t
CRange range;
$ }8 K' w: S: t LPDISPATCH lpDisp = NULL;2 P# N9 E% C9 ~4 _" z
- a. R3 a9 n9 I& e- a# C0 o* I
//创建Excel 服务器(启动Excel)
/ j+ K4 {1 `* N+ c4 k, s if(!ExcelApp.CreateDispatch(_T("Excel.Application"),NULL))( G* a* h" s$ @( ]6 Q. [
{# s$ s# M) } d- C+ l5 P
AfxMessageBox(_T("启动Excel服务器失败!"));! X# F/ g2 M& P
return -1;
8 x5 K9 u3 H! P& z4 o2 [ U8 f }: F+ e: L; ^. O
6 N) Q$ C, w( x/ l( X /*判断当前Excel的版本*/
3 H- Y9 ^, f; k3 t7 y1 @ CString strExcelVersion = ExcelApp.get_Version();
, t- V( p2 v1 s, j0 g7 c& `0 J int iStart = 0;
8 \ p" x' x) P( O* C& F5 [ strExcelVersion = strExcelVersion.Tokenize(_T("."), iStart);
% o7 ~# D$ G$ B. d if (_T("11") == strExcelVersion) |$ \+ @6 \' q% a
{! s9 t! ~4 J! Y2 ~0 @
AfxMessageBox(_T("当前Excel的版本是2003。"));$ A S1 j0 E% x# y, V2 s, ?
}3 D- R: R) c4 O4 g
else if (_T("12") == strExcelVersion)4 i6 j- V% _3 z7 M5 W
{1 b4 g; O0 y1 X9 S
AfxMessageBox(_T("当前Excel的版本是2007。"));
: y7 U$ E# _1 B+ I }0 v5 W# @. r6 N
else
. W( l; A8 j4 s: L8 c {# |) p% d# c3 A7 ]' }' D! p1 v
AfxMessageBox(_T("当前Excel的版本是其他版本。"));
7 H6 y1 b+ a/ C" V }
6 g) U* `6 `) |: v6 j9 `* J
0 T" j8 f/ F' B- y+ b9 @ ExcelApp.put_Visible(TRUE);- I3 S* j2 E5 m" g9 f9 x
ExcelApp.put_UserControl(FALSE);
8 {5 _( ~8 h/ i4 Y; T& |
% C% k0 n# l4 p. e; ] /*得到工作簿容器*/
, M, e& a$ n9 {) R books.AttachDispatch(ExcelApp.get_Workbooks());
0 S# K+ c; R/ n \) ]% }+ L0 a5 \
/*打开一个工作簿,如不存在,则新增一个工作簿*/2 E& a0 g# E' U. w0 z
CString strBookPath = _T("C:\\tmp.xls");! s2 T1 M) G; U/ K$ f6 u# B q
try
5 ]' G, i4 a4 }* ?. Z {
; f6 X$ u1 E& \' y) w {3 | /*打开一个工作簿*/
2 O0 |7 C# t2 B: k lpDisp = books.Open(strBookPath,
+ c$ Y! z+ v3 A+ s l3 V0 X6 p vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,+ @( O; @1 o5 X/ |0 {
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing, % @4 U# \, R# f) x8 W1 } {
vtMissing, vtMissing, vtMissing, vtMissing);
4 [9 d% u' v4 H3 Z8 V% V# p9 h book.AttachDispatch(lpDisp);) g4 ?1 x( `" V& `
}# _& h$ [ `" z# Q- S8 K, k
catch(...)" t0 N( d) z8 m9 o5 G
{+ Q1 @2 V/ R: F
/*增加一个新的工作簿*/' Z* k! v+ ]+ e" o- f! L* H5 K
lpDisp = books.Add(vtMissing);! h4 @# H: a z# I( T% L, U
book.AttachDispatch(lpDisp);
! |" U, O) b: z. K7 s0 z5 P }
* H8 N. `# \; f0 F- [1 j
% M& ?+ O$ ]- ~ T! k; h3 J
8 F6 ] p. N5 Y( [ /*得到工作簿中的Sheet的容器*/. s9 B4 `$ c0 y9 ]
sheets.AttachDispatch(book.get_Sheets());
2 M- r F. K% U! z: u, D
# y/ }* B) V9 A1 B /*打开一个Sheet,如不存在,就新增一个Sheet*/
* H/ n, G( @0 z$ d2 u6 Q CString strSheetName = _T("NewSheet");3 U4 _6 S4 Z% g7 I; K) D( H/ Q
try
5 l( X' y) n4 [/ \ {
- d6 t! i4 K8 i0 f1 k* K1 d /*打开一个已有的Sheet*/
. A! Y) M0 e4 t6 C lpDisp = sheets.get_Item(_variant_t(strSheetName));
: h. ^# g" {: x. x sheet.AttachDispatch(lpDisp);! L! O' j( c/ b) g+ K' Y
}
. S/ b1 c5 n1 ^8 v/ R' G6 d) T1 r7 Z, V catch(...)
$ E/ r6 B. N$ _7 f3 d. e {
5 z( K1 C b3 P6 S( b% _4 i /*创建一个新的Sheet*/
* I4 [% m* ^( z' d lpDisp = sheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);/ O: O, k* d$ W0 K s( B5 Q
sheet.AttachDispatch(lpDisp);7 y/ C, a' c" j: R' z" {7 f
sheet.put_Name(strSheetName);) |. x% T, |3 \; |' R# d% A- @- N
}, O* h& t: V. O' u& Q; K
$ q( V7 I, Q) v" V( v
system("pause");3 H1 q& Q; j! B3 {: e- u
" O$ z7 O, @! ?0 |. ^1 I% M0 S /*向Sheet中写入多个单元格,规模为10*10 */ w m0 Z: u% G+ e
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t("J10"));
_% J$ C5 h" [7 I* h. [* Y range.AttachDispatch(lpDisp);$ `$ h* W/ Q+ `
& {' B5 b% O" G1 g. v) z& {3 a2 }5 P VARTYPE vt = VT_I4; /*数组元素的类型,long*/
: @/ a+ T2 _' v9 m4 n& T SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/; P3 ~$ y& s( U# d
sabWrite[0].cElements = 10;
' x$ `% Q1 R$ O/ e% z* d sabWrite[0].lLbound = 0;$ m+ t* D9 _8 M" X' K" \
sabWrite[1].cElements = 10;
?* \* R3 f' u sabWrite[1].lLbound = 0;
" F2 c" y7 ~6 C: I: V x+ S+ u2 j d
COleSafeArray olesaWrite;3 [" `2 ?- Z3 |0 U$ d9 G& ~
olesaWrite.Create(vt, sizeof(sabWrite)/sizeof(SAFEARRAYBOUND), sabWrite);
1 `6 I4 ]7 i3 z1 E
3 F* O/ d5 d8 P) `2 I) m /*通过指向数组的指针来对二维数组的元素进行间接赋值*/8 Z5 ~& C& w1 Y7 E; |# f; _
long (*pArray)[2] = NULL;
& N* E- `; e. [* W& s- R" J5 Z olesaWrite.AccessData((void **)&pArray);
" m& Z2 V. ~3 F- O. z memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
, Z0 R3 x+ S- E
2 `; d4 f ^. v0 e* a /*释放指向数组的指针*/8 G$ \: O6 d9 R$ a2 K
olesaWrite.UnaccessData();9 ^) [: H+ M. H6 l: n1 Y* [
pArray = NULL;" c4 T$ d" Y7 W' v" L- K
* X! ~( [) G( D: l0 o
/*对二维数组的元素进行逐个赋值*/# T* U& d! d. f
long index[2] = {0, 0};6 W8 V& r- F4 D% W' m
long lFirstLBound = 0;
4 O+ [& \) a2 B1 x- J6 f5 k5 R% E5 U long lFirstUBound = 0;7 `0 A i- p2 S* [5 M+ y6 D- j
long lSecondLBound = 0;
* }8 J% a' \) r: o( c long lSecondUBound = 0;' x# }5 i) Z5 X9 ~
olesaWrite.GetLBound(1, &lFirstLBound);
/ V2 C# a% p; h( M! L |# E olesaWrite.GetUBound(1, &lFirstUBound);/ Q% p- n+ f2 q0 l/ o- G
olesaWrite.GetLBound(2, &lSecondLBound);' s( G, s$ Z: B1 y& i, E) j- }
olesaWrite.GetUBound(2, &lSecondUBound);
" K4 L5 n9 Y1 J; E3 S! A for (long i = lFirstLBound; i <= lFirstUBound; i++)2 d5 T# ~" J- m$ ~ m
{
( p- V# V8 X$ n/ J index[0] = i;5 ~- S c# F; m, L+ t+ _" Y0 f1 H7 W
for (long j = lSecondLBound; j <= lSecondUBound; j++)5 R. S+ y* X/ U, W; U+ G
{3 W6 b+ G1 B2 z. `3 O& @
index[1] = j;, N' }9 b9 m! i1 I+ J
long lElement = i * sabWrite[1].cElements + j; 9 U- q; W3 x6 U S' L
olesaWrite.PutElement(index, &lElement);( E+ ?6 N& J" L1 r. D
}
% W" b% }6 u$ q1 D4 [ }; Q& ?! M% E, W9 M- N. r
" Z1 r" P8 o% ~/ j1 ?6 Y4 Y
/*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/
; {- \5 o7 p4 m& g/ m VARIANT varWrite = (VARIANT)olesaWrite;
; t1 T! M9 O3 t& q$ D range.put_Value2(varWrite);% o; p) F4 O& q0 c: Y! ]" s9 @
( p8 e& A' J% ~3 @' s
system("pause");
7 _, C4 ?! m' ^2 U0 z/ C: v5 s! t+ R, u* `. ^
/*根据文件的后缀名选择保存文件的格式*/1 I) Q! b3 R( m8 b
CString strSaveAsName = _T("C:\\new.xlsx");: @" I6 h. V. P) K; c+ e$ B
CString strSuffix = strSaveAsName.Mid(strSaveAsName.ReverseFind(_T('.')));' }4 g8 F& `% z5 l" _# L
XlFileFormat NewFileFormat = xlOpeNXMLWorkbook;
6 ?2 w7 B% }6 U% f2 O if (0 == strSuffix.CompareNoCase(_T(".xls")))
1 S( Q: v* W M! b, J {# ~3 m& R7 ?0 ~# I! e# I9 y( P7 _ |
NewFileFormat = xlExcel8;
$ C6 }4 y* N; [2 h: H }* Y, \- X7 Q; a5 M3 `6 |
book.SaveAs(_variant_t(strSaveAsName), _variant_t((long)NewFileFormat), vtMissing, vtMissing, vtMissing, 4 Q% J5 p! ^7 X; a9 F3 I7 q0 Q' o
vtMissing, 0, vtMissing, vtMissing, vtMissing,
4 f0 T9 D" ]7 \) ]& D2 F) s vtMissing, vtMissing);8 f1 g6 t1 y& p. f- F! O( X8 y# W6 z/ u; D
5 O$ s# c# a0 D9 K7 K
system("pause");
8 P) w* M& `/ p
2 u" t2 K: a0 r5 W! `8 i /*读取Excel表中的多个单元格的值,在listctrl中显示*/8 Y; q& }2 L# D) I4 T# h
VARIANT varRead = range.get_Value2();, a: R* m& o7 O
COleSafeArray olesaRead(varRead);
+ |" [; V9 ]5 L/ k/ W3 Y, V
) f& j% |: y. b; s4 s5 p3 W VARIANT varItem;5 v- c. m! |8 C8 |+ k8 X) O
CString strItem;
5 H3 Z* v+ U; s lFirstLBound = 0;
- Q. A8 t& x b9 x lFirstUBound = 0;
H5 l+ K% X. A2 R! r- N* i lSecondLBound = 0;
: {: v5 G+ ^) R4 J& Q1 v lSecondUBound = 0;6 u( q" T; c7 o. j- z' D
olesaRead.GetLBound(1, &lFirstLBound);
6 U/ O2 w1 S* v5 ~! ] olesaRead.GetUBound(1, &lFirstUBound);0 |9 p; v" J( [$ P9 @
olesaRead.GetLBound(2, &lSecondLBound);3 I* r- c7 g9 B% z6 _- F/ ]
olesaRead.GetUBound(2, &lSecondUBound);
$ t! V) p! ]1 }! O8 c: X memset(index, 0, 2 * sizeof(long));
* @8 k- S" r8 L m_ListCtrl.InsertColumn(0, _T(""), 0, 100);
5 A O# u* o F! H+ O2 e( T for (long j = lSecondLBound; j<= lSecondUBound; j++)0 f- P+ j; B) ~; I( n( h4 m
{
+ K a' `) T5 ]+ ]' g9 w CString strColName = _T("");
9 K/ B; g. }; W/ n* r strColName.Format(_T("%d"), j);% h4 G7 X7 M& H3 y2 t1 K
m_ListCtrl.InsertColumn(j, strColName, 0, 100);0 N* K t( h0 P5 V1 L9 l2 _
}
- n: G* V9 x5 b6 v for (long i = lFirstLBound; i <= lFirstUBound; i++)4 [5 _$ j! p, P' J- j
{( Q" P+ x- E) e# G
CString strRowName = _T("");! S8 R3 |! y8 J" M1 F' o
strRowName.Format(_T("%d"), i);
8 u& |, @! o# a# k% h. N: c m_ListCtrl.InsertItem(i-1, strRowName);( p g2 x* R' q" O% s
# M( W5 b2 Q* n# e* \ u index[0] = i;
, {/ L0 a/ T0 E( p" [+ K+ I$ [6 ? for (long j = lSecondLBound; j <= lSecondUBound; j++)
: y1 t' x9 a+ x6 B# q# G {2 y& E. H- u1 [' t/ c# G
index[1] = j;
/ b( A6 B. _% u& z olesaRead.GetElement(index, &varItem);
4 w; J) e% F7 {/ x% B2 Y! K( R- q) w, @+ A" Z! r- k& j
switch (varItem.vt)8 G! u, L. E% P$ V, ~
{
4 u& p2 e' c! m case VT_R8:
2 c5 `. U5 C8 T6 i {5 m* W% {' h+ P" e. _
strItem.Format(_T("%d"), (int)varItem.dblVal);
/ Y7 J" W( I+ r- X, h+ P }
% @9 ?% {9 \: G: z# V$ D, S
3 ]0 f* s$ B* Q$ U" p case VT_BSTR:# i; H2 f* {: L% u: g8 ~& ^
{
7 b. g' Q6 R8 i1 H: ? strItem = varItem.bstrVal;8 z- Z3 w4 {$ o/ c; J: w
}, ~! S3 u) |7 a) O8 O8 M
2 ^6 p; u5 l3 y/ i9 G; B
case VT_I4:
% V' k) v* D$ `, A9 V {
, ^) k6 D7 n' Q/ X: L strItem.Format(_T("%ld"), (int)varItem.lVal);
! v/ M3 t: e4 [" @" h }
) v2 }2 S! r& S# ~+ e
, i% h7 D! C' C; ?' o default:
5 e, T. m) Z# _$ x# r {
) F( D+ d" I# _' z; v
% Q2 K) V3 M/ `- g% V0 L }
+ z: j. `5 [0 k% q }
/ p1 @2 C3 r/ J6 |3 z U" t$ e3 m; B
m_ListCtrl.SetItemText(i-1, j, strItem);# M1 j8 M: ^3 j. }% [9 D. N
}0 Y6 T' j \* a# u9 ^
}
/ h C4 J( {$ D: r3 M# l& R8 I! P& X& S' w4 v
. z0 `9 }5 J: H7 S/ S' t" s
9 }9 L. H2 Y; b4 P$ ^7 i
/*释放资源*/6 q" F; {2 J3 m( u% F: t
sheet.ReleaseDispatch();( Q7 N/ K( }1 L9 w7 l T
sheets.ReleaseDispatch();
& |% e/ C0 F0 E0 A book.ReleaseDispatch();- `- {0 r8 u8 p- w
books.ReleaseDispatch();
$ L& Z# B, S, f3 |* o1 }, ? ExcelApp.Quit();
5 F7 Y5 t- H2 J- r( D ExcelApp.ReleaseDispatch();[url=][/url]
" J. `! H1 C: h" Y8 Y7 d, k* M5 f% A9 u4 H e
|
|