消防监控系统中动态配置ODBC数据源的VC程序实现
2018-11-01卢志强
卢志强
摘要:面对客户提出的数据库中心需要由固定IP地址改为可动态修改的IP,以适应机房变更的需求,而且可能会在同一个应用程序中访问不同的数据库,作者在程序中自动完成这一工作的方法,快速满足了客户的需求。该文将详细介绍动态配置数据源的方法。
关键词:ODBC 数据源;动态配置;VC
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2018)18-0063-01
1 引言
本人项目组开发了某消防监控系统,采用VC开发实现,使用固定配置的ODBC数据源,而系统交付后遇到客户需求更改,提出原设计的数据库中心需要由固定IP地址改为可动态修改的IP,以适应机房变更的需求,而且可能会在同一个应用程序中访问不同的数据库,因此采用一般的加载方法就有了无法克服的缺陷。如果能在程序中自动完成这一工作,将能快速满足客户的需求。那么该如何通过程序代码完成数据源的注册呢?本文将详细介绍动态配置数据源的方法。
2 VC中加载数据源的具体实现
在VC中注册ODBC数据源一般有两种方法:利用函数SQLConfigDataSource和修改注册表。本文重点介绍利用函数SQLConfigDataSource的方法。
如果只是静态的配置ODBC数据源,即数据源各参数(数据源名称、服务器、数据库文件路径、数据库名等)是固定的,那么使用SQLConfigDataSource函数可以轻松的实现程序配置数据源,举例如下:
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!");
函數最后一个参数(lpszAttributes)为一连串的"KeyName = value"字符串,每两个KeyName值之间用""隔开,连续的两个引号,相当于连接符号,使用它们可以便于新代码排版,这比在行结尾处加连接符"\"更方便。lpszAttributes的具体设置,可以参考Windows系统目录下帮助文件odbcjtn.hlp主题目录标签中的“ODBC API函数改变|SQLConfigDataSource”条目。而尤其值得说明的是每个"KeyName = value"字符串都必须以"\0"结尾,这就造成了动态设定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!");
运行结果:Fail!
单步调试观察变量变化:
发现lpszAttributes仅包含"DSN=EINet"的信息,其他配置数据源的信息都丢失了,原因在于CString默认以"\0"作为字符串的结束标志,在强制类型转换时造成了数据丢失。
解决办法为:使用编码转换,在堆栈中分配内存,创建一个char型数组,将lpszAttributes中的"\0"用"#"来表示,然后将数组中的字符赋给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));
}
上面两个函数中用到的USES_CONVERSION是ATL中的一个宏定义。用于编码转换(用的比较多的是CString的LPCWSTR转换)。在ATL下使用要包含头文件atlconv.h,使用时一定要小心,它们从堆栈上分配内存,直到调用它的函数返回,该内存不会被释放。如果在一个循环中,这个宏被反复调用几次,将不可避免的产生stackoverflow。为避免这种情况发生,同时减小代码量,在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);
}
调用上述自定义函数:
CString strServerIP = _T("192.168.1.100");
if(AddDSN(strServerIP))
AfxMessageBox("Success!");
执行代码后结果:Success!
可以看到,程序顺利地执行,成功配置好了数据源。
3 结束语
从以上分析和运行结果可以看出:利用函数SQLConfigDataSource和修改注册表,都可以实现动态修改数据源,尤其是可以实现多个数据源的切换,从而实现同一个应用程序中访问不同的数据库甚至通过IP地址切換实现访问同一数据源名的多个远程服务器。