OCI(Oracle Call Interface)是一种用于访问数据库的底层C语言API接口。主要被用于与达梦数据库(DM)进行交互,支持执行SQL语句、事务管理、绑定参数、获取结果集等功能。有时为了复现dmoci应用问题,需要整理简单的demo,使用Visual Studio 2022 直接新建项目,更方便调试。本文档包括新建项目到调试流程,适合新手参考。
Visual Studio无原生C项目模板,需借助“C++”空项目并配置为C模式。
注:如果无“c++”空项目,原因是安装时没有选择“C++”对应的模块,运行Visual Studio Installer,点击[修改]
勾选[使用C++的桌面开发],点击[修改]
拷贝如下代码到TEST_OCI.cpp。
注意:需手动创建表并插入测试数据。
调试时需配置实际ip、port和账号密码。
/************************************************************************/
/* DCI编程实例
create table sysdba.person(personid varchar(20), sex varchar(20), name varchar(20), email varchar(20), phone varchar(20));
insert into sysdba.person(personid,sex, name, email, phone) values('111','FM','张三','z@g.com','027-00');
commit;
*/
/************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "DCI.h"
/* 声明句柄 */
OCIEnv* envhp; /* 环境句柄 */
OCISvcCtx* svchp; /* 服务环境句柄 */
OCIServer* srvhp; /* 服务器句柄 */
OCISession* authp; /* 会话句柄 */
OCIStmt* stmthp; /* 语句句柄 */
OCIDescribe* dschp; /* 描述句柄 */
OCIError* errhp; /* 错误句柄 */
OCIDefine* defhp[3]; /* 定义句柄 */
OCIBind* bidhp[4]; /* 绑定句柄 */
sb2 ind[3]; /* 指示符变量 */
/* 绑定select结果集的参数 */
text szpersonid[11]; /* 存储personid列 */
text szsex[20]; /* 存储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:5237");
strcpy(strUserName, "SYSDBA");
strcpy(strPassword, "Dameng123");
/* 初始化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, (sb4)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("%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("DEBUG: FETCH ROW \n");
printf("%-10s", (char*)szpersonid);
printf("%-10s", (char*)szname);
printf("%-10s\n", (char*)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, (sb4)strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
/* 设置输入参数 */
memset(szsex, 0, sizeof(szsex));
strcpy((char*)szsex, "FM");
memset(szname, 0, sizeof(szname));
strcpy((char*)szname, "张三");
memset(szemail, 0, sizeof(szemail));
strcpy((char*)szemail, "z@g.com");
memset(szphone, 0, sizeof(szphone));
strcpy((char*)szphone, "027-00");
/* 绑定输入列 */
OCIBindByName(stmthp, &bidhp[0], errhp, (text*)":sex", 4, (dvoid*)szsex, (sb4)strlen((char*)szsex),
SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
OCIBindByName(stmthp, &bidhp[1], errhp, (text*)":name", 5, (dvoid*)szname, (sb4)strlen((char*)szname),
SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
OCIBindByName(stmthp, &bidhp[2], errhp, (text*)":email", 6, (dvoid*)szemail, (sb4)strlen((char*)szemail),
SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
OCIBindByName(stmthp, &bidhp[3], errhp, (text*)":phone", 6, (dvoid*)szphone, (sb4)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("%s\n", errbuf);
}
/* 提交到数据库 */
OCITransCommit(svchp, errhp, OCI_DEFAULT);
//结束会话
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);
return 0;
}
复制源码后因为缺少依赖,会有大量的报错,需添加依赖。
1)编译过程中需要用到头文件
头文件包含了程序中使用的函数、类、宏等的声明。在编译过程中,编译器会引用这些头文件以确保代码的正确性。
达梦数据库相关头文件:
DPI.h:DPI 接口的主头文件。
DPIext.h:扩展功能的头文件。
DPItypes.h:定义了 DPI 接口使用的数据类型。
fldr.h:快速装载接口的头文件。
DPIucode.h:UTF-16 编码支持的头文件。
2)在链接阶段,需要指定库文件的路径。
库文件包含了程序中使用的函数、类等的实现。在链接阶段,链接器会将这些库文件与编译生成的目标文件进行链接,生成最终的可执行文件。
达梦数据库相关库文件:
dmdpi.lib:DPI 接口的静态库文件。
dmfldr_dll.lib:快速装载接口的静态库文件。
3)动态库文件在程序运行时被加载,用于提供程序运行所需的函数和类的实现。
达梦Windows环境相关动态库文件:
dmdpi.dll:DPI 接口的动态库文件。
dmfldr_dll.dll:快速装载接口的动态库文件。
在程序运行时,需要确保动态库文件所在的目录被包含在环境变量中:
Windows:将动态库路径添加到 PATH 环境变量中。
这些动态库在dm8_20260401_x86_win_64_ent_8.1.4.200_pack2_dmdci.zip中,需先手动下载,再按需配置。也可以直接将dmdci.zip中的所有文件直接拷贝到项目的DEBUG目录(C:\DM\oci\TEST_OCI\x64\Debug),这样就不需要配置环境变量了。
右击项目,打开属性,
C/C++ -> 常规 -> 附加包含目录 -> C:\dmdbms\include
链接器 -> 附加库目录 -> c:\dmdbms\include
链接器 -> 输入 -> 附加依赖项 -> dmoci.lib
以防万一我把所有选项都配置了下。
如果报strcpy不全的错误,可以右击项目属性->C/C+±>预处理器->预处理器定义中添加:_CRT_SECURE_NO_WARNINGS
初始源码为:
strcpy(szsex, "FM");
OCIBindByName(stmthp, &bidhp[0], errhp, ":sex", 4, szsex, strlen((char*)szsex),
SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
需添加转义:
strcpy((char*)szsex, "FM");
OCIBindByName(stmthp, &bidhp[0], errhp, (text*)":sex", 4, (dvoid*)szsex, (sb4)strlen((char*)szsex),
SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
共4个参数,所以有4个报错,都通过添加转义解决。
忘记配置环境变量,为了省事,直接将dmdci.zip中的所有文件直接拷贝到项目的DEBUG目录(C:\DM\oci\TEST_OCI\x64\Debug)绕过。
详细报错:
生成开始于 14:41...
1>------ 已启动生成: 项目: TEST_OCI, 配置: Debug x64 ------
1>oci.cpp
1>C:\DM\oci\TEST_OCI\oci.cpp(109,76): warning C4267: “参数”: 从“size_t”转换到“dsb4”,可能丢失数据
1>C:\DM\oci\TEST_OCI\oci.cpp(111,78): warning C4267: “参数”: 从“size_t”转换到“dsb4”,可能丢失数据
1>C:\DM\oci\TEST_OCI\oci.cpp(113,80): warning C4267: “参数”: 从“size_t”转换到“dsb4”,可能丢失数据
1>C:\DM\oci\TEST_OCI\oci.cpp(115,80): warning C4267: “参数”: 从“size_t”转换到“dsb4”,可能丢失数据
1>TEST_OCI.vcxproj -> C:\DM\oci\TEST_OCI\x64\Debug\TEST_OCI.exe
1>已完成生成项目“TEST_OCI.vcxproj”的操作。
========== 生成: 1 成功,0 失败,0 最新,0 已跳过 ==========
========== 生成 于 14:41 完成,耗时 09.547 秒 ==========
解决方案:
原始代码:
OCIStmtPrepare(stmthp, errhp, (text*)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
添加(sb4)
OCIStmtPrepare(stmthp, errhp, (text*)sql, (sb4)strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
有未关闭的后台窗口,例如:
关闭旧后台窗口即可。
文章
阅读量
获赞
