OLEDB 枚举数据源
在之前的程序中,可以看到有这样一个功能,弹出一个对话框让用户选择需要连接的数据源,并输入用户名和密码,最后连接;而且在一些数据库管理软件中也提供这种功能——能够自己枚举出系统中存在的数据源,同时还可以枚举出能够连接的SQL Server数据库的实例。其实这个功能是OLEDB提供的高级功能之一。
枚举对象用于搜寻可用的数据源和其它的枚举对象(层次式),枚举出来的对象是一个树形结构。在程序中提供一个枚举对象就可以枚举里面的所有数据源,如果没有指定所使用的的上层枚举对象,则可以使用顶层枚举对象来枚举可用的OLEDB提供程序,其实我们使用枚举对象枚举数据源时它也是在注册表的对应位置进行搜索,所以我们可以直接利用操作注册表的方式来获取数据源对象,但是注册表中的信息过于复杂,而且系统对注册表的依赖比较严重,所以并不推荐使用这种方式。
枚举对象的原型如下:
CoType TEnumerator {
[mandatory] IParseDisplayName;
[mandatory] ISourcesRowset;
[optional] IDBInitialize;
[optional] IDBProperties;
[optional] ISupportErrorInfo;
}
顶层枚举对象的获取和遍历
要利用数据源枚举功能,第一个要获取的枚举对象就是顶层枚举对象。或者称之为根枚举器,根枚举器对象的CLSID是CLSID_OLEDB_ENUMNRATOR,顶层枚举对象可以使用标准的COM对象创建方式来创建,之后可以使用ISourceRowset对象的GetSourcesRowset,得到数据源组合成的结果集。接着可以根据行集中的行类型来判断是否是一个子枚举对象或者数据源对象。如果是子枚举对象,可以利用名字对象的方法创建一个新的子枚举对象,然后根据这个枚举对象来枚举其中的数据源对象。
一般来说这颗数结构只有两层。
OLEDB提供者结果集
在上面我们说可以根据结果集中的行类型来判断是否是一个子枚举对象或者数据源对象,那么怎么获取这个行类型呢?这里需要了解返回的行集的结构。
字段名称 | 类型 | 最大长度 | 含义描述 |
---|---|---|---|
SOURCES_NAME | DBTYPE_WSTR | 128 | 枚举对象或数据源名称 |
SOURCES_PARSENAME | DBTYPE_WSTR | 128 | 可以传递给IParseDisplayName接口并得到一个moniker对象的字符串(枚举对象或数据源的moniker) |
SOURCES_DESCRIPTION | DBTYPE_WSTR | 128 | 枚举对象或数据源的描述 |
SOURCES_TYPE | DBTYPE_UI2 | 2(单位字节) | 枚举对象或实例的类型,有下列值: DBSOURCETYPE_BINDER (=4)- URL DBSOURCETYPE_DATASOURCE_MDP (=3) - OLAP提供者 DBSOURCETYPE_DATASOURCE_TDP (=1) - 关系型或表格型数据源 DBSOURCETYPE_ENUMERATOR (=2) - 子枚举对象 |
SOURCES_ISPARENT | DBTYPE_BOOL | 2(单位字节) | 是否是父枚举器 |
在枚举时根据SOURCES_TYPE字段来判断是否是子枚举对象,如果是则使用第二列的数据获取子枚举器的对象。
如果根据名称创建子枚举器
这里需要使用IMoniker接口。
名字对象(moniker)的创建方法,是一种标准的以名字解析方法创建一个COM对象及接口的方法。相比于直接使用CoCreateInstance来说是一种更加高级的方法。
这是标准的COM 对象的创建方式,其原理就是通过一个全局唯一的名称在注册表中搜索得到对应的CLSID,然后根据ID调用CoCreateInstance来创建对象。具体搜索过程可以参考COM基础系列
在数据源枚举的应用中,先从ISourcesRowset对象中Query出IParseDisplayName接口,再调用该接口的ParseDisplayName方法传入上述表格中SOURCES_PARSENAME的值,得到IMoniker接口,最后调用全局函数BindMinker传递IMoniker接口指针并指定需要创建的接口ID。
具体例子
最后是一个具体的例子
这个例子中创建了一个MFC应用程序,最后效果类似于前面几个例子中的OLEDB的数据源选择对话框。
在例子中最主要的代码有两段:IDBSourceDlg对话框的EnumDataSource方法,和IDBConnectDlg方法Initialize。这两个分别用来枚举系统中存在的数据源对象和数据源对象中对应的数据库实例。当用户根据界面的提示选择了对应的选项后点击测试连接按钮来尝试连接。
这里展示的代码主要是3段,枚举数据源,枚举数据源中对应的数据库实例,以及根据选择的实例生成对应的数据源对象接口并测试连接。
void IDBSourceDlg::EnumDataSource(ISourcesRowset *pISourceRowset)
{
COM_DECLARE_INTERFACE(IRowset);
COM_DECLARE_INTERFACE(IAccessor);
COM_DECLARE_INTERFACE(IMoniker);
COM_DECLARE_INTERFACE(IParseDisplayName);
HROW *rgRows = NULL;
HACCESSOR hAccessor = NULL;
ULONG cRows = 10;
DWORD dwOffset = 0;
PVOID pData = NULL;
PVOID pCurrentData = NULL;
DBCOUNTITEM cRowsObtained = 0;
LPOLESTR lpParamName = OLESTR("");
//利用顶层枚举对象来枚举系统中存在的数据源
HRESULT hRes = pISourceRowset->GetSourcesRowset(NULL, IID_IRowset, 0, NULL, (IUnknown**)&pIRowset);
ISourcesRowset *pSubSourceRowset = NULL;
ULONG ulEaten = 0;
if (FAILED(hRes))
{
ComMessageBox(NULL, _T("OLEDB 错误"), MB_OK, __T("创建接口ISourcesRowset失败,错误码:%08x\n"), hRes);
goto __CLEAR_UP;
}
DBBINDING rgBinding[3] = {0}; //这里只关心我们需要的列,不需要获取所有的列
for (int i = 0; i < 3; i++)
{
rgBinding[i].bPrecision = 0;
rgBinding[i].bScale = 0;
rgBinding[i].cbMaxLen = 128 * sizeof(WCHAR);
rgBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBinding[i].dwPart = DBPART_VALUE;
rgBinding[i].eParamIO = DBPARAMIO_NOTPARAM;
rgBinding[i].wType = DBTYPE_WSTR;
rgBinding[i].obLength = 0;
rgBinding[i].obStatus = 0;
rgBinding[i].obValue = dwOffset;
dwOffset += rgBinding[0].cbMaxLen;
}
rgBinding[0].iOrdinal = 3; //第三项是数据源或者枚举器的描述信息,用于显示
rgBinding[1].wType = DBTYPE_UI2;
rgBinding[1].iOrdinal = 4; //第四列是枚举出来的类型信息,用于判断是否需要递归
rgBinding[2].iOrdinal = 1; //第一列是枚举出来的类型信息,用于获取子枚举器
hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
if (FAILED(hRes))
{
ComMessageBox(NULL, _T("OLEDB 错误"), MB_OK, __T("查询接口pIAccessor失败,错误码:%08x\n"), hRes);
goto __CLEAR_UP;
}
hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 3, rgBinding, 0, &hAccessor, NULL);
if (FAILED(hRes))
{
ComMessageBox(NULL, _T("OLEDB 错误"), MB_OK, __T("创建访问器失败,错误码:%08x\n"), hRes);
goto __CLEAR_UP;
}
pData = MALLOC(dwOffset * cRows);
while (TRUE)
{
hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, cRows, &cRowsObtained, &rgRows);
if(S_OK != hRes && cRowsObtained == 0)
{
break;
}
ZeroMemory(pData, dwOffset * cRows);
for (int i = 0; i < cRowsObtained; i++)
{
pCurrentData = (BYTE*)pData + dwOffset * i;
pIRowset->GetData(rgRows[i], hAccessor, pCurrentData);
DATASOURCE_ENUM_INFO dbei = {0}; //将枚举到的相关信息存储到对应的结构中
dbei.csSourceName = (LPCTSTR)((BYTE*)pCurrentData + rgBinding[2].obValue);
dbei.csDisplayName = (LPCTSTR)((BYTE*)pCurrentData + rgBinding[0].obValue);
dbei.dbTypeEnum = *(DBTYPEENUM*)((BYTE*)pCurrentData + rgBinding[1].obValue);
m_DataSourceList.AddString(dbei.csDisplayName); //显示数据源信息
g_DataSources.push_back(dbei);
}
pIRowset->ReleaseRows(cRowsObtained, rgRows, NULL, NULL, NULL);
}
pIAccessor->ReleaseAccessor(hAccessor, NULL);
__CLEAR_UP:
FREE(pData);
CoTaskMemFree(rgRows);
COM_SAFE_RELEASE(pIRowset);
COM_SAFE_RELEASE(pIAccessor);
}
void IDBConnectDlg::Initialize(const CStringW& csSelected)
{
BSTR lpOleName = NULL;
ULONG uEaten = 0;
for (vector<DATASOURCE_ENUM_INFO>::iterator it = g_DataSources.begin(); it != g_DataSources.end(); it++)
{
if (it->csDisplayName == csSelected)
{
lpOleName = it->csSourceName.AllocSysString();
}
}
COM_DECLARE_INTERFACE(ISourcesRowset);
COM_DECLARE_INTERFACE(IParseDisplayName);
COM_DECLARE_INTERFACE(IMoniker);
CoCreateInstance(CLSID_OLEDB_ENUMERATOR, NULL, CLSCTX_INPROC_SERVER, IID_ISourcesRowset, (void**)&pISourcesRowset);
pISourcesRowset->QueryInterface(IID_IParseDisplayName, (void**)&pIParseDisplayName);
pIParseDisplayName->ParseDisplayName(NULL, lpOleName, &uEaten, &pIMoniker);
if (lpOleName != NULL)
{
SysFreeString(lpOleName);
}
HRESULT hRes = BindMoniker(pIMoniker, 0, IID_ISourcesRowset, (void**)&m_pConnSourceRowset);
COM_SAFE_RELEASE(pIMoniker);
COM_SAFE_RELEASE(pIParseDisplayName);
if (FAILED(hRes))
{
COM_SAFE_RELEASE(m_pConnSourceRowset);
return;
}
COM_DECLARE_INTERFACE(IRowset)
hRes = m_pConnSourceRowset->GetSourcesRowset(NULL, IID_IRowset, 0, NULL, (IUnknown**)&pIRowset);
if (FAILED(hRes))
{
return;
}
DBBINDING rgBind[1] = {0};
rgBind[0].bPrecision = 0;
rgBind[0].bScale = 0;
rgBind[0].cbMaxLen = 128 * sizeof(WCHAR);
rgBind[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBind[0].dwPart = DBPART_VALUE;
rgBind[0].eParamIO = DBPARAMIO_NOTPARAM;
rgBind[0].iOrdinal = 2; //绑定第二项,用于展示数据源
rgBind[0].obLength = 0;
rgBind[0].obStatus = 0;
rgBind[0].obValue = 0;
rgBind[0].wType = DBTYPE_WSTR;
HACCESSOR hAccessor = NULL;
HROW *rghRows = NULL;
PVOID pData = NULL;
PVOID pCurrData = NULL;
ULONG cRows = 10;
COM_DECLARE_INTERFACE(IAccessor);
hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
if (FAILED(hRes))
{
COM_SAFE_RELEASE(pIRowset);
return;
}
hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBind, 0, &hAccessor, NULL);
DBCOUNTITEM cRowsObtained;
if (FAILED(hRes))
{
COM_SAFE_RELEASE(pIRowset);
COM_SAFE_RELEASE(pIAccessor);
return;
}
pData = MALLOC(rgBind[0].cbMaxLen * cRows);
while (TRUE)
{
hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, cRows, &cRowsObtained, &rghRows);
if (S_OK != hRes && cRowsObtained == 0)
{
break;
}
for (int i = 0; i < cRowsObtained; i++)
{
pCurrData = (BYTE*)pData + rgBind[0].cbMaxLen * i;
pIRowset->GetData(rghRows[i], hAccessor, pCurrData);
m_ComboDataSource.AddString((LPOLESTR)pCurrData);
}
pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
}
FREE(pData);
pIAccessor->ReleaseAccessor(hAccessor, NULL);
}
void IDBConnectDlg::OnBnClickedBtnConnectTest()
{
// TODO: 在此添加控件通知处理程序代码
CStringW csSelected = _T("");
ULONG chEaten = 0;
m_ComboDataSource.GetWindowText(csSelected);
COM_DECLARE_INTERFACE(IParseDisplayName);
COM_DECLARE_INTERFACE(IMoniker);
if (m_pConnSourceRowset == NULL)
{
MessageBox(_T("连接失败"));
return;
}
HRESULT hRes = m_pConnSourceRowset->QueryInterface(IID_IParseDisplayName, (void**)&pIParseDisplayName);
if (FAILED(hRes))
{
return;
}
hRes = pIParseDisplayName->ParseDisplayName(NULL, csSelected.AllocSysString(), &chEaten, &pIMoniker);
COM_SAFE_RELEASE(pIParseDisplayName);
if (FAILED(hRes))
{
MessageBox(_T("连接失败"));
return;
}
COM_DECLARE_INTERFACE(IDBProperties);
hRes = BindMoniker(pIMoniker, 0, IID_IDBProperties, (void**)&pIDBProperties);
COM_SAFE_RELEASE(pIMoniker);
if (FAILED(hRes))
{
MessageBox(_T("连接失败"));
return;
}
//获取用户输入
CStringW csDB = _T("");
CStringW csUser = _T("");
CStringW csPasswd = _T("");
GetDlgItemText(IDC_EDIT_USERNAME, csUser);
GetDlgItemText(IDC_EDIT_PASSWORD, csPasswd);
GetDlgItemText(IDC_EDIT_DATABASE, csDB);
//设置链接属性
DBPROP connProp[5] = {0};
DBPROPSET connPropset[1] = {0};
connProp[0].colid = DB_NULLID;
connProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
connProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
connProp[0].vValue.vt = VT_BSTR;
connProp[0].vValue.bstrVal = csSelected.AllocSysString();
connProp[1].colid = DB_NULLID;
connProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
connProp[1].dwPropertyID = DBPROP_INIT_CATALOG;
connProp[1].vValue.vt = VT_BSTR;
connProp[1].vValue.bstrVal = csDB.AllocSysString();
connProp[2].colid = DB_NULLID;
connProp[2].dwOptions = DBPROPOPTIONS_REQUIRED;
connProp[2].dwPropertyID = DBPROP_AUTH_USERID;
connProp[2].vValue.vt = VT_BSTR;
connProp[2].vValue.bstrVal = csUser.AllocSysString();
connProp[3].colid = DB_NULLID;
connProp[3].dwOptions = DBPROPOPTIONS_REQUIRED;
connProp[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
connProp[3].vValue.vt = VT_BSTR;
connProp[3].vValue.bstrVal = csPasswd.AllocSysString();
connPropset[0].cProperties = 4;
connPropset[0].guidPropertySet = DBPROPSET_DBINIT;
connPropset[0].rgProperties = connProp;
hRes = pIDBProperties->SetProperties(1, connPropset);
if (FAILED(hRes))
{
COM_SAFE_RELEASE(pIDBProperties);
return;
}
COM_DECLARE_INTERFACE(IDBInitialize);
hRes = pIDBProperties ->QueryInterface(IID_IDBInitialize, (void**)&pIDBInitialize);
COM_SAFE_RELEASE(pIDBProperties);
if (FAILED(hRes))
{
return;
}
hRes = pIDBInitialize->Initialize();
if (FAILED(hRes))
{
MessageBox(_T("连接失败"));
}else
{
MessageBox(_T("连接成功"));
}
pIDBInitialize->Uninitialize();
COM_SAFE_RELEASE(pIDBInitialize);
}
最后,这次由于是一个MFC的程序,涉及到的代码文件比较多,因此就不像之前那样以代码片段的方式方上来了,这次我将其以项目的方式放到GitHub上供大家参考。
项目地址
OLEDB 枚举数据源的更多相关文章
- OLEDB 数据变更通知
除了之前介绍的接口,OLEDB还定义了其他一些支持回调的接口,可以异步操作OLEDB对象或者得到一些重要的事件通知,从而使应用程序有机会进行一些必要的处理.其中较有用的就是结果集对象的变更通知接口.通 ...
- [WinForm] DataGridView 绑定 DT && ComboBox 列绑定 Dict
一 需求介绍 一般像枚举类型的数据,我们在数据库里存储着诸如(1.2.3.4-)或者("001"."002"."003"-)此类,但是界面 ...
- ADO.net操作数据库
今天整理硬盘,发现2年前开始着手开始学习C#的学习日记.陆续整理,一是自己的知识梳理梳理,二是希望与大家多多交流,能给初学者带来一定帮助,当然是更高兴的啦. 断线对象 另一类是与数据源无关的断线对象, ...
- 【转】ODBC、OLE DB、 ADO的区别
一.ODBC ODBC的由来 1992年Microsoft和Sybase.Digital共同制定了ODBC标准接口,以单一的ODBC API来存取各种不同的数据库.随后ODBC便获得了许多数据库厂商和 ...
- mbos之动态图表设计
前言 所谓,一图胜千言.人脑有80%的部分专门用于视觉处理.而随着数据时代的全面来临,我们自然有必要将数据转化为图形与图表. Mbos是一个快速,稳定的云端轻应用开发平台.帮助企业快速开发移动应用,加 ...
- BI之SSIS入门最新版Visual Studio调试技巧
简介 最近公司业务需要用到BI SSIS,SSIS是什么?"SSIS是Microsoft SQL Server Integration Services的简称,是生成高性能数据集成解决方案( ...
- SNF软件开发机器人平台2018-发展升级履历-零编程时代
一.SNF软件开发机器人产品白皮书 二.SNF开发机器人教程:链接:https://pan.baidu.com/s/1Qpomg11c_1b1NKY5P7e4Bw 密码:jwc3 三.SNF软件开发机 ...
- SNF软件开发机器人2018最新更新内容
SNF软件开发机器人从10月份到现在的更新升级情况如下: 1 表单 表单控件占多列时,宽度默认0,自适应宽度2 excel导出 部分excel导出方法移动到框架中,可通用获取3 生成代码 生成的代码, ...
- C# 通用树形数据结构
前言 树在图论中是一种重要的图,由于其自身的许多特殊性质,也是一种重要的计算机数据结构,在很多地方都有用.但是这些树大多都是作为其他应用的内部数据结构来使用.我们无法了解这些树的详细信息,而 .Net ...
随机推荐
- JSP标签的用法
JSP动作标签: 通过动作标签,程序员可以在JSP页面中把页面的显示功能部分 封装起来,是整个页面更简洁和易于维护 <jsp:useBean> 装载一个将在JSP页面中使用的JavaBea ...
- python连接postgreSQL
利用python(我用的是python2.7版本)连接postgresql数据库,这里使用psycopg2这个插件 官网下载psycopg2-2.5.1.tar.gz:http://initd.org ...
- os模块与 sys模块
os模块是与操作系统交互的一个接口 ''' os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作 ...
- python学习之路---day19--面向对象--约束
约束和异常一:约束: 约束方法:(解释:就相当于在想项目中,先提前规划好框架和约定基本代码,像定义好调用的名字后,后面的名字只能用这个,用其他的 名字就会抛出异常.错误) 1:提取父类,然后再父类中定 ...
- Android 利用apache tomcat在自己的电脑上搭建服务器
1.什么叫服务器 装了服务器端的软件的那台电脑被称为服务器.常见的服务器的软件有apache tomcat. 2.Tomcat 介绍 tomcat是一种轻量级的web容器服务器,使用tomcat可以实 ...
- paraview plot over line
- [BZOJ 4850][Jsoi2016]灯塔
传送门 #include <bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) ...
- 【算法笔记】B1021 个位数统计
1021 个位数统计 (15 分) 给定一个 k 位整数 N=dk−110k−1+⋯+d1101+d0 (0≤di≤9, i=0,⋯,k−1, dk−1> ...
- hql语句加别名的错误
写了一个查询数量的方法,结果执行不出来,debug后是runtimeException,然后就在那个hql里面使劲找,将他翻译成sql在数据库中执行,结果也没问题,原来的hql如下: 注意这个num, ...
- Python-append()/extend()
append()向列表尾部添加一个新的元素,只接受一个参数 extend()只接受一个列表作为参数,将参数中的每个元素都添加到原列表 append()用法示例: >> mylist = [ ...