C语言DCI(OCI)方式连接DM数据库

C语言DCI(OCI)方式连接DM数据库

一、背景

近期用户使用DCI的方式连接达梦数据库出现中文乱码的问题,所以决定写一个测试Demo。

因为使用DCI的方式连接,适配中心https://eco.dameng.com/docs/zh-cn/app-dev/index.html 也没有相关的例子。为避免版权纠纷所以在DM8以后的数据库中就没有DCI相关的资料文档了。实际上是将DCI的文档跟数据库这块单独分开了。所以如果要用就需要单独去找或者申请。DCI的相关文档在资料包中有。

需要说明的是,本人非开发人员,所以在C++代码编写思路或者使用方式上难免有些低级操作,调试中也是参考了网上很多朋友发的帖子,如有错误或者修改建议,欢迎指正。谢谢!

请添加图片描述

环境

操作系统:Window 10

数据库版本:Dm8.1.2.18

开发工具:Visual Studio 2017

DCI的库:在资料中,整篇文章用的是解压后的dmoci目录的文件

注意:DM8相对较老的一些版本在安装数据库以后就可以在“安装目录/drivers”下看到dci、oci目录,但是新版本已经将oci单独打包,名字为xxxx_dmdci.zip(资料网盘已提供,本章用的是压缩包中dmoci目录的库),内有oci和occi所需的头文件和库文件。数据库的drivers目录仍然保留有dci目录与库文件。需要oci相关文件的朋友需要注意一下这里。准确来说本次的文章应该是OCI的方式来连接,所以文章中的所有dci可以统称为oci。

二、数据库的测试SQL

把sql装载到库里

DROP TABLE "SYSDBA"."PERSON" CASCADE;
 
CREATE TABLE "SYSDBA"."PERSON"
(
 "PERSONID" INT IDENTITY(1,1) NOT NULL,
 "SEX" CHAR(1) NOT NULL,
 "NAME" VARCHAR(50) NOT NULL,
 "EMAIL" VARCHAR(50) NULL,
 "PHONE" VARCHAR(25) NULL,
  CLUSTER PRIMARY KEY("PERSONID") ENABLE 
);
 
SET IDENTITY_INSERT "SYSDBA"."PERSON" ON;
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(1,'F','李丽','lily@sina.com','02788548562');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(2,'M','王刚','','02787584562');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(3,'M','李勇','','02782585462');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(4,'F','郭艳','','02787785462');
INSERT INTO "SYSDBA"."PERSON"("PERSONID","SEX","NAME","EMAIL","PHONE") VALUES(5,'F','孙丽','','13055173012');
SET IDENTITY_INSERT "SYSDBA"."PERSON" OFF;
 
COMMIT;

三、新建项目

  1. 新建项目

请添加图片描述

  1. 新建源文件并新建一个demo.cpp的文件

请添加图片描述

请添加图片描述


/************************************************************************/
/* DCI编程实例 (较DCI手册示例有改动)									*/
/************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "DCI.h"

// 声明句柄
DCIEnv* envhp;				/* 环境句柄 */
DCISvcCtx* svchp;			/* 服务环境句柄 */
DCIServer* srvhp;			/* 服务器句柄 */
DCISession* authp;			/* 会话句柄 */
DCIStmt* stmthp;			/* 语句句柄 */
DCIDescribe* dschp;			/* 描述句柄 */
DCIError* errhp;			/* 错误句柄 */
DCIDefine* defhp[3];		/* 定义句柄 */
DCIBind* bidhp[4];			/* 绑定句柄 */
sb2 ind[3];					/* 指示符变量 */

// 绑定select结果集的参数
text szpersonid[11];		/* 存储personid列 */
text szsex[2];				/* 存储sex列 */
text szname[51];			/* 存储name列 */
text szemail[51];			/* 存储mail列 */
text szphone[26];			/* 存储phone列 */
char sql[256];				/* 存储执行的sql语句 */


int main(int argc, char* argv[])
{
	char strServerName[50];
	char strUserName[50];
	char strPassword[50];
	int ret;
	text errbuf[100];

	// 设置服务器,用户名和密码
	strcpy(strServerName, "localhost");
	strcpy(strUserName, "SYSDBA");
	strcpy(strPassword, "SYSDBA");

	// 初始化OCI应用环境
	OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL);

	// 初始化环境句柄
	OCIEnvInit(&envhp, OCI_DEFAULT, 0, 0);

	// 分配句柄
	OCIHandleAlloc(envhp, (dvoid**)&svchp, OCI_HTYPE_SVCCTX, 0, 0);			/* 服务器环境句柄 */
	OCIHandleAlloc(envhp, (dvoid**)&srvhp, OCI_HTYPE_SERVER, 0, 0);			/* 服务器句柄 */
	OCIHandleAlloc(envhp, (dvoid**)&authp, OCI_HTYPE_SESSION, 0, 0);		/* 会话句柄 */
	OCIHandleAlloc(envhp, (dvoid**)&errhp, OCI_HTYPE_ERROR, 0, 0);			/* 错误句柄 */
	OCIHandleAlloc(envhp, (dvoid**)&dschp, OCI_HTYPE_DESCRIBE, 0, 0);		/* 描述符句柄 */

	// 连接服务器
	OCIServerAttach(srvhp, errhp, (text*)strServerName,
		(sb4)strlen(strServerName), OCI_DEFAULT);

	// 设置用户名和密码
	OCIAttrSet(authp, OCI_HTYPE_SESSION, (text*)strUserName,
		(ub4)strlen(strUserName), OCI_ATTR_USERNAME, errhp);
	OCIAttrSet(authp, OCI_HTYPE_SESSION, (text*)strPassword,
		(ub4)strlen(strPassword), OCI_ATTR_PASSWORD, errhp);

	// 设置服务器环境句柄属性
	OCIAttrSet((dvoid*)svchp, (ub4)OCI_HTYPE_SVCCTX,
		(dvoid*)srvhp, (ub4)0, OCI_ATTR_SERVER, errhp);
	OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, (dvoid*)authp, 0,
		OCI_ATTR_SESSION, errhp);

	// 创建并开始一个用户会话
	OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT);
	OCIHandleAlloc(envhp, (dvoid**)&stmthp, OCI_HTYPE_STMT, 0, 0);			/* 语句句柄 */

		/************************************************************************/
		/* 查询person表															*/
		/************************************************************************/

	strcpy(sql, "select personid, name, phone from sysdba.person");

	// 准备SQL语句
	OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

	// 绑定输出列
	OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (ub1*)szpersonid,
		sizeof(szpersonid), SQLT_STR, &ind[0], 0, 0, OCI_DEFAULT);
	OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (ub1*)szname,
		sizeof(szname), SQLT_STR, &ind[1], 0, 0, OCI_DEFAULT);
	OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (ub1*)szphone,
		sizeof(szphone), SQLT_STR, &ind[2], 0, 0, OCI_DEFAULT);

	// 执行SQL语句
	ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, 0, NULL, NULL, OCI_DEFAULT);
	if (ret != 0)
	{
		OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
		printf("\n%s\n", errbuf);
	}
	printf("%-10s%-10s%-10s\n", "PERSONID", "NAME", "PHONE");
	while ((OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) != OCI_NO_DATA)
	{
		printf("%-10s", szpersonid);
		printf("%-10s", szname);
		printf("%-10s\n", szphone);
	}

	/************************************************************************/
	/* 向person表插入一条数据												*/
	/************************************************************************/

	memset(sql, 0, sizeof(sql));
	strcpy(sql, "insert into sysdba.person(sex, name, email, phone) values(:sex,:name,:email,:phone)");

	// 准备SQL语句
	OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

	// 设置输入参数
	memset(szsex, 0, sizeof(szsex));
	memcpy(szsex, "M", strlen("M") + 1);
	memset(szname, 0, sizeof(szname));
	memcpy(szname, "张三", strlen("张三") + 1);
	memset(szemail, 0, sizeof(szemail));
	memcpy(szemail, "zhangsan@dameng.com", strlen("zhangsan@dameng.com") + 1);
	memset(szphone, 0, sizeof(szphone));
	memcpy(szphone, "02712345678", strlen("02712345678") + 1);

	// 绑定输入列
	const OraText col_sex[] = ":sex";
	const OraText col_name[] = ":name";
	const OraText col_email[] = ":email";
	const OraText col_phone[] = ":phone";
	OCIBindByName(stmthp, &bidhp[0], errhp, col_sex, 4, szsex,
		strlen((char*)szsex), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
	OCIBindByName(stmthp, &bidhp[1], errhp, col_name, 5, szname,
		strlen((char*)szname), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
	OCIBindByName(stmthp, &bidhp[2], errhp, col_email, 6, szemail,
		strlen((char*)szemail), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
	OCIBindByName(stmthp, &bidhp[3], errhp, col_phone, 6, szphone,
		strlen((char*)szphone), SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

	// 执行SQL语句
	ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
		(CONST OCISnapshot*) 0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
	if (ret != 0)
	{
		OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
		printf("\n%s\n", errbuf);
	}
	else
	{
		printf("\n新增了 name = 张三 的记录。\n");
	}

	// 提交到数据库
	OCITransCommit(svchp, errhp, OCI_DEFAULT);


	/************************************************************************/
	/* 更新person表															*/
	/************************************************************************/

	memset(sql, 0, sizeof(sql));
	strcpy(sql, "update sysdba.person set sex='M',name='Liuhuan',email='12345678@qq.com',phone='13399990000' WHERE personid='1'");

	// 准备SQL语句		
	OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

	// 执行SQL语句
	ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
		(CONST OCISnapshot*)0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
	if (ret != 0)
	{
		OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
		printf("\n%s\n", errbuf);
	}
	else
	{
		printf("\n更新了 personid = 1 的记录。\n");
	}

	// 提交到数据库
	OCITransCommit(svchp, errhp, OCI_DEFAULT);


	/************************************************************************/
	/* 删除person表的ID为5的记录,首先要在数据库中存在这条记录				*/
	/************************************************************************/

	memset(sql, 0, sizeof(sql));
	strcpy(sql, "delete from sysdba.person WHERE personid=:1");

	// 准备SQL语句
	OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

	// 绑定输入参数
	memset(szpersonid, 0, sizeof(szpersonid));
	memcpy(szpersonid, "5", strlen("5") + 1);
	OCIBindByPos(stmthp, &bidhp[0], errhp, 1, szpersonid, strlen((char*)szpersonid),
		SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);

	// 执行SQL语句
	ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
		(CONST OCISnapshot*) 0, (OCISnapshot*)0, (ub4)OCI_DEFAULT);
	if (ret != 0)
	{
		OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
		printf("\n%s\n", errbuf);
	}
	else
	{
		printf("\n删除了 personid = 5 的记录。\n");
	}

	// 提交到数据库
	OCITransCommit(svchp, errhp, OCI_DEFAULT);


	/************************************************************************/
	/* 再次查询person表														*/
	/************************************************************************/

	strcpy(sql, "select personid, name, phone from sysdba.person");

	// 准备SQL语句
	OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

	// 绑定输出列
	OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (ub1*)szpersonid,
		sizeof(szpersonid), SQLT_STR, &ind[0], 0, 0, OCI_DEFAULT);
	OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (ub1*)szname,
		sizeof(szname), SQLT_STR, &ind[1], 0, 0, OCI_DEFAULT);
	OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (ub1*)szphone,
		sizeof(szphone), SQLT_STR, &ind[2], 0, 0, OCI_DEFAULT);

	// 执行SQL语句
	ret = OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, 0, NULL, NULL, OCI_DEFAULT);
	if (ret != 0)
	{
		OCIErrorGet(errhp, 1, NULL, &ret, (OraText*)errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
		printf("\n%s\n", errbuf);
	}
	printf("\n%-10s%-10s%-10s\n", "PERSONID", "NAME", "PHONE");
	while ((OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) != OCI_NO_DATA)
	{
		printf("%-10s", szpersonid);
		printf("%-10s", szname);
		printf("%-10s\n", szphone);
	}


	//结束会话
	OCISessionEnd(svchp, errhp, authp, (ub4)0);

	//断开与数据库的连接
	OCIServerDetach(srvhp, errhp, OCI_DEFAULT);

	//释放OCI句柄
	OCIHandleFree((dvoid*)dschp, OCI_HTYPE_DESCRIBE);
	OCIHandleFree((dvoid*)stmthp, OCI_HTYPE_STMT);
	OCIHandleFree((dvoid*)errhp, OCI_HTYPE_ERROR);
	OCIHandleFree((dvoid*)authp, OCI_HTYPE_SESSION);
	OCIHandleFree((dvoid*)svchp, OCI_HTYPE_SVCCTX);
	OCIHandleFree((dvoid*)srvhp, OCI_HTYPE_SERVER);
	system("pause");
	return 0;
}

四、项目引入所需的库

1. 引入头文件

右侧项目-点击头文件-添加,将压缩包中dmoci/include目录下的文件给添加进去

请添加图片描述

请添加图片描述

最终的效果如下

请添加图片描述

2. 引入资源文件

右侧项目-点击资源文件-添加现有项,将压缩包中dmoci/dmoci.lib文件给添加进去

请添加图片描述

请添加图片描述

五、配置项目

放到项目上,右键点击属性。

请添加图片描述

修改C++选项

1. 修改C++选项常规

  1. 点击C/C++
  2. 将最上方的配置修改为所有配置以及所有平台
  3. 附件包含目录,内容如下
D:\damba\dm8\include;D:\damba\win_64__dmdci\dmoci;D:\damba\win_64__dmdci\dmoci\include
  1. D:\damba\dm8\include; 其中D:\damba\dm8 本机达梦安装目录,也就是引入达梦安装目录的include
  2. D:\damba\win_64__dmdci\dmoci;其中D:\damba\win_64__dmdci 本机dci压缩解压后的路径,给改了个名
  3. D:\damba\win_64__dmdci\dmoci\include;其实就是引入dci\dmoci的includer

请添加图片描述

如果不确定包对不对的可以看着我这个对照一下dll

请添加图片描述

2. 修改C++选项所有选项

  1. 点击C/C++ — 所有选项
  2. 将最上方的配置修改为所有配置以及所有平台
  3. 附件包含目录,内容如下
D:\damba\dm8\include;D:\damba\win_64__dmdci\dmoci;D:\damba\win_64__dmdci\dmoci\include

请添加图片描述

修改链接器配置

1.修改链接器配置常规

  1. 点击链接器-常规
  2. 将最上方的配置修改为所有配置以及所有平台
  3. 附件库目录,内容如下
D:\damba\dm8\include;D:\damba\win_64__dmdci\dmoci;D:\damba\win_64__dmdci\dmoci\include

请添加图片描述

2.修改链接器配置输入

  1. 点击链接器-输入
  2. 将最上方的配置修改为所有配置以及所有平台
  3. 附件依赖项,注意;分割
dmoci.lib;

请添加图片描述

六、运行项目

如果出现找不到动态库请参考Q&A

请添加图片描述

Q&A

1.编译时错误:LNK2019

请添加图片描述

原因有两个:

  1. 用的Dci的库不是资料包里面的,就导致自己引用的dci库没有这些方法,所以报错
  2. dci的库项目编译时没有加载到项目里,重新按照修改链接器配置的部分对照修改

2.运行时错误:找不到动态库

请添加图片描述

这是因为没有将动态库文件拷贝到操作系统库目录去,操作系统在自己的环境变量目录中找不到需要的文件。

因为我是window系统,所以就将压缩包中dmoci目录下的所有东西拷贝到C:\Windows\System32的目录下

注意:拷贝过程中会有两个文件提示是否覆盖,这里选否即可

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
要在 Visual Studio 中添加达梦数据库DMDB)的 DCIDatabase Connectivity Interface)驱动,可以按照以下步骤进行操作: 1. 首先,确保已经安装了达梦数据库系统,并且拥有 DCI 驱动的安装文件。 2. 打开 Visual Studio 项目。 3. 在“解决方案资源管理器”窗口中,右键单击你的项目,并选择“属性”选项。 4. 在项目属性对话框中,选择“配置属性” > “C/C++” > “常规”。 5. 在右侧的选项中,找到“附加包含目录”(Additional Include Directories)选项,并添加包含 DCI 头文件的目录路径。这将告诉编译器在编译时搜索 DCI 头文件的位置。 6. 在“配置属性” > “链接器” > “常规”中,找到“附加库目录”(Additional Library Directories)选项,并添加包含 DCI 库文件的目录路径。这将告诉链接器在链接时搜索 DCI 库文件的位置。 7. 在“配置属性” > “链接器” > “输入”中,找到“附加依赖项”(Additional Dependencies)选项,并添加 DCI 驱动的库文件名。例如,如果驱动库文件名为 dci.lib,则输入“dci.lib”。这将告诉链接器在链接时使用指定的 DCI 驱动库。 完成上述步骤后,Visual Studio 将能够正确地编译和链接使用达梦数据库的代码,并在运行时加载和使用所需的 DCI 驱动。 需要注意的是,添加 DCI 驱动时,请确保驱动文件实际存在于指定的目录中,并且与你的项目兼容。另外,还需要根据具体的达梦数据库DCI 驱动的要求进行其他配置和操作。 希望对你有所帮助!如有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值