编译环境:Windows XP + Visual Studio 2010
数据库:Access 2010,accdb格式

主代码(main.c):
请注意db_row_count函数(也就是SQLRowCount这个Windows API函数)只能用于获取INSERT、UPDATE和DELETE语句影响的行数,无法获取SELECT语句查询返回的记录集的行数。

#include <stdio.h>
#include "db.h"struct product
{int id;char name[50];double price;int remaining;
};int main(void)
{int cnt, ret;struct product product;Statement stmt;// 反斜杠一定要双写!!!!ret = db_connect_a("D:\\My Documents\\Visual Studio 2010\\Projects\\mdbtest\\mdbtest\\测试.accdb");if (ret == -1){printf("%s\n", db_geterror_a());return -1;}ret = db_has_records_a("SELECT * FROM 商品 WHERE 商品名称 = '铅笔'");if (ret == 1){stmt = db_prepare_a("UPDATE 商品 SET 商品库存 = 商品库存 - 1 WHERE 商品名称 = ? AND 商品库存 > 0");db_set_str_a(stmt, 1, "铅笔");ret = db_exec_stmt(stmt, 0);if (ret == 0)printf("已减%d个商品的库存\n", db_row_count(stmt));elseprintf("库存不足\n");db_free(stmt);}else if (ret == 0){ret = db_exec_a("INSERT INTO 商品 (商品名称, 商品价格, 商品库存) VALUES ('铅笔', 3.5, 4)", &cnt);if (ret == 0)printf("已新增%d个商品\n", cnt);else if (ret == -1)printf("插入记录失败\n");}else if (ret == -1)printf("检查失败!\n");stmt = db_query_a("SELECT 商品编号, 商品名称, 商品价格, 商品库存 FROM 商品 ORDER BY 商品编号 DESC");// 这里用不了db_row_count函数if (stmt != NULL){db_bind_str_a(stmt, 2, product.name, sizeof(product.name));db_bind_double(stmt, 3, &product.price);while (db_fetch(stmt) == 1){product.id = db_get_int(stmt, 1);product.remaining = db_get_int(stmt, 4);printf("[%d] %s %.2lf %d\n", product.id, product.name, product.price, product.remaining);}db_free(stmt);}db_disconnect();return 0;
}

db.h:

#include <WTypes.h>typedef void *Statement;/* 数据库连接与断开 */
int db_connect(LPCTSTR dbname);
int db_connect_a(const char *dbname);
void db_disconnect(void);
LPCTSTR db_geterror(void);
const char *db_geterror_a(void);/* Prepared Statement 相关 */
Statement db_prepare(LPTSTR sql);
int db_exec_stmt(Statement stmt, int free);
// 设置SQL语句中的问号
void db_set_int(Statement stmt, int i, int *p);
void db_set_str_a(Statement stmt, int i, char *s);
void db_set_str_w(Statement stmt, int i, wchar_t *ws);/* 直接执行查询,不Prepare */
Statement db_query(LPTSTR sql); // 执行SELECT查询,需要手动释放资源
int db_row_count(Statement stmt);
int db_exec(LPTSTR sql, int *cnt); // 执行普通查询
int db_has_records(LPTSTR sql); // 直接执行SELECT查询,判断结果集是否有记录
void db_free(Statement stmt); // 释放db_query的资源int db_exec_func_a(int (*func1)(LPTSTR sql), int (*func2)(LPTSTR sql, int *cnt), char *sql, int *cnt);
#define db_exec_a(sql, cnt) db_exec_func_a(NULL, db_exec, (sql), (cnt))
#define db_has_records_a(sql) db_exec_func_a(db_has_records, NULL, (sql), NULL)
Statement db_stmt_func_a(Statement (*func)(LPTSTR sql), char *sql);
#define db_prepare_a(sql) db_stmt_func_a(db_prepare, (sql))
#define db_query_a(sql) db_stmt_func_a(db_query, (sql))/* 记录集操作 */
int db_fetch(Statement stmt); // 获取一行记录
// 绑定字段到变量上
void db_bind_int(Statement stmt, int col, int *p); // 以整数类型保存第col列的内容
void db_bind_str_a(Statement stmt, int col, char *buf, int len);
void db_bind_str_w(Statement stmt, int col, wchar_t *buf, int len); // 以字符串类型保存第col列的内容
void db_bind_float(Statement stmt, int col, float *p); // 以小数类型保存第col列的内容
void db_bind_double(Statement stmt, int col, double *p);int db_get_int(Statement stmt, int col); // 直接以整数类型读取第col列的内容#ifdef _UNICODE
#define db_bind_str db_bind_str_w
#define db_set_str db_set_str_w
#else
#define db_bind_str db_bind_str_a
#define db_set_str db_set_str_a
#endif

db.c:

/*
这个文件里封装了很多操作数据库的函数
参考资料:https://msdn.microsoft.com/en-us/library/ms714562%28v=vs.85%29.aspx
*/#include <tchar.h>
#include <stdio.h>
#include <Windows.h>
#include <sqlext.h>
#include "encoding.h"
#include "db.h"#define DB_DSN TEXT("Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=%s;")
#define DB_MAXERRLEN 500static SQLHENV hEnv;
static SQLHDBC hConn;
static TCHAR szErrorMsg[DB_MAXERRLEN];int db_connect(LPCTSTR dbname)
{TCHAR szDSN[MAX_PATH + 100]; // 数据库连接字符串TCHAR szCode[6]; // 错误代码TCHAR szMsg[256]; // 错误信息SQLRETURN rc;SQLAllocHandle(SQL_HANDLE_ENV, NULL, &hEnv);SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hConn);_stprintf_s(szDSN, _countof(szDSN), DB_DSN, dbname); // 根据数据库文件名生成数据库连接字符串rc = SQLDriverConnect(hConn, NULL, szDSN, SQL_NTS, NULL, 0, NULL, 0); // 根据连接字符串连接数据库if (!SQL_SUCCEEDED(rc)){// 连接失败的错误提示rc = SQLGetDiagRec(SQL_HANDLE_DBC, hConn, 1, szCode, NULL, szMsg, _countof(szMsg), NULL);if (SQL_SUCCEEDED(rc))_stprintf_s(szErrorMsg, DB_MAXERRLEN, TEXT("无法连接数据库!\n错误代码: %s\n错误信息: %s"), szCode, szMsg);elselstrcpy(szErrorMsg, TEXT("未知错误!"));db_disconnect();return -1; // 连接失败时,函数返回-1}return 0; // 连接成功时,函数返回0
}int db_connect_a(const char *dbname)
{
#ifdef _UNICODEint ret;wchar_t *dbname_w;dbname_w = ANSItoUTF16(dbname);if (dbname_w == NULL){lstrcpy(szErrorMsg, TEXT("系统内存不足!"));return -1;}ret = db_connect(dbname_w);free(dbname_w);return ret;
#elsereturn db_connect(dbname);
#endif
}void db_disconnect(void)
{SQLDisconnect(hConn);SQLFreeHandle(SQL_HANDLE_DBC, hConn);SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}LPCTSTR db_geterror(void)
{return szErrorMsg;
}const char *db_geterror_a(void)
{
#ifdef _UNICODEstatic char msg[DB_MAXERRLEN];UTF16toANSIBuffer(szErrorMsg, msg, DB_MAXERRLEN);return msg;
#elsereturn szErrorMsg;
#endif
}Statement db_prepare(LPTSTR sql)
{SQLHSTMT hStmt;SQLAllocHandle(SQL_HANDLE_STMT, hConn, &hStmt);SQLPrepare(hStmt, sql, lstrlen(sql));return hStmt;
}int db_exec_stmt(Statement stmt, int free)
{SQLRETURN rc;rc = SQLExecute(stmt);if (free)db_free(stmt);if (!SQL_SUCCEEDED(rc))return -1;return 0;
}void db_set_int(Statement stmt, int i, int *p)
{static SQLLEN size = sizeof(int); // 该变量的地址必须固定, 所以要加staticSQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, p, 0, &size); // 整型变量的地址也必须固定
}void db_set_str_a(Statement stmt, int i, char *s)
{static SQLLEN len = SQL_NTS;SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, strlen(s), 0, s, 0, &len);
}void db_set_str_w(Statement stmt, int i, wchar_t *ws)
{static SQLLEN len = SQL_NTS;SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_VARCHAR, wcslen(ws) * sizeof(wchar_t), 0, ws, 0, &len);
}Statement db_query(LPTSTR sql)
{SQLRETURN rc;Statement stmt;stmt = db_prepare(sql);rc = SQLExecute(stmt);if (SQL_SUCCEEDED(rc))return stmt;else{db_free(stmt);return NULL;}
}int db_row_count(Statement stmt)
{SQLLEN len;SQLRETURN rc;rc = SQLRowCount(stmt, &len);if (SQL_SUCCEEDED(rc))return len;elsereturn -1;
}int db_exec(LPTSTR sql, int *cnt)
{Statement stmt;stmt = db_query(sql);if (stmt != NULL){if (cnt != NULL)*cnt = db_row_count(stmt);db_free(stmt);return 0;}elsereturn -1;
}int db_has_records(LPTSTR sql)
{int ret;Statement stmt;stmt = db_query(sql);if (stmt == NULL)return 0;ret = db_fetch(stmt);db_free(stmt);return ret;
}void db_free(Statement stmt)
{SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}int db_exec_func_a(int (*func1)(LPTSTR sql), int (*func2)(LPTSTR sql, int *cnt), char *sql, int *cnt)
{int ret = -1;
#ifdef _UNICODEwchar_t *sql_w;sql_w = ANSItoUTF16(sql);if (sql_w == NULL)return -1;if (func1 != NULL)ret = func1(sql_w);else if (func2 != NULL)ret = func2(sql_w, cnt);free(sql_w);
#elseif (func1 != NULL)ret = func1(sql);else if (func2 != NULL)ret = func2(sql, cnt);
#endifreturn ret;
}Statement db_stmt_func_a(Statement (*func)(LPTSTR sql), char *sql)
{
#ifdef _UNICODEwchar_t *sql_w;Statement stmt;sql_w = ANSItoUTF16(sql);if (sql_w == NULL)return NULL;stmt = func(sql_w);free(sql_w);return stmt;
#elsereturn func(sql);
#endif
}int db_fetch(Statement stmt)
{SQLRETURN rc;rc = SQLFetch(stmt);if (rc == SQL_SUCCESS)return 1;else if (rc == SQL_NO_DATA)return 0;elsereturn -1;
}void db_bind_int(Statement stmt, int col, int *p)
{SQLBindCol(stmt, col, SQL_C_LONG, p, sizeof(int), NULL);
}void db_bind_str_a(Statement stmt, int col, char *buf, int len)
{SQLBindCol(stmt, col, SQL_C_CHAR, buf, len, NULL);
}void db_bind_str_w(Statement stmt, int col, wchar_t *buf, int len)
{SQLBindCol(stmt, col, SQL_C_WCHAR, buf, len, NULL);
}void db_bind_float(Statement stmt, int col, float *p)
{SQLBindCol(stmt, col, SQL_C_FLOAT, p, sizeof(float), NULL);
}void db_bind_double(Statement stmt, int col, double *p)
{SQLBindCol(stmt, col, SQL_C_DOUBLE, p, sizeof(double), NULL);
}int db_get_int(Statement stmt, int col)
{int n;SQLRETURN ret;ret = SQLGetData(stmt, col, SQL_C_LONG, &n, sizeof(int), NULL);if (SUCCEEDED(ret))return n;elsereturn 0;
}

encoding.h:

char *ANSItoUTF8(const char *s);
wchar_t *ANSItoUTF16(const char *s);
char *UTF16to8(const wchar_t *ws);
char *UTF16toANSI(const wchar_t *ws);
char *UTF16toANSIBuffer(const wchar_t *ws, char *buf, int bufsize);
wchar_t *UTF8to16(const char *s);
char *UTF8toANSI(const char *s);#ifdef _UNICODE
#define UTF8toTSTR(s) UTF8to16(s)
#define TSTRtoUTF8(ws) UTF16to8(ws)
#else
#define UTF8toTSTR(s) UTF8toANSI(s)
#define TSTRtoUTF8(s) ANSItoUTF8(s)
#endif

encoding.c:

/*
本文件主要负责处理字符编码转换。
Visual Studio项目属性中有一个选项叫做“字符集”,当选择“使用 Unicode 字符集”时,则“#ifdef _UNICODE”条件成立。
char字符数组主要存放ANSI和UTF8编码的字符串,ANSI是GB2312、GBK、Big5等单国语言字符集的统称。
wchar_t字符数组主要存放UTF16编码的字符串。
UTF8、UTF16、UTF32都是Unicode字符集,每个字符的编号是相同的,只是存储方式不同。对于基本多语言平面的字符来说,UTF8是变长存储,UTF16和32是定长存储。
char str[] = "ABC";
wchar_t wstr[] = L"ABC";当“#ifdef _UNICODE”条件成立时,TCHAR等于wchar_t。不成立时,TCHAR等于char。
TCHAR tstr[] = TEXT("ABC");
LPTSTR等于TCHAR *,LPCTSTR等于const TCHAR *。
*/
#include <Windows.h>
#include "encoding.h"char *ANSItoUTF8(const char *s)
{char *t;wchar_t *ws;ws = ANSItoUTF16(s);if (ws == NULL)return NULL;t = UTF16to8(ws);free(ws);return t;
}wchar_t *ANSItoUTF16(const char *s)
{int n;wchar_t *ws;n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);ws = (wchar_t *)malloc(n * sizeof(wchar_t));if (ws == NULL)return NULL;MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);return ws;
}char *UTF16to8(const wchar_t *ws)
{int n;char *s;n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);s = (char *)malloc(n * sizeof(char));if (s == NULL)return NULL;WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL);return s;
}char *UTF16toANSI(const wchar_t *ws)
{int n;char *s;n = WideCharToMultiByte(CP_ACP, 0, ws, -1, NULL, 0, NULL, NULL);s = (char *)malloc(n * sizeof(char));if (s == NULL)return NULL;WideCharToMultiByte(CP_ACP, 0, ws, -1, s, n, NULL, NULL);return s;
}char *UTF16toANSIBuffer(const wchar_t *ws, char *buf, int bufsize)
{WideCharToMultiByte(CP_ACP, 0, ws, -1, buf, bufsize, NULL, NULL);return buf;
}wchar_t *UTF8to16(const char *s)
{int n;wchar_t *ws;n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);ws = (wchar_t *)malloc(n * sizeof(wchar_t));if (ws == NULL)return NULL;MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, n);return ws;
}char *UTF8toANSI(const char *s)
{char *t;wchar_t *ws;ws = UTF8to16(s);if (ws == NULL)return NULL;t = UTF16toANSI(ws);free(ws);return t;
}

C语言通过ODBC函数操作Access数据库(mdb和accdb格式)相关推荐

  1. Java操作Access数据库使用方法及案例 及 所需jar包【源码及jar包在最后下载】

    目录 写在前面 数据库编辑软件MDBplus.exe SQL查询语句的差异 字符串转数字 字符串转日期 示例:如下图打开SQL编辑窗口 源码及jar包下载地址 写在前面 最近接了一个项目,就是个简单的 ...

  2. golang odbc mysql_go语言通过odbc操作Access数据库的方法

    本文实例讲述了go语言通过odbc操作Access数据库的方法.分享给大家供大家参考.具体如下: 这里需要用到go-odbc库,下载地址为:https://github.com/weigj/go-od ...

  3. MFC+ODBC操作Access数据库pdf版

    <MFC+ODBC操作Access数据库pdf版> 下载地址: 网盘下载 转载于:https://www.cnblogs.com/long12365/p/9731370.html

  4. 直接通过ADO操作Access数据库(修改版)

    自身的OLE DB Privider,而且还可以应用所有的ODBC驱动程序.关于OLE DB和ADO的其它详细情况,读者可以自行查阅相关书籍或MSDN,这里就不一一说明了.让我们直接步入主题:如何掌握 ...

  5. python不可以操作access数据库_Python操作Access数据库基本操作步骤分析

    Python编程语言的出现,带给开发人员非常大的好处.我们可以利用这样一款功能强大的面向对象开源语言来轻松的实现许多特定功能需求.比如Python操作Access数据库的功能实现等等.在Python操 ...

  6. DELPHI中操作ACCESS数据库

    DELPHI中操作ACCESS数据库(建立.mdb文件,压缩数据库) 以下代码在WIN2K,D6,MDAC2.6下测试通过, 编译好的程序在WIN98第二版无ACCESS环境下运行成功. //在之前u ...

  7. 转:关于ASP操作Access数据库时出现死锁.ldb的解决方法

    Asp操作Access数据库时出现死锁.ldb导致网站访问缓慢的问题描述 最近asp网站出现数据库错误,在ftp登陆后发现原来的后缀MDB文件多了一个后缀LDB文件,是自动被锁,在一天某个时间段内打不 ...

  8. Asp 操作Access数据库时出现死锁.ldb的解决方法

    问题: 最近经常用的asp网站"搬家"到一个昂贵的服务器时候,出现这个问题,访问后台时候,出现数据库错误的字样,在ftp登陆后发现原来的后缀MDB文件多了一个后缀LDB文件,百度搜 ...

  9. VB.NET 使用 OleDb 操作 Access 数据库(来自 MSDN)

    VB.NET 使用 OleDb 操作 Access 数据库 这里采用 OleDb 方式对 Access 数据库进行访问,主要内容都来自 MSDN 帮助文档.建议新手注意 MSDN 的用途.下面的代码只 ...

最新文章

  1. multiprocessing多进程(31-04)创建进程的两种方式
  2. 微软编程题:寻找最小的k个值
  3. 用VC++实现console程序显示彩色文本
  4. 【转】如何修改IIS的默认端口号
  5. 在Blazor中构建数据库应用程序——第1部分——项目结构和框架
  6. Nginx部署前后端分离项目,配置SSL证书,结果刷新报500异常
  7. 理解 Android 的 ONE_SHOT_MAKEFILE
  8. python pandas 教程_Python pandas十分钟教程
  9. Apache服务器和tomcat服务器有什么区别(转)
  10. 家用台式电脑计算机上的硬磁盘,台式电脑如何连接硬盘
  11. 算法训练 - 黑色星期五 有些西方人比较迷信,如果某个月的13号正好是星期五,他们就会觉得不太吉利,用古人的说法,就是“诸事不宜”。请你编写一个程序,统计出在某个特定的年份中,出现了多少次既是13号又
  12. mac系统ps快捷键大全-来自三人行慕课
  13. 同建金融IT新生态——令克软件富途证券达成战略合作
  14. DQN-[Playing Atari with Deep Reinforcement Learning]
  15. python--斗地主
  16. 【力扣Hot100】155. 最小栈
  17. NetworkMiner主机指纹识别原理
  18. 解决 wsl Cannot Connect to X display 和 Message bochs cannot connect to X server localhost0.0
  19. Python.习题四 循环结构
  20. STM32——ADC采集

热门文章

  1. 项目分包后出的测评报告能盖cnas/cma标识章吗
  2. 微信识别二维码下载不了app
  3. Android视频编辑SDK--RDVECore来自锐动的无UI,高度抽象化API
  4. 三极管和MOS管开关速度谁快呀
  5. Spring-day1
  6. 在ObjectARX中使用MFC-使用MfcGridCtrl
  7. 502 bad gateway这是什么意思_2020年11月11日将出现“水星西大距”,这是什么意思?...
  8. 抖音表情制作方法 动态GIF怎么玩
  9. 高通平台蓝牙--BLE 配置文件的日志示例
  10. Small Cell的基本概念