Chinaunix首页 | 论坛 | 博客
  • 博客访问: 205618
  • 博文数量: 264
  • 博客积分: 6010
  • 博客等级: 准将
  • 技术积分: 2740
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-03 13:25
文章分类

全部博文(264)

文章存档

2011年(1)

2009年(263)

我的朋友

分类: C/C++

2009-06-04 13:37:11

在使用 VC、 VB、 Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置 ODBC数据源。对于一般用户而言,配置 ODBC数据源可能是一件比较困难的工作。而且,在实际应用中,用户往往要求在同一个应用程序中访问不同的数据源,因此采用一般的加载方法就有了无法克服 的缺陷。为能在程序中完成这一工作,方便应用程序的使用,本文以 VC为开发环境介绍两种在应用程序中动态加载 ODBC系统数据源的方法。 % H) r$ m! U0 I, ?( D
# u0 M& K  o# c& f) m$ o
方法一:修改注册表 - t: h. e1 d+ s, b" |; ^

% {; [6 L2 R+ r( z% d) Q设计思路
5 P6 W8 h* r' y% `# C
2 A  ?  l1 g! b5 J6 X6 v一般情况下,当用户在控制 面板中配置好 ODBC数据源后, Windows系统便在注册表中加入了一些子键来存储用户的配置结果。当应用程序需要用到数据源时, Windows便会通知底层接口查阅注册表中该数据源的配置。如果用户删除了某个 ODBC数据源,那么也会在注册表中有所反应。如果配置的数据源是用户数据源, Windows系统便会修改注册表的 HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI子键;如果配置的数据源是系统数据源, Windows系统便会修改注册表的 HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.
$ h; d0 n8 m0 G& T0 H' l( B- X) ]6 @7 T# P! o: m) z$ w
INI主键。因此,我们可以在应用程序中使用 Windows API中的注册表编辑函数来完成 Windows所做的工作,这样就可以达到动态加载数据源的目的。具体实现对于不同类型的数据源,注册表的修改也各有不同,但基本上都要修改两个地方。一 个是在 ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在 \ODBC.INI\ODBC Data Sources子键下建立一个新项以便告诉驱动程序管理器 ODBC数据源的类型。下面以配置一个 Microsoft Access数据源为例给出实现此功能的函数的代码。 ( C& N  K( D6 T% |1 m* h
" }8 y  q+ Z6 k
/* strSourceName是要创建的数据源名, strSourceDb是数据库存放路径, strDescription是数据源的描述字符串。* / " z7 Z& E. g. t

3 \& b! u$ p  GBOOL CLoadOdbcDlg:: LoadDbSource(CString strSourceName,CString strSourceDb, CString strDescription) ' F" K5 m4 U* W6 L# T( j

9 z% K5 G4 J" q" L& u{ ( F. x- k. C6 z8 U4 X
1 I+ t! d/ R" R$ {; t. w* V
//存放打开的注册表键 ' c& F2 R+ [4 f# F- a2 ~9 X$ t

7 y3 ~6 S2 k" x. A/ r1 r  YHKEY hKey;
# X: W* ]7 j' Z7 A! L/ a& S2 I8 ^, Z# |! ?; M& N
DWORD dw; 9 m& q9 ^3 I7 u7 [

* _8 r8 h6 Q: q( v: P//存放注册表 API函数执行的返回值 ; n/ Q7 ?7 N5 Q/ e, M& @

; |4 o- s9 E% f: z8 kLONG lReturn;
, y# S6 C9 ^& l2 l; o, D& k: R+ J9 e* P# e: ?0 i/ x3 O6 @& V
//存放要打开的子键
) b5 t& W" C& i1 ^& V8 H( f' ?  G* ]
& e  [3 g, L. m- g# `/ yCString strSubKey; ! v4 K- J3 X* M- _+ j3 C+ ~

3 O' n! R3 y# A4 Q3 c, t//检测是否安装了 MS Access ODBC driverdbcjt32.dll
8 c9 R3 L. `5 K) A9 p, G. S; [" |, K
//获得 Windows系统目录
  K) z" h2 L8 P/ G  L* _3 ^$ R
4 ~/ y) |( G3 L0 ]5 F: [) @char sysDir[MAX_PATH]; 1 t0 u2 V) B5 \; C' s' R

! a5 i/ N' x6 ~1 V, R% g) [1 @char drvName[]=" \\odbcjt32.dll" ;
2 ~1 w, C( I" S8 v6 H, c+ ^/ B1 K" s4 t; E) V; w# w
::GetSystemDirectory (sysDir,MAX_PATH); 9 s5 o# k# Q" G

' ^, Q, Y! n" wstrcat(sysDir,drvName);   j- {$ n9 j$ r+ f6 p$ X/ n+ }
( ^0 i) n0 a% O4 |+ \! T' N) M
CFileFind findFile;
; K4 F1 F% l& q
! D" \5 T- {$ R1 R, N4 a( J  i# X6 ?if(!findFile.FindFile (sysDir)) " n, x1 ]  J6 V: h
! \! u4 h" o' y( p2 o* g  [6 }" A8 _. y
{ $ g; F% s5 M5 S6 C1 b/ V+ ?4 H- i

3 `. N) j" H/ N0 Z3 s& z- dAfxMessageBox("您的计算机系统中没有安装 MS Access的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。 " ,MB_OK|MB_ICONSTOP); - P7 l: M5 L' ^5 f! T' K

. i9 c5 j- @& W% E4 Dreturn false;
  y/ v9 e+ m; W, a$ c3 y
- L- W6 q5 u2 E2 d; }9 t$ s} ( w- ~. u9 o, B7 d

9 `6 S. s+ L0 @- E! YstrSubKey=" SOFTWARE\\ODBC\\ODBC.INI\\"+ strSourceName; # b5 R3 W4 s# z! y) z' c+ n

- \$ F# K. r* a6 k- ?& ]; s# x//创建 ODBC数据源在注册表中的子键
7 w9 ?0 F9 A6 u. z+ r8 F* I! r/ l/ \- M& Q' B- x) K4 |
lReturn=::RegCreateKeyEx(HKEY_LOCAL_ ; w- J) F/ b- L/ l: u

5 [2 @' F  X; _$ o. U! b9 a: WMACHINE,(LPCTSTR)strSubKey,0,NULL,REG_OPTION
8 x- m- B* t1 q3 ?1 J! l* b, X. Z/ ]( ~: x/ t
_NON_VOLATILE,KEY_WRITE,NULL,& hKey,& dw);
) ^% N; H6 w2 O: i- x" c( J
1 l+ J. t" [5 A9 V6 w( K! H: Wif(lReturn != ERROR_SUCCESS) # L2 ]1 z  u5 b$ `9 }

4 ], u! t1 K/ v' H. x8 areturn false;
/ S% h) n4 H: ?7 s) N- R% g$ D4 B* Z) Q
//设置数据源的各项参数 ) ]" s/ `* E1 i) K
- J5 D4 L" }/ y; `- {* B
CString strDbq=strSourceDb;
1 v  g1 k! s% O, ?' G4 p- j; ]* l4 G4 A( W# \' J9 _
CString strDriver=sysDir;
, z! ~: F3 U1 w# T8 q. R3 l# y0 s6 i( y2 r8 {- ~
DWORD dwDriverId=25;
' e5 E8 Z# y1 |* j! ~7 ]7 c5 ]9 n, }3 ~7 L7 w% M
CString strFil=" MS Access" ;
1 j/ s( x& `' G! G' x1 v! k* S+ `, z
CString strPwd=strSourceName;
" m3 S/ n+ D$ n1 H' q, c7 {# \( q' }: d% P8 p2 p) J6 k3 C
DWORD dwSafeTransactions=0;
, `+ v  r* W0 y  V1 H8 n9 ]8 o
  A* f: F4 D/ E9 v3 v! aCString strUid=strSourceName; 2 n2 L. J# _/ t' T
! M; L, Y" ^2 ]6 `8 Y) t! r
::RegSetValueEx (hKey," DBQ" ,0L,REG_SZ,
2 }6 K/ S- O' q% W. C
5 |& l, {* ^+ @" w; t; ](CONST BYTE* )((LPCTSTR) strDbq),strDbq .GetLength ()) ; ::RegSetValueEx (hKey," Description" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strDescription),strDescription.GetLength());
3 s+ {8 V, z7 P+ E7 R5 q1 I) H5 t
5 R0 w  a! n0 T6 p/ j% E; f& W+ W, u' L::RegSetValueEx (hKey," Driver" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strDriver),strDriver .GetLength ()); ; F+ a: L" x6 w0 U* F

% r8 O) G& M* H5 q: J5 O) X::RegSetValueEx (hKey," DriverId" ,0L,REG_DWORD,(CONST BYTE* )(& dwDriverId),sizeof(dw));
+ D) m, |/ ]8 ?3 F) K; e
/ o9 w/ s: }; {& E3 P::RegSetValueEx (hKey," FIL" ,0L,REG_SZ,
- a! H7 P' L. u" F6 I/ p- t- L. ]3 J8 R" v1 I& K" t
(CONST BYTE* )((LPCTSTR) strFil),strFil .GetLength ()); 7 r- v0 V- _0 X: \
" \5 J" |/ @9 V$ {7 s
::RegSetValueEx (hKey," PWD" ,0L,REG_SZ,
- A9 B1 z0 u4 |8 d9 r- n" {! B
# a! L* a: F0 \* ~4 u4 W(CONST BYTE* )((LPCTSTR)strPwd),strPwd.GetLength ()) ; ::RegSetValueEx (hKey," SafeTransactions" ,0L, 8 X% j. W3 P% D% j$ ^( _
2 z  y) p- p0 D' S; a, F
REG_DWORD,(CONST BYTE* )(& dwSafeTransactions),sizeof(dw));
+ f& X* }  c3 C5 V3 g( y5 [; P! C7 m" u+ L: K( P
::RegSetValueEx (hKey," UID" ,0L,REG_SZ,
( c3 w. W& J& a, q  R# v
  v9 k5 Q/ x. [1 {* J9 |/ ~(CONST BYTE* )((LPCTSTR)strUid),strUid .GetLength ()); ::RegCloseKey(hKey);   n" R. H9 O2 u( ?) J1 {

9 E$ c5 t" Y' v//创建 ODBC数据源的 Jet子键
/ m' n/ L, V) ~3 `3 N: O
- l, n# w6 t- ~7 }strSubKey+ =" \\Engines\\Jet" ;
) U2 _" V* b6 h0 I$ N0 C0 p: Q
# _# X0 p! U; c# W7 T  o! s3 Q) UlReturn=::RegCreateKeyEx (HKEY_LOCAL_MACHINE ,(LPCTSTR)strSubKey,0,NULL,REG_OPTION_NON_ 4 ^+ e+ y' t6 m% j4 H

7 z1 \$ V, u9 [VOLATILE,KEY_WRITE,NULL,& hKey,& dw);
) ?/ P2 U  k  q9 ]0 ?# r) ~5 \5 m
( n$ Y3 e( L# M( T. D7 i/ u* Iif(lReturn != ERROR_SUCCESS) ' @# @9 [" S7 s# I) w8 E

4 ~+ |6 A8 l9 L7 X+ s8 areturn false; 7 ?) q$ D, t- H& M( A8 _
( u) V# `  F6 [: _+ n
//设置该子键下的各项参数 ! j$ c8 |, t0 H& o6 ]8 v; ^
2 p, F3 \: @- O1 [2 G6 W
CString strImplict=" " ; 9 S1 v. f4 X2 h% C7 p) ?! i  C* q2 f
  p1 F) Z3 i: j5 u; Q7 X/ Z
CString strUserCommit=" Yes" ;
7 V  Z( p& E, O' b- O6 x- P7 o
5 l* [& U/ b% I2 o3 T0 o# wDWORD dwPageTimeout=5;
0 [4 V) [- ~2 m2 Z$ A6 d3 L! {$ Y2 @9 C; e. N0 q
DWORD dwThreads=3; & v7 l3 K1 c5 C- T  N
6 m; t% F% S: D' I$ k; i5 R
DWORD dwMaxBufferSize=2048;
5 \# T2 F0 z' W* `) e% @. Q, _: N! {( ]3 W% x3 H/ o
::RegSetValueEx (hKey," ImplictCommitSync" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strImplict),strImplict.GetLength ()+ 1); 3 M+ F1 g5 L6 n4 ~
8 o% ]. x9 Z& @5 }% S. d# t( _1 G
::RegSetValueEx (hKey," MaxBufferSize" ,0L,REG_DWORD,(CONST BYTE* )(& dwMaxBufferSize),sizeof(dw)); 4 F4 F' b6 X- D( s
0 I- @3 H$ T% l9 t, }, y) c. V
::RegSetValueEx (hKey," PageTimeout" ,0L,REG_DWORD,(CONST BYTE* )(& dwPageTimeout),sizeof(dw)); ) F- g9 l# M) Q6 K; ?

0 U2 v) U% T' S; {% D* K1 M::RegSetValueEx (hKey," Threads" ,0L,REG_DWORD,(CONST BYTE* )(& dwThreads),sizeof(dw));
" N" D* q6 O" p* X: x% w) ]/ U5 P( Y0 W0 E4 D
::RegSetValueEx (hKey," UserCommitSync" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strUserCommit),strUserCommit.GetLength ());
1 K2 d% [3 ]' O1 |, ]3 m1 g9 q4 L# C, P9 x# B5 c7 u  v
::RegCloseKey (hKey); 0 ?! m( y; e$ N5 {
% a9 Q. j  a( @  Q9 L
//设置 ODBC数据库引擎名称 0 H9 K5 F+ [0 y3 {
0 x7 r4 c. q5 D% T: K
lReturn=::RegOpenKeyEx (HKEY_LOCAL_MACHINE, " SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources" ,0L,KEY_WRITE,& hKey); 9 G; ?8 j" ^2 `$ x# v

. J" t/ f: M9 h. w- w/ I4 Nif(lReturn !=ERROR_SUCCESS)
/ [8 [3 i8 N5 T; E8 d  M6 X1 F6 H; F/ g
return false;
. c% m7 a* A: c; z8 z& D( z% x6 r  ~# E2 y* k; M3 j3 k, n
CString strDbType=" Microsoft Access Driver (* .mdb)" ; ::RegSetValueEx (hKey,strSourceName,0L,REG_SZ,(CONST BYTE* )((LCTSTR)strDbType),strDbType.GetLength ()); * D. [! W6 V/ R4 b& c) \% W
, \, _$ R5 j' q- a
return true; 6 }2 K7 Z7 |7 T& Z. C
3 g$ m% W5 v: o+ X
} 8 R* U2 N8 e# |% Q6 W

# r' ~' v3 P0 I3 S. Q; g5 q, D由于在动态加载中,一般只会改变数据库文件、数据源说明以及数据源描述,故上述函数可以实现应用中的大部分要求。如果应用中还需要作更多的改变,那么也可以通过改变函数参数的方式加以实现。对于需要动态加载多种类型数据源的情况,可以用具有不同参数的重载函数去实现。 9 G6 u$ y& G" b( M
5 Z0 m$ M$ Z7 ^4 b: j1 h7 o( C
方法二:利用 DLL
6 j) k! [/ ^  D3 I/ `4 H) [$ D
( J7 y! d4 `3 ?0 ^: d0 V设计思路 % J6 J" W9 g% \  ~# S# l/ F

1 C- j$ a0 m# H$ V  ZWindows系统子目录中的动态链接库 Odbcinst.dll提供了一个可以动态地增加、修改和删除数据源的函数 SQLConfigDataSource()。该函数的原型如下:
5 |& B/ i0 d7 L! |' u9 N! Y% o, C; J
# X0 Y% V! y) ^: ~BOOL SQLConfigDataSource(HWND hwndParent,WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes); 7 G# P# Q. D2 \" X
* @( Z/ F; a" |% q% V5 c
hwndParent参数是父窗口句柄。如果该值为 NULL,将不会显示与父窗口有关的对话框。 : S6 B# _! s, T4 n
9 H8 X0 h7 L: o0 }# R' l
fRequest参数可以设置为下面的数值之一:
9 {) {2 O2 U$ Y/ a! z
2 q$ y2 F# K; A4 Q, w& M8 E/ N; pS ODBC_ADD_DSN:增加一个新的用户数据源; 3 f4 l: y3 }  G( L) R* O& t! Q

8 N6 x! S& P/ l5 @# g) h# t* N  iS ODBC_CONFIG_DSN:修改(配置)一个已经存在的用户数据源; . q# X" e9 E* d# H. F% }5 g: ]
4 y2 j7 W0 t! Y3 a
S ODBC_REMOVE_DSN:删除一个已经存在的用户数据源;   v7 B: P5 s2 j2 R2 \% W

. I; _- b$ j0 gS ODBC_ADD_SYS_DSN:增加一个新的系统数据源; ! l4 @8 b7 p7 d

* I6 h! R; f( A3 V: dS ODBC_CONFIG_SYS_DSN:修改 (配置 )一个已经存在的系统数据源;
( R$ c/ k: V0 {, L
; l9 y8 ?, t# I5 rS ODBC_REMOVE_SYS_DSN:删除一个已经存在的系统数据源。
# ~, G. f' c; |0 U7 @
% ?8 G$ K, K0 P  zlpszDriver参数用于传递数据库引擎的名字,等同于方法一中 strDbType变量。 0 J5 L4 U0 i; I) G/ c4 X+ P" ^1 D8 x
% i2 M# @0 c. U0 h6 b3 U( `. O' o
lpszAttirbutes参数是关键字的值,即一连串的 " keyname=value"字符串,每两个字符串之间用 " \"隔开,如 DSN=Personnel Data\0UID=Smith\0DATABASE=Personnel。关于该参数的详细设置请参阅 MSDN中 SQLConfigDataSource()函数的帮助文档和各种 ODBC驱动程序文档。
3 g" k: P$ f, t( j; T/ x) S$ p) w/ g" ~3 W: r7 Q. u5 w
具体实现
. k5 B+ U6 O7 l9 q; ?# I$ ^( b) r" ~. j# q, t9 P
由于 VC的缺省库文件中不包含 SQLConfigDataSource()函数,因此使用该函数之前需要将 odbcinst.h文件包含在工程的头文件中,在工程的 Settings属性对话框 Link属性页的 Object/library modules编辑框中增加 odbc32.lib,同时保证系统目录 system32下有文件 odbccp32.dll。 . \  i$ Z7 n! U4 n0 h

0 V2 d9 o  j- v- P8 C6 t( C仍以 Microsoft Access为例,设置数据源名为 demo,数据源描述为 "示例数据源 ",那么在需要动态加载数据源的地方加入下列代码即可:
# Y3 B( q# c+ `! j7 l* t) ~; N/ o' a  P
::SQLConfigDataSource (NULL,ODBC_ADD_SYS_DSN," Microsoft Access Driver (* .mdb)"," DSN=demo\0Descirption=示例数据库 " );
. p! E( r5 i# a% ]5 n6 b5 N( p: i# Q6 j+ a: l
小结
( d$ t  [, c) s9 l  K/ \
5 e% w; q, u& @/ T" |6 @, l. M上述 两种方法都可以实现动态加载各种类型的 ODBC数据源,并且在 Windows95/98/NT/2000环境下调试通过。方法一在实现时需要较多的代码,方法二所需代码虽少,但需要额外文件的支持,而且随着数据源配 置的灵活性的增加,为了形成 lpszAttributes字符串,其代码长度也会相应增加。由于从控制面板配置数据源使得程序员可以获得更加直观的理解,所以对于注册表中各项值以及 相应项名称的获得除了可以查阅相关驱动程序的文档外,程序员也可以在编程前先通过控制面板配置 ODBC数据源,然后根据注册表中相应部分的内容进行编程。
阅读(151) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~