盧志強(qiáng)
摘要:面對客戶提出的數(shù)據(jù)庫中心需要由固定IP地址改為可動態(tài)修改的IP,以適應(yīng)機(jī)房變更的需求,而且可能會在同一個應(yīng)用程序中訪問不同的數(shù)據(jù)庫,作者在程序中自動完成這一工作的方法,快速滿足了客戶的需求。該文將詳細(xì)介紹動態(tài)配置數(shù)據(jù)源的方法。
關(guān)鍵詞:ODBC 數(shù)據(jù)源;動態(tài)配置;VC
中圖分類號:TP311 文獻(xiàn)標(biāo)識碼:A 文章編號:1009-3044(2018)18-0063-01
1 引言
本人項(xiàng)目組開發(fā)了某消防監(jiān)控系統(tǒng),采用VC開發(fā)實(shí)現(xiàn),使用固定配置的ODBC數(shù)據(jù)源,而系統(tǒng)交付后遇到客戶需求更改,提出原設(shè)計(jì)的數(shù)據(jù)庫中心需要由固定IP地址改為可動態(tài)修改的IP,以適應(yīng)機(jī)房變更的需求,而且可能會在同一個應(yīng)用程序中訪問不同的數(shù)據(jù)庫,因此采用一般的加載方法就有了無法克服的缺陷。如果能在程序中自動完成這一工作,將能快速滿足客戶的需求。那么該如何通過程序代碼完成數(shù)據(jù)源的注冊呢?本文將詳細(xì)介紹動態(tài)配置數(shù)據(jù)源的方法。
2 VC中加載數(shù)據(jù)源的具體實(shí)現(xiàn)
在VC中注冊O(shè)DBC數(shù)據(jù)源一般有兩種方法:利用函數(shù)SQLConfigDataSource和修改注冊表。本文重點(diǎn)介紹利用函數(shù)SQLConfigDataSource的方法。
如果只是靜態(tài)的配置ODBC數(shù)據(jù)源,即數(shù)據(jù)源各參數(shù)(數(shù)據(jù)源名稱、服務(wù)器、數(shù)據(jù)庫文件路徑、數(shù)據(jù)庫名等)是固定的,那么使用SQLConfigDataSource函數(shù)可以輕松的實(shí)現(xiàn)程序配置數(shù)據(jù)源,舉例如下:
if(SQLConfigDataSource(NULL, ODBC_ADD_DSN, "SQL Server",
"DSN=EINet\0"
"SERVER=192.168.1.100\0"
"DATABASE=myEIfireDB\0"
"Trusted_Connection=Yes\0")==1)
AfxMessageBox("Success!");
上面的代碼也可以寫成:
if(SQLConfigDataSource(NULL, ODBC_ADD_DSN, "SQL Server", "DSN=EINet\0SERVER=192.168.1.100\0DATABASE=myEIfireDB\0Trusted_Connection=Yes")==1)
AfxMessageBox("Success!");
函數(shù)最后一個參數(shù)(lpszAttributes)為一連串的"KeyName = value"字符串,每兩個KeyName值之間用""隔開,連續(xù)的兩個引號,相當(dāng)于連接符號,使用它們可以便于新代碼排版,這比在行結(jié)尾處加連接符"\"更方便。lpszAttributes的具體設(shè)置,可以參考Windows系統(tǒng)目錄下幫助文件odbcjtn.hlp主題目錄標(biāo)簽中的“ODBC API函數(shù)改變|SQLConfigDataSource”條目。而尤其值得說明的是每個"KeyName = value"字符串都必須以"\0"結(jié)尾,這就造成了動態(tài)設(shè)定lpszAttributes的困難,先看這段代碼:
CString strSource;
CString strIP = _T("192.168.1.100");
strSource.Format("DSN=EINet\0SERVER=\"%s\"\0DATABASE=myEIfireDB\0Trusted_Connection=Yes",strIP);
LPCSTR lpszAttributes = (LPCSTR)strSource;
if(SQLConfigDataSource(NULL,ODBC_ADD_DSN,"SQL Server",lpszAttributes)==1)
AfxMessageBox("Success!");
else
AfxMessageBox("Fail!");
運(yùn)行結(jié)果:Fail!
單步調(diào)試觀察變量變化:
發(fā)現(xiàn)lpszAttributes僅包含"DSN=EINet"的信息,其他配置數(shù)據(jù)源的信息都丟失了,原因在于CString默認(rèn)以"\0"作為字符串的結(jié)束標(biāo)志,在強(qiáng)制類型轉(zhuǎn)換時造成了數(shù)據(jù)丟失。
解決辦法為:使用編碼轉(zhuǎn)換,在堆棧中分配內(nèi)存,創(chuàng)建一個char型數(shù)組,將lpszAttributes中的"\0"用"#"來表示,然后將數(shù)組中的字符賦給CString字符串,用CString的Replace方法再將"#"替換為"\0"。
具體代碼如下:
BOOL AddDSN(CString strServerIP)
{
USES_CONVERSION;
_TCHAR buffer[MAX_PATH] = {0};
LPCTSTR lpstrServerIP = strServerIP;
_stprintf(buffer, _T("DSN= eiSource #")_T("SERVER=%s#") _T("DATABASE=eiDB#")_T("Trusted_Connection=Yes#"),lpstrServerIP);
CString strAttributes = buffer;
int len = strAttributes.GetLength();
strAttributes.Replace(_T('#'), _T('\0'));
return SQLConfigDataSource(NULL, ODBC_ADD_DSN, "SQL Server", strAttributes.GetBuffer(len));
}
上面兩個函數(shù)中用到的USES_CONVERSION是ATL中的一個宏定義。用于編碼轉(zhuǎn)換(用的比較多的是CString的LPCWSTR轉(zhuǎn)換)。在ATL下使用要包含頭文件atlconv.h,使用時一定要小心,它們從堆棧上分配內(nèi)存,直到調(diào)用它的函數(shù)返回,該內(nèi)存不會被釋放。如果在一個循環(huán)中,這個宏被反復(fù)調(diào)用幾次,將不可避免的產(chǎn)生stackoverflow。為避免這種情況發(fā)生,同時減小代碼量,在VC下可以更方便地直接使用下列代碼:
BOOL ConfigDSN(CString strServerIP){
_TCHAR buffer[MAX_PATH] = {0};
LPCTSTR lpstrServerIP = strServerIP;
sprintf(buffer, _T("DSN= eiSource #")_T("SERVER=%s#") _T("DATABASE=eiDB#")_T("Trusted_Connection=Yes#"),lpstrServerIP);
CString strAttributes = buffer;
strAttributes.Replace(_T('#'), _T('\0'));
return SQLConfigDataSource(NULL, ODBC_CONFIG_DSN, "SQL Server", strAttributes);
}
調(diào)用上述自定義函數(shù):
CString strServerIP = _T("192.168.1.100");
if(AddDSN(strServerIP))
AfxMessageBox("Success!");
執(zhí)行代碼后結(jié)果:Success!
可以看到,程序順利地執(zhí)行,成功配置好了數(shù)據(jù)源。
3 結(jié)束語
從以上分析和運(yùn)行結(jié)果可以看出:利用函數(shù)SQLConfigDataSource和修改注冊表,都可以實(shí)現(xiàn)動態(tài)修改數(shù)據(jù)源,尤其是可以實(shí)現(xiàn)多個數(shù)據(jù)源的切換,從而實(shí)現(xiàn)同一個應(yīng)用程序中訪問不同的數(shù)據(jù)庫甚至通過IP地址切換實(shí)現(xiàn)訪問同一數(shù)據(jù)源名的多個遠(yuǎn)程服務(wù)器。