天天看點

VC中動态加載ODBC解決方法

在使用 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[]=\" file://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; [Page]

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+ =\" file://Engines//Jet\" ;

lReturn=::RegCreateKeyEx (HKEY_LOCAL_MACHINE ,(LPCTSTR)strSubKey,0,NULL,REG_OPTION_NON_

VOLATILE,KEY_WRITE,NULL,& hKey,& dw);

//設定該子鍵下的各項參數

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)

    CString strDbType=\" Microsoft Access Driver (* .mdb)\" ; ::RegSetValueEx (hKey,strSourceName,0L,REG_SZ,(CONST BYTE* )((LCTSTR)strDbType),strDbType.GetLength ());

return true;

  由于在動态加載中,一般隻會改變資料庫檔案、資料源說明以及資料源描述,故上述函數可以實作應用中的大部分要求。如果應用中還需要作更多的改變,那麼也可以通過改變函數參數的方式加以實作。對于需要動态加載多種類型資料源的情況,可以用具有不同參數的重載函數去實作。 [Page]

  方法二:利用 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資料源,然後根據系統資料庫中相應部分的内容進行程式設計。