在使用 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数据源为例给出实现此功能的函数的代码。
#include <Windows.h>
#include <atlstr.h>
/*
此函数可以动态创建Microsoft Access数据库的系统DSN。如果要创建用户DSN只需要把HKEY_LOCAL_MACHINE改为HKEY_CURRENT_USER
如果之前已经存在了一个相同名称的DSN,则删除之前的再创建
strSourceName是要创建的数据源名,也就是odbc中的DSN名称
strSourceDb是数据库存放路径(如D:\Access.mdb)
strDescription是数据源的描述字符串(仅仅是个字符串对数据源的描述)
cstrUid 访问数据源的uid
cstrPwd访问数据源的pwd
返回TRUE为创建DSN成功
*/
BOOL CreateAccessDSN(CString cstrSourceName,CString cstrSourceDb, CString cstrDescription,CString cstrUid,CString cstrPwd)
{
//存放打开的注册表键
HKEY hKey;
DWORD dw;
//存放注册表 API函数执行的返回值
LONG lReturn;
//存放要打开的子键
CString cstrSubKey;
//检测是否安装了 MS Access ODBC driver:odbcjt32.dll
//获得 Windows系统目录
TCHAR sysDir[MAX_PATH];
TCHAR drvName[]=_T("\\odbcjt32.dll");
::GetSystemDirectory (sysDir,MAX_PATH);
lstrcat(sysDir,drvName);
/*CFileFind findFile;
if(!findFile.FindFile (sysDir))
{
AfxMessageBox("您的计算机系统中没有安装 MS ssAcce的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。" ,MB_OK|MB_ICONSTOP);
return false;
} */
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(sysDir, &FindFileData);
if(hFind == INVALID_HANDLE_VALUE)
{
return false;
}
cstrSubKey=_T("SOFTWARE\\ODBC\\ODBC.INI\\")+cstrSourceName;
//先判断要创建的ODBC数据源是否存在
lReturn=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,KEY_READ,&hKey);
if (lReturn==ERROR_SUCCESS)//打开成功说明已经存在
{
//删除它和他的所有子键
BOOL bRet=RegDelnode(hKey,(LPTSTR)cstrSubKey.GetString());
if (!bRet)//删除失败
return FALSE;
}
//创建 ODBC数据源在注册表中的子键
lReturn=::RegCreateKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);
if(lReturn != ERROR_SUCCESS)
return false;
//设置数据源的各项参数
CString cstrDbq=cstrSourceDb;
CString cstrDriver=sysDir;
DWORD dwDriverId=25;
CString cstrFil=_T("MS Access") ;
CString cstrPWD=cstrPwd;
DWORD dwSafeTransactions=0;
CString cstrUID=cstrUid;
::RegSetValueEx (hKey,_T("DBQ") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrDbq),cstrDbq .GetLength ()) ;
::RegSetValueEx (hKey,_T("Description") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDescription),cstrDescription.GetLength());
::RegSetValueEx (hKey,_T("Driver"),0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDriver),cstrDriver .GetLength ());
::RegSetValueEx (hKey,_T("DriverId") ,0L,REG_DWORD,(CONST BYTE*)(&dwDriverId),sizeof(dw));
::RegSetValueEx (hKey,_T("FIL") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrFil),cstrFil .GetLength ());
::RegSetValueEx (hKey,_T("PWD") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrPWD),cstrPWD.GetLength ()) ;
::RegSetValueEx (hKey,_T("SafeTransactions") ,0L,REG_DWORD,(CONST BYTE*)(&dwSafeTransactions),sizeof(dw));
::RegSetValueEx (hKey,_T("UID")
方法一:修改注册表
设计思路
一般情况下,当用户在控制面板中配置好 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数据源为例给出实现此功能的函数的代码。
#include <Windows.h>
#include <atlstr.h>
/*
此函数可以动态创建Microsoft Access数据库的系统DSN。如果要创建用户DSN只需要把HKEY_LOCAL_MACHINE改为HKEY_CURRENT_USER
如果之前已经存在了一个相同名称的DSN,则删除之前的再创建
strSourceName是要创建的数据源名,也就是odbc中的DSN名称
strSourceDb是数据库存放路径(如D:\Access.mdb)
strDescription是数据源的描述字符串(仅仅是个字符串对数据源的描述)
cstrUid 访问数据源的uid
cstrPwd访问数据源的pwd
返回TRUE为创建DSN成功
*/
BOOL CreateAccessDSN(CString cstrSourceName,CString cstrSourceDb, CString cstrDescription,CString cstrUid,CString cstrPwd)
{
//存放打开的注册表键
HKEY hKey;
DWORD dw;
//存放注册表 API函数执行的返回值
LONG lReturn;
//存放要打开的子键
CString cstrSubKey;
//检测是否安装了 MS Access ODBC driver:odbcjt32.dll
//获得 Windows系统目录
TCHAR sysDir[MAX_PATH];
TCHAR drvName[]=_T("\\odbcjt32.dll");
::GetSystemDirectory (sysDir,MAX_PATH);
lstrcat(sysDir,drvName);
/*CFileFind findFile;
if(!findFile.FindFile (sysDir))
{
AfxMessageBox("您的计算机系统中没有安装 MS ssAcce的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。" ,MB_OK|MB_ICONSTOP);
return false;
} */
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(sysDir, &FindFileData);
if(hFind == INVALID_HANDLE_VALUE)
{
return false;
}
cstrSubKey=_T("SOFTWARE\\ODBC\\ODBC.INI\\")+cstrSourceName;
//先判断要创建的ODBC数据源是否存在
lReturn=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,KEY_READ,&hKey);
if (lReturn==ERROR_SUCCESS)//打开成功说明已经存在
{
//删除它和他的所有子键
BOOL bRet=RegDelnode(hKey,(LPTSTR)cstrSubKey.GetString());
if (!bRet)//删除失败
return FALSE;
}
//创建 ODBC数据源在注册表中的子键
lReturn=::RegCreateKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);
if(lReturn != ERROR_SUCCESS)
return false;
//设置数据源的各项参数
CString cstrDbq=cstrSourceDb;
CString cstrDriver=sysDir;
DWORD dwDriverId=25;
CString cstrFil=_T("MS Access") ;
CString cstrPWD=cstrPwd;
DWORD dwSafeTransactions=0;
CString cstrUID=cstrUid;
::RegSetValueEx (hKey,_T("DBQ") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrDbq),cstrDbq .GetLength ()) ;
::RegSetValueEx (hKey,_T("Description") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDescription),cstrDescription.GetLength());
::RegSetValueEx (hKey,_T("Driver"),0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDriver),cstrDriver .GetLength ());
::RegSetValueEx (hKey,_T("DriverId") ,0L,REG_DWORD,(CONST BYTE*)(&dwDriverId),sizeof(dw));
::RegSetValueEx (hKey,_T("FIL") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrFil),cstrFil .GetLength ());
::RegSetValueEx (hKey,_T("PWD") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrPWD),cstrPWD.GetLength ()) ;
::RegSetValueEx (hKey,_T("SafeTransactions") ,0L,REG_DWORD,(CONST BYTE*)(&dwSafeTransactions),sizeof(dw));
::RegSetValueEx (hKey,_T("UID")