用Visual C++程序实现设置ODBC数据源
苏州供电局信息中心
唐一均
---- ODBC(Open Database Conectivity)即开放式数据库互联,作为Windows开放性结构的一个重要部分已经为很多的Windows程序员所熟悉,ODBC的工作依赖于数据库制造商提供的驱动程序,使用ODBC API的时候,Windows的ODBC管理程序,把数据库访问的请求传递给正确的驱动程序,驱动程序再使用SQL语句指示DBMS完成数据库访问工作,因此,ODBC的存在为我们开发应用数据库程序提供了非常强大的能力和灵活性。 ---- 为了使ODBC能与数据库一起工作,必须把数据库注册到ODBC驱动程序管理器,这项工作可以通过定义一个DSN或数据源名字来完成。通常,我们只能手动打开系统控制面板,运行其中的ODBC数据源管理器,手工配置数据源,但是这项工作对用户而言过于复杂,我们必须考虑用程序替用户完成这些配置工作。 ---- 因此许多程序员在发布自己编写的数据库软件时候都希望能有一个优秀的安装程序能够自动设置好ODBC数据源,虽然现在InstallShield等一些优秀的安装制作软件可以帮助我们实现此类功能,但毕竟缺少灵活,程序员不能完全控制它,事实上,我们完全可以自己编写一些程序实现此类功能,实现的方法有几种,一种办法是用程序修改Windows注册表,程序员可以用Windows API函数增改HKEY_LOCAL_MACHINE\Software\ODBC下的ODBC.INI中的键值,这种方法比较烦琐。我现在推荐一种在程序中使用ODBC API的方法,程序员可以在任何时候都可以用Visual C++编写的程序调用这些API函数来设置ODBC数据源。 ---- 下面我用MFC写一个程序来演示如何实现这个功能: ---- 首先,打开Visual C++,在File菜单上选New,然后选定MFC AppWizard(exe)类的项目,Project name我们定为try,按下OK键,下一Step 1屏幕中选Dialog based,由于不必用到后面的选项,此时即可按下Finish键,结果系统将生成一个新的项目。完成上述工作后,在左侧Workspace窗口中,选择ResourceView,打开try resources中的Dialog资源,选择并打开IDD_TRY_DIALOG对话窗口,在Controls菜单窗口中点选按键图标,回到IDD_TRY_DIALOG对话窗口并点击此窗口,将生成一个名叫Button1的按键,选中此按键再按鼠标右键,在弹出式菜单上选Properties选项,在出现的对话框中把Caption项的Button1值改为Setup ODBC,关闭此对话框,再选中此按键按鼠标右键,选择ClassWizard,在出现的对话窗口中,Object Ids选IDC_BUTTON1,Messages中双击BN_CLICKED,此时弹出Add Member Function对话窗,Member function name是OnButton1,按OK键。在Member functions选项中双击onButton1 ON_IDC_BUTTION1:BN_CLICKED,在出现的void CTryDlg::OnButton1()函数中用以下ODBC API函数语句替换 //TODO: Add your control notification handler code here这条注释语句: SQLConfigDataSource(NULL,ODBC_ADD
_SYS_DSN,"Microsoft Access Driver (*.mdb)\0","
DSN=TryDB\0DBQ=D:\\Database\\
try.mdb\0DEFAULTDIR=D:\\DATABASE\0\0");
'---- 您可以根据您不同的设置需要修改上面的语句,SQLConfigDataSource一般有以下几个许可的参数:ODBC_ADD_DSN: 加入一个新的用户数据源,ODBC_CONFIG_DSN: 修改一个存在的用户数据源,ODBC_REMOVE_DSN: 删除一个存在的用户数据源,ODBC_ADD_SYS_DSN: 增加一个新的系统数据源,ODBC_CONFIG_SYS_DSN: 修改一个存在的系统数据源,ODBC_REMOVE_SYS_DSN: 删除一个存在的系统数据源,ODBC_REMOVE_DEFAULT_DSN: 删除省缺的数据源说明部分。需要注意的是,当我们使用SQLConfigDataSource ODBC API函数时必须声明包含系统的odbcinst.h头文件,所以我们再选择workspace窗口中FileView打开Header Files中try.h,在其中加入#include "odbcinst.h",如果不加入这个头文件,系统编译时就会显示undeclared identifier错误,在完成上述步骤后,假如我们立即编译并Link这个项目,会发现出现下面的错误:
tryDlg.obj : error LNK2001: unresolved
external symbol _SQLConfigDataSource@16
Debug/try.exe : fatal error LNK1120:
1 unresolved externals---- 有些人可能因为找不出错误而放弃了,其实这是因为当我们使用SQLConfigDataSource 这个API函数时候必须用到odbccp32.dll,它是Microsoft提供的32位ODBC安装和管理的DLL,如果是16位必须用到odbcinst.dll,odbccp32.dll有一个import library,所以解决的办法就是把这个odbccp32.lib加到我们的项目中,我们可以打开Project系统菜单项,选Add to Project子菜单,在其中选Files项,打开VC安装目录下的\vc\lib\目录,文件类型选Library Files(.lib), 选择其中Odbccp32.lib后按OK键,然后重新编译即可通过,运行这个程序,将弹出对话窗,按下Setup ODBC按键,之后,您就可以通过控制面板的ODBC数据源管理器或注册表查看运行结果,您会发现,您的数据库已经成功的注册了。
解决方案 »
- 使用MFC编程,创建一个工具栏,想让它隐藏,应该怎么做
- 动态加载toolbar的问题?
- 一个关于字符串的问题,请高手们指点!!
- arp 命令显示(本机)某个网络适配器的arp表为什么总是为空
- 关于DWORD的问题
- 请问如何在文档类的ondraw函数中获得自建对话框的指针?谢谢
- onhotkey问题
- 没办法,CSDN上没有InstallShield板块,我只能在这里发问题了。
- 很简单的一个问题,100分相送
- 用VC编写的服务程序 在Windows 7 中直接以管理员身份运行
- VC中怎么访问服务器上的数据库?
- 高手请帮忙:为什么call GetListCtrl().InsertItem(LV_ITEM*)返回的总是0
在使用 VC、 VB、 Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置 ODBC数据源。对于一般用户而言,配置 ODBC数据源可能是一件比较困难的工作。而且,在实际应用中,用户往往要求在同一个应用程序中访问不同的数据源,因此采用一般的加载方法就有了无法克服的缺陷。为能在程序中完成这一工作,方便应用程序的使用,本文以 VC为开发环境介绍两种在应用程序中动态加载 ODBC系统数据源的方法。
方法一:修改注册表
设计思路
一般情况下,当用户在控制面板中配置好 ODBC数据源后, Windows系统便在注册表中加入了一些子键来存储用户的配置结果。当应用程序需要用到数据源时, Windows便会通知底层接口查阅注册表中该数据源的配置。如果用户删除了某个 ODBC数据源,那么也会在注册表中有所反应。如果配置的数据源是用户数据源, Windows系统便会修改注册表的 HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI子键;如果配置的数据源是系统数据源, Windows系统便会修改注册表的 HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI主键。因此,我们可以在应用程序中使用 Windows API中的注册表编辑函数来完成 Windows所做的工作,这样就可以达到动态加载数据源的目的。
具体实现
对于不同类型的数据源,注册表的修改也各有不同,但基本上都要修改两个地方。一个是在 ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在 \ODBC.INI\ODBC Data Sources子键下建立一个新项以便告诉驱动程序管理器 ODBC数据源的类型。下面以配置一个 Microsoft Access数据源为例给出实现此功能的函数的代码。
/* strSourceName是要创建的数据源名, strSourceDb是数据库存放路径, strDescription是数据源的描述字符串。* /
BOOL CLoadOdbcDlg:: LoadDbSource(CString strSourceName,CString strSourceDb, CString strDescription)
{
//存放打开的注册表键
HKEY hKey;
DWORD dw;
//存放注册表 API函数执行的返回值
LONG lReturn;
//存放要打开的子键
CString strSubKey;
//检测是否安装了 MS Access ODBC driver:odbcjt32.dll
//获得 Windows系统目录
char sysDir[MAX_PATH];
char drvName[]=" \\odbcjt32.dll" ;
::GetSystemDirectory (sysDir,MAX_PATH);
strcat(sysDir,drvName);
CFileFind findFile;
if(!findFile.FindFile (sysDir))
{
AfxMessageBox("您的计算机系统中没有安装 MS Access的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。 " ,MB_OK|MB_ICONSTOP);
return false;
}
strSubKey=" SOFTWARE\\ODBC\\ODBC.INI\\"+ strSourceName;
//创建 ODBC数据源在注册表中的子键
lReturn=::RegCreateKeyEx(HKEY_LOCAL_
MACHINE,(LPCTSTR)strSubKey,0,NULL,REG_OPTION
_NON_VOLATILE,KEY_WRITE,NULL,& hKey,& dw);
if(lReturn != ERROR_SUCCESS)
return false;
//设置数据源的各项参数
CString strDbq=strSourceDb;
CString strDriver=sysDir;
DWORD dwDriverId=25;
CString strFil=" MS Access" ;
CString strPwd=strSourceName;
DWORD dwSafeTransactions=0;
CString strUid=strSourceName;
::RegSetValueEx (hKey," DBQ" ,0L,REG_SZ,
(CONST BYTE* )((LPCTSTR) strDbq),strDbq .GetLength ()) ; ::RegSetValueEx (hKey," Description" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strDescription),strDescription.GetLength());
::RegSetValueEx (hKey," Driver" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strDriver),strDriver .GetLength ());
::RegSetValueEx (hKey," DriverId" ,0L,REG_DWORD,(CONST BYTE* )(& dwDriverId),sizeof(dw));
::RegSetValueEx (hKey," FIL" ,0L,REG_SZ,
(CONST BYTE* )((LPCTSTR) strFil),strFil .GetLength ());
::RegSetValueEx (hKey," PWD" ,0L,REG_SZ,
(CONST BYTE* )((LPCTSTR)strPwd),strPwd.GetLength ()) ; ::RegSetValueEx (hKey," SafeTransactions" ,0L,
REG_DWORD,(CONST BYTE* )(& dwSafeTransactions),sizeof(dw));
::RegSetValueEx (hKey," UID" ,0L,REG_SZ,
(CONST BYTE* )((LPCTSTR)strUid),strUid .GetLength ()); ::RegCloseKey(hKey);
//创建 ODBC数据源的 Jet子键
strSubKey+ =" \\Engines\\Jet" ;
lReturn=::RegCreateKeyEx (HKEY_LOCAL_MACHINE ,(LPCTSTR)strSubKey,0,NULL,REG_OPTION_NON_
VOLATILE,KEY_WRITE,NULL,& hKey,& dw);
if(lReturn != ERROR_SUCCESS)
return false;
//设置该子键下的各项参数
CString strImplict=" " ;
CString strUserCommit=" Yes" ;
DWORD dwPageTimeout=5;
DWORD dwThreads=3;
DWORD dwMaxBufferSize=2048;
::RegSetValueEx (hKey," ImplictCommitSync" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strImplict),strImplict.GetLength ()+ 1);
::RegSetValueEx (hKey," MaxBufferSize" ,0L,REG_DWORD,(CONST BYTE* )(& dwMaxBufferSize),sizeof(dw));
::RegSetValueEx (hKey," PageTimeout" ,0L,REG_DWORD,(CONST BYTE* )(& dwPageTimeout),sizeof(dw));
::RegSetValueEx (hKey," Threads" ,0L,REG_DWORD,(CONST BYTE* )(& dwThreads),sizeof(dw));
::RegSetValueEx (hKey," UserCommitSync" ,0L,REG_SZ,(CONST BYTE* )((LPCTSTR)strUserCommit),strUserCommit.GetLength ());
::RegCloseKey (hKey);
//设置 ODBC数据库引擎名称
lReturn=::RegOpenKeyEx (HKEY_LOCAL_MACHINE, " SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources" ,0L,KEY_WRITE,& hKey);
if(lReturn !=ERROR_SUCCESS)
return false;
CString strDbType=" Microsoft Access Driver (* .mdb)" ; ::RegSetValueEx (hKey,strSourceName,0L,REG_SZ,(CONST BYTE* )((LCTSTR)strDbType),strDbType.GetLength ());
return true;
}
由于在动态加载中,一般只会改变数据库文件、数据源说明以及数据源描述,故上述函数可以实现应用中的大部分要求。如果应用中还需要作更多的改变,那么也可以通过改变函数参数的方式加以实现。对于需要动态加载多种类型数据源的情况,可以用具有不同参数的重载函数去实现。
方法二:利用 DLL
设计思路
Windows系统子目录中的动态链接库 Odbcinst.dll提供了一个可以动态地增加、修改和删除数据源的函数 SQLConfigDataSource()。该函数的原型如下:
BOOL SQLConfigDataSource(HWND hwndParent,WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes);
hwndParent参数是父窗口句柄。如果该值为 NULL,将不会显示与父窗口有关的对话框。
fRequest参数可以设置为下面的数值之一:
ODBC_ADD_DSN:增加一个新的用户数据源;
ODBC_CONFIG_DSN:修改(配置)一个已经存在的用户数据源;
ODBC_REMOVE_DSN:删除一个已经存在的用户数据源;
ODBC_ADD_SYS_DSN:增加一个新的系统数据源;
ODBC_CONFIG_SYS_DSN:修改 (配置 )一个已经存在的系统数据源;
ODBC_REMOVE_SYS_DSN:删除一个已经存在的系统数据源。
lpszDriver参数用于传递数据库引擎的名字,等同于方法一中 strDbType变量。
lpszAttirbutes参数是关键字的值,即一连串的 " keyname=value"字符串,每两个字符串之间用 " \"隔开,如 DSN=Personnel Data\0UID=Smith\0DATABASE=Personnel。关于该参数的详细设置请参阅 MSDN中 SQLConfigDataSource()函数的帮助文档和各种 ODBC驱动程序文档。
具体实现
由于 VC的缺省库文件中不包含 SQLConfigDataSource()函数,因此使用该函数之前需要将 odbcinst.h文件包含在工程的头文件中,在工程的 Settings属性对话框 Link属性页的 Object/library modules编辑框中增加 odbc32.lib,同时保证系统目录 system32下有文件 odbccp32.dll。
仍以 Microsoft Access为例,设置数据源名为 demo,数据源描述为 "示例数据源 ",那么在需要动态加载数据源的地方加入下列代码即可:
::SQLConfigDataSource (NULL,ODBC_ADD_SYS_DSN," Microsoft Access Driver (* .mdb)"," DSN=demo\0Descirption=示例数据库 " );
小结
上述两种方法都可以实现动态加载各种类型的 ODBC数据源,并且在 Windows95/98/NT/2000环境下调试通过。方法一在实现时需要较多的代码,方法二所需代码虽少,但需要额外文件的支持,而且随着数据源配置的灵活性的增加,为了形成 lpszAttributes字符串,其代码长度也会相应增加。由于从控制面板配置数据源使得程序员可以获得更加直观的理解,所以对于注册表中各项值以及相应项名称的获得除了可以查阅相关驱动程序的文档外,程序员也可以在编程前先通过控制面板配置 ODBC数据源,然后根据注册表中相应部分的内容进行编程。