title: OLEDB数据源

date: 2018-01-12 21:42:37

tags: [OLEDB, 数据库编程, VC++, 数据库]

categories: windows 数据库编程

keywords: OLEDB, 数据库编程, VC++, 数据库

数据源在oledb中指数据提供者,这里可以简单的理解为数据库程序。数据源对象代表数据库的一个连接,是需要创建的第一个对象。而数据源对象主要用于配置数据库连接的相关属性如连接数据库的用户名密码等等

数据源主要完成的功能如下:

  1. 进行数据库身份认证
  2. 为每个连接准备对应的资源,如对应的数据缓冲,网络连接资源
  3. 设置连接属性,给访问者何种权限,设置连接的超时值等等,对象会根据对应的属性打开对应的接口。它的这些设置都是通过属性进行的

OLEDB属性与属性设置

OLEDB虽然是基于COM的一组接口,但是它与标准的COM接口有点不同,它的一大特色在于它自身的属性设置,有的接口虽然对象中存在但是调用QueryInterface是查询不出来的,只有设置相应的接口才会打开,有的接口可以根据属性值表现不同的行为。比如设置了对应的只读属性则不允许使用更新接口。

每个属性都有值、类型、说明和读写属性,对于行集对象,还有一个用于指示是否可以逐列应用它的指示器。

属性由一个GUID和一个整数ID进行唯一标识。

属性集是所有具有相同 组GUID 的一组属性。在逻辑上它们都用于同一种功能,比如有的属性集用于设置数据源连接属性,有的用于设置行集属性等等。它们是应用在同一个特定对象上的一组属性。在每个这样的属性组中都有属性每个属性属于一个或者多个属性组。

属性定义如下:

typedef struct tagDBPROP {
DBPROPID dwPropertyID; //属性GUID
DBPROPOPTIONS dwOptions; //属性的操作方式
DBPROPSTATUS dwStatus; //属性设置状态
DBID colid; //属性ID,一般给DB_NULLID
VARIANT vValue; //属性值
} DBPROP;

dwOptions:属性的操作方式有3种,但是一般只使用其中的两种:DBPROPOPTIONS_REQUIRED表示必须设置成功,如果设置失败,则设置属性的操作失败,DBPROPOPTIONS_OPTIONAL,表示可选,即即使该属性设置失败,设置属性的操作也返回成功。DBPROPOPTIONS_SETIFCHEAP表示如果在设置属性操作时在在dwStatus参数中返回该属性设置的状态,是否成功,失败的原因等等。

属性集的定义如下:

typedef struct tagDBPROPSET {
DBPROP * rgProperties; //属性数组的指针
ULONG cProperties; //属性数组中元素个数
GUID guidPropertySet; //属性集的GUID
} DBPROPSET;

目前属性组包括初始化属性组、数据源属性组、会话属性组、行集属性组、表属性组和列属性组等等。

设置属性一般包含如下几个步骤:

  1. 分配一个属性类型DBPRO的数组,一般倾向于多分配一个,最后一个数组元素全0,作为结尾
  2. 确定每个属性的属性GUID,即明确我们需要设置的是对象的哪个属性
  3. 填充对应的属性值,属性操作方式
  4. 填充对应的属性集DBPROPSET结构。设置该属性集的GUID
  5. 调用对应的接口设置属性

数据源对象接口

数据源对象的接口定义如下:

CoType TDataSource {
[mandatory] interface IDBCreateSession; //创建回话对象
[mandatory] interface IDBInitialize; //创建数据源连接对象
[mandatory] interface IDBProperties; ///创建数据源的属性操作对象
[mandatory] interface IPersist;
[optional] interface IConnectionPointContainer;
[optional] interface IDBAsynchStatus;
[optional] interface IDBDataSourceAdmin;
[optional] interface IDBInfo;
[optional] interface IObjectAccessControl;
[optional] interface IPersistFile;
[optional] interface ISecurityInfo;
[optional] interface ISupportErrorInfo;
[optional] interface ITrusteeAdmin;
[optional] interface ITrusteeGroupAdmin;
}

在上面代码中,mandatory表示是数据源必须提供的接口,optional表示的是可选性提供的接口,在创建对应的接口时尽量使用必须实现的接口,如果需要使用可选择的接口,一定要判断数据源是否支持。在数据源对象中最主要的还是前三个必须提供的接口

连接到数据库

连接到数据源一般使用IDBInitialize接口的Initialize方法,但是生成IDBInitialize接口有几种不同的方式,下面一一列举出来

直接创建IDBInitialize接口

这种方式一般调用CoCreateInstance函数创建,下面是具体的代码

#include <tchar.h>
#include <windows.h>
#include <strsafe.h> #define COM_NO_WINDOWS_H //如果已经包含了Windows.h或不使用其他Windows库函数时
#define OLEDBVER 0x0260 //MSDAC2.6版
#include <oledb.h>
#include <oledberr.h> #define GRS_ALLOC(sz) HeapAlloc(GetProcessHeap(),0,sz)
#define GRS_CALLOC(sz) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz)
#define GRS_SAFEFREE(p) if(NULL != p){HeapFree(GetProcessHeap(),0,p);p=NULL;} #define GRS_USEPRINTF() TCHAR pBuf[1024] = {}
//定义输出宏
#define GRS_PRINTF(...) \
GRS_USEPRINTF();\
StringCchPrintf(pBuf,1024,__VA_ARGS__);\
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pBuf,lstrlen(pBuf),NULL,NULL); //安全释放,为了养成良好的编码习惯,特作此宏定义
#define GRS_SAFERELEASE(I)\
if(NULL != (I))\
{\
(I)->Release();\
(I)=NULL;\
}
//检测上一步的操作是否成功
#define GRS_COM_CHECK(hr,...)\
if(FAILED(hr))\
{\
GRS_PRINTF(__VA_ARGS__);\
goto CLEAR_UP;\
} int _tmain(int argc, TCHAR* argv[])
{
CoInitialize(NULL);
//创建OLEDB init接口
IDBInitialize *pDBInit = NULL;
IDBProperties *pIDBProperties = NULL;
//设置链接属性
DBPROPSET dbPropset[1] = {0};
DBPROP dbProps[5] = {0};
CLSID clsid_MSDASQL = {0}; //sql server 的数据源对象 HRESULT hRes = CLSIDFromProgID(_T("SQLOLEDB"), &clsid_MSDASQL);
GRS_COM_CHECK(hRes, _T("获取SQLOLEDB的CLSID失败,错误码:0x%08x\n"), hRes);
hRes = CoCreateInstance(clsid_MSDASQL, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,(void**)&pDBInit);
GRS_COM_CHECK(hRes, _T("无法创建IDBInitialize接口,错误码:0x%08x\n"), hRes); //指定数据库实例名,这里使用了别名local,指定本地默认实例
dbProps[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
dbProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;
dbProps[0].vValue.vt = VT_BSTR;
dbProps[0].vValue.bstrVal = SysAllocString(OLESTR("LIU-PC\\SQLEXPRESS"));
dbProps[0].colid = DB_NULLID; //指定数据库库名
dbProps[1].dwPropertyID = DBPROP_INIT_CATALOG;
dbProps[1].dwOptions = DBPROPOPTIONS_REQUIRED;
dbProps[1].vValue.vt = VT_BSTR;
dbProps[1].vValue.bstrVal = SysAllocString(OLESTR("Study"));
dbProps[1].colid = DB_NULLID; //指定链接数据库的用户名
dbProps[2].dwPropertyID = DBPROP_AUTH_USERID;
dbProps[2].vValue.vt = VT_BSTR;
dbProps[2].vValue.bstrVal = SysAllocString(OLESTR("sa")); //指定链接数据库的用户密码
dbProps[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
dbProps[3].vValue.vt = VT_BSTR;
dbProps[3].vValue.bstrVal = SysAllocString(OLESTR("123456")); //设置属性
hRes = pDBInit->QueryInterface(IID_IDBProperties, (void**)&pIDBProperties);
GRS_COM_CHECK(hRes, _T("查询IDBProperties接口失败, 错误码:%08x\n"), hRes);
dbPropset->guidPropertySet = DBPROPSET_DBINIT;
dbPropset[0].cProperties = 4;
dbPropset[0].rgProperties = dbProps;
hRes = pIDBProperties->SetProperties(1, dbPropset);
GRS_COM_CHECK(hRes, _T("设置属性失败, 错误码:%08x\n"), hRes); //链接数据库
hRes = pDBInit->Initialize();
GRS_COM_CHECK(hRes, _T("链接数据库失败:错误码:%08x\n"), hRes);
//do something
pDBInit->Uninitialize(); GRS_PRINTF(_T("数据库操作成功!!!!!\n"));
CLEAR_UP:
GRS_SAFEFREE(pDBInit);
GRS_SAFEFREE(pIDBProperties);
CoUninitialize();
return 0;
}

这是一份完整的可执行代码,后续的部分对于重复的代码将不再给出。

在上述代码中我们首先根据字符串SQLOLEDB查找到SQL Server对应的数据源对象,然后根据数据源对象查询出IDBProperties对象,接着分配一些空间来设置属性和属性集,调用IDBProperties接口的SetProperties函数来设置对应的数据源对象的接口。最后调用IDBInitialize接口的Initialize链接数据源,调用Uninitialize函数来断开连接。

一般数据源对象的属性集合的GUID为DBPROPSET_DBINIT,下面包含的属性最主要的有:

  1. DBPROP_INIT_DATASOURCE:数据连接实例(具体的DBMS实例名)
  2. DBPROP_INIT_CATALOG:目录名(在SQL Server中对应的是具体的数据库名称,对于ORACLE来说没有意义)
  3. DBPROP_AUTH_USERID: 用户名
  4. DBPROP_AUTH_PASSWORD: 密码

我们也注意到上面调用SysAllocString的BSTR类型的字符串并没有调用对应的函数进行释放,会不会发生内存泄露?其实不用担心OLEDB在断开连接的时候已经帮助我们释放了这部分空间。

使用IDBPromptInitialize接口来创建数据源对象

上述方法是依托于标准的COM,虽然也成功创建的数据源连接,但是无法在标准的com之上进行更多的初始化操作,导致了有些特定的高级功能无法使用,所以在实践中常用的还是利用IDBPromptInitialize和IDataInitialize的方式比较多。

IDBPromptInitialize创建时会弹出一个数据源选择的对话框,供用户选择相关配置信息(数据源/用户名/密码等)然后根据这些配置自动生成连接对象。

下面看一个弹出数据源对话框的例子:

void ConnectSQLServerByDialog() //通过弹出对话框来链接SQL SERVER数据库
{
DECLARE_BUFFER();
DECLARE_OLEDB_INTERFACE(IDBPromptInitialize);
DECLARE_OLEDB_INTERFACE(IDBInitialize); HWND hDesktop = GetDesktopWindow();
HRESULT hRes = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER, IID_IDBPromptInitialize, (void**)&pIDBPromptInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建IDBPromptInitialize接口失败: %08x"), hRes);
//调用该函数弹出数据源对话框
hRes = pIDBPromptInitialize->PromptDataSource(NULL, hDesktop, DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize, (IUnknown**)&pIDBInitialize);
COM_CHECK_SUCCESS(hRes, _T("弹出数据源对话框失败:%08x\n"), hRes); hRes = pIDBInitialize->Initialize();
COM_CHECK_SUCCESS(hRes, _T("链接数据库失败:%08x\n"), hRes);
COM_PRINTF(_T("链接数据库成功\n")); hRes = pIDBInitialize->Uninitialize();
__CLEAN_UP:
SAFE_RELEASE(pIDBPromptInitialize);
SAFE_RELEASE(pIDBInitialize);
}

除了这种方式,他还可以直接创建出IDBInitialize接口,利用之前设置属性的方式来连接到数据库,下面是一个演示的例子:

    HRESULT hRes = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_IDataInitialize, (void**)&pIDataInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建接口IDBInitialize失败:%08x\n"), hRes);
hRes = CLSIDFromProgID(_T("SQLOLEDB"), &clsid);
COM_CHECK_SUCCESS(hRes, _T("查询SQLOLEDB CLSID 失败:%08x\n"), hRes);
hRes = pIDataInitialize->CreateDBInstance(clsid, NULL,
CLSCTX_INPROC_SERVER, NULL, IID_IDBInitialize,
(IUnknown**)&pIDBInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建IDBInitialize接口失败:%08x\n"), hRes); //后续的代码就是我们之前写的那段定义属性,设置属性,连接数据库的代码

使用IDataInitialize接口来创建数据源对象

使用IDataInitialize接口可以直接使用连接字串连接到数据库,下面是使用连接字串的例子:

void ConnectSQLServerByConnstr() //通过连接字符串连接数据库
{
DECLARE_OLEDB_INTERFACE(IDataInitialize);
DECLARE_OLEDB_INTERFACE(IDBInitialize);
DECLARE_BUFFER();
HRESULT hRes = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_IDataInitialize, (void**)&pIDataInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建IDataInitialize接口失败:%08x!\n"), hRes); hRes = pIDataInitialize->GetDataSource(NULL, CLSCTX_INPROC_SERVER,
OLESTR("Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password = 123456;Initial Catalog=Study;Data Source=LIU-PC\\SQLEXPRESS;"),
IID_IDBInitialize, (IUnknown**)&pIDBInitialize);
COM_CHECK_SUCCESS(hRes, _T("获取IDBInitialize接口失败:%08x!\n"), hRes);
hRes = pIDBInitialize->Initialize();
COM_CHECK_SUCCESS(hRes, _T("连接数据库失败:%08x!\n"), hRes);
COM_PRINTF(_T("连接数据库成功\n"));
pIDBInitialize->Uninitialize();
__CLEAN_UP:
SAFE_RELEASE(pIDataInitialize);
SAFE_RELEASE(pIDBInitialize);
}

获取连接字串

其实除了上面这种直接创建IDataInitialize接口的方法外,还可以使用IDBPromptInitialize接口Query出一个IDataInitialize接口,然后再设置连接字串连接到数据库。

其实在OLEDB中,可以认为连接字串最终被翻译为对应的属性,也就是说OLEDDB保存着对应连接的属性,我们可以通过不同的方式来获取不同类型的属性,比如使用IDBProperties接口来获取对应的链接属性,或者使用IDataInitialize的GetInitializationString函数来获取连接的链接字串。

既然它保存着每个连接的对应属性,那么是不是可以将用户在数据源对话框上的操作最终保存为数据连接字串呢,答案是肯定的。实现的思路如下:

  1. 调用IDBPromptInitialize接口的PromptDataSourc方法弹出数据源对话框,让用户操作
  2. 根据IDBPromptInitialize接口Query出IDataInitialize接口
  3. 调用IDataInitialize接口的GetInitializationString来获取连接字串

    下面是具体实现的代码:
void GetConnectString()
{
DECLARE_OLEDB_INTERFACE(IDBPromptInitialize);
DECLARE_OLEDB_INTERFACE(IDataInitialize);
DECLARE_OLEDB_INTERFACE(IDBInitialize);
DECLARE_BUFFER(); LPOLESTR pConnStr = NULL;
HWND hDeskTop = GetDesktopWindow();
HRESULT hRes = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER, IID_IDBPromptInitialize, (void**)&pIDBPromptInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建IDBPromptInitialize接口失败:%08x!\n"), hRes);
hRes = pIDBPromptInitialize->PromptDataSource(NULL, hDeskTop, DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize, (IUnknown**)&pIDBInitialize);
COM_CHECK_SUCCESS(hRes, _T("弹出数据源对话框失败:%08x\n"), hRes); hRes= pIDBPromptInitialize->QueryInterface(IID_IDataInitialize, (void**)&pIDataInitialize);
COM_CHECK_SUCCESS(hRes, _T("创建IDataInitialize接口失败:%08x!\n"), hRes); hRes = pIDataInitialize->GetInitializationString(pIDBInitialize, TRUE, &pConnStr);
COM_CHECK_SUCCESS(hRes, _T("获取连接字串失败失败:%08x\n"), hRes); COM_PRINTF(_T("连接字符串:%s"), pConnStr);
SysAllocString(pConnStr);
__CLEAN_UP:
SAFE_RELEASE(pIDataInitialize);
SAFE_RELEASE(pIDBInitialize);
SAFE_RELEASE(pIDBPromptInitialize);
}

为了节约篇幅,这些笔记内容只会列举部分关键的代码,至完整的代码我会随着博客内容的进度慢慢上传到GitHub项目中,并在博文的最末尾给出对应文件的地址

本次代码地址1

本次代码地址2

OLEDB数据源的更多相关文章

  1. OleDB Destination 用法

    第一部分:简介 OleDB Destination component 是将数据流load 到destination,共有5种Data Access Mode,一般的Destination compo ...

  2. OLEDB数据源和目标组件

    在SSIS工程的开发过程中,OLEDB 数据源和目标组件是最常用的数据流组件.从功能上讲,OLEDB 数据源组件用于从OLEDB 提供者(Provider)中获取数据,传递给下游组件,OLEDB提供者 ...

  3. SSIS Passing Parameters to an ADO .NET Source query;向ado.net数据源传递参数。

    使用SSIS的oledb数据源时的参数按钮如下图: 但是在使用ADO.NET源连接到MYSQL时,没有这个参数按钮,如何向数据流的sql command传递参数呢? steps: 1. 在 控制流 选 ...

  4. Execute SQL Task 参数和变量的映射

    Execute SQL Task能够执行带参数的SQL查询语句或存储过程(SP),通过SSIS的变量(Variable)对参数赋值.对于不同的Connection Manager,在Task中需要使用 ...

  5. SSIS,参数坑

    首先我有一个这样的饿存储过程, @procGuid 这个是 存放 guId的字符串 当如传入  0 的时候, @procGuid 会赋值,并且返回. ) ,@procGuid output print ...

  6. .net 常用的命名空间和类

    一.基础命名空间 l  System.Collections 包含了一些与集合相关的类型,比如列表,队列,位数组,哈希表和字典等. l  System.IO 包含了一些数据流类型并提供了文件和目录同步 ...

  7. SSIS 对数据排序

    SSIS 对数据排序有两种方式,一种是使用Sort组件,一种是使用sql command的order by clause进行排序. 一,使用Sort组件进行排序 SortType:升序 ascendi ...

  8. 行集函数:OpenRowSet 和 OpenQuery

    在SQL Server中,行集函数是不确定性的,这意味着,每次调用,返回值不总是相同的.返回值是不确定的,这意味着,对于相同的输入值,不保证每次返回的值都是相同的.对行集函数的每次调用,行集函数都是单 ...

  9. 使用SSIS进行数据清洗

    简介     OLTP系统的后端关系数据库用于存储不同种类的数据,理论上来讲,数据库中每一列的值都有其所代表的特定含义,数据也应该在存入数据库之前进行规范化处理,比如说"age"列 ...

随机推荐

  1. HDU 3569 Imaginary Date 简单期望

    推一下公式.就会发现是这个.. 由于设结果有x种方案.则每一个数字出现的概率都均等,然后和就是x*m 每种方案的概率是1/x 每一个数出现的概率都是1/n 所以每一个方案的和就是 sum/n *m # ...

  2. Android之不须要自己定义View(ViewfindView.java)最简单的二维码扫描

    不废话,先爆照 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  3. Linux 高速操作IOport

    在嵌入式设备中对GPIO的操作是最主要的操作. 一般的做法是写一个单独驱动程序,网上大多数的样例都是这种.事实上linux以下有一个通用的GPIO操作接口.那就是我要介绍的 "/sys/cl ...

  4. Asp.net MVC 填充word并下载

    使用Aspose.word填充内容并下载(免费版Aspose) 填充固定模式的Word文档,需要先制作Word模板: Aspose.Words主要通过Words里域(Fields)来控制内容.     ...

  5. 用maven建立java web项目

    1.在eclipse的菜单栏选择File->New->Other->Maven->Maven ,并在第一个框打勾,然后点击下一步 2.转换为java的Dynamic Web P ...

  6. json api

    from flask import Flask, redirect, url_for, jsonify, request app = Flask(__name__) users = [] ''' RE ...

  7. SVN 安装与使用教程总结

    转载:http://www.cnblogs.com/armyfai/p/3985660.html SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需 ...

  8. 【开源】接口管理平台eoLinker AMS 开源版3.1.5同步线上版!免费增加大量功能!

    概要:eoLinker是一个免费开源的针对开发人员需求而设计的接口管理工具,通过简单的操作来帮助开发者进行接口文档管理.接口自动化测试.团队协作.数据获取.安全防御监控等功能,降低企业的接口管理成本, ...

  9. 为什么还坚持.NET? 找一门适合自己的语言去做编程

    为什么还坚持.NET? 找一门适合自己的语言去做编程 接触了.NET快十二年了,现在专注于分布式服务的开发. 中间经历过各种编程语言的诱惑,ios等. 前几年才对自己有比较明确的定位 技术上:找到适合 ...

  10. HTTP Live Streaming 直播(iOS直播) 初识

    HTTP Live Streaming(HLS)技术,并实现了一个HLS编码器HLSLiveEncoder,当然,C++写的.其功能是采集摄像头与麦克风,实时进行H.264视频编码和AAC音频编码,并 ...