| 在使用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, "ImplicitCommitSync", 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*)((LPCTSTR)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編輯框中增加odbccp32.lib,同時保證系統目錄system32下有檔案odbccp32.dll。 仍以Microsoft Access為例,設定資料來源名為demo,資料來源描述為"樣本資料來源",那麼在需要動態載入資料來源的地方加入下列代碼即可: ::SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN, "Microsoft Access Driver (*.mdb)", "DSN=demo/0Description=樣本資料庫"); 小結 上述兩種方法都可以實現動態載入各種類型的ODBC資料來源,並且在Windows95/98/NT/2000環境下調試通過。方法一在實現時需要較多的代碼,方法二所需代碼雖少,但需要額外檔案的支援,而且隨著資料來源配置的靈活性的增加,為了形成lpszAttributes字串,其代碼長度也會相應增加。由於從控制台配置資料來源使得程式員可以獲得更加直觀的理解,所以對於註冊表中各項值以及相應項名稱的獲得除了可以查閱相關驅動程式的文檔外,程式員也可以在編程前先通過控制台配置ODBC資料來源,然後根據註冊表中相應部分的內容進行編程。 |