如何在 Windows 11 上运行和调试 Pro*C 程序?
结论
可以。Windows 11 本机无需安装 Oracle Database。准备好以下组件后,就可以完成 Pro*C 的预编译、C 编译、链接、运行和调试:
- Oracle Instant Client Basic
- Oracle Instant Client SDK
- Oracle Instant Client Precompiler
- Visual Studio 2022 的 Desktop development with C++ workload
- 可访问远程 Oracle 数据库的网络和账号
现有的 32-bit ODAC/OLE DB 驱动主要供 SSRS 使用,不能说明 ProC 环境已经完整。它通常不包含 proc.exe,也不一定包含 ProC 编译需要的头文件和导入库。
位数和版本混用最容易引发问题。proc.exe、Instant Client、SDK、链接库、生成的程序和 MSVC 目标平台必须统一为 x64 或 x86。使用 VS 2022 时,建议单独建立一套 x64 Instant Client 环境,不要直接修改现有的 32-bit SSRS 客户端。
应该下载哪些组件
从同一个 Oracle Instant Client 版本、同一个平台页面下载三个 ZIP 包:
- Basic Package,提供运行时 DLL。
- SDK Package,提供 C 头文件和链接库。
- Precompiler Package,提供
proc.exe、Pro*C 头文件、配置文件和示例。
Basic Light 可能不包含部分代码页和本地化支持。接手旧程序时,使用 Basic Package 通常省事一些。
另一种方案是安装完整的 Oracle Database Client,并在自定义安装中选择 Pro*C/C++ 或 Programmer 组件。完整客户端体积更大,也更容易与现有的 ODAC、Oracle Home、注册表配置和 PATH 冲突。如果只是搭建隔离的实验环境,Instant Client ZIP 包更容易管理。
Oracle 12c 数据库并不要求客户端也使用 12c。较新的客户端通常可以连接受支持的旧数据库,但仍要核对对应客户端版本的互操作性说明,并留意旧数据库使用的认证协议、字符集和加密策略。
某个 Oracle Client 版本是否正式支持 VS 2022,要以该版本的 Windows 平台说明为准。proc.exe 能够运行,并不代表整个工具链都在官方支持范围内。如果所选客户端不支持 VS 2022,可以安装该版本支持的 MSVC Build Tools,或改用支持较新 MSVC 的 Oracle Client。
建立隔离目录
假设将三个 ZIP 包解压并合并到:
C:\oracle\instantclient_19_23
合并后的目录结构通常类似:
C:\oracle\instantclient_19_23
├── oci.dll
├── proc.exe
├── sdk
│ ├── include
│ └── lib
└── precomp
├── admin
├── demo
└── public
实际目录会因客户端版本和压缩包布局而异,应以下载包中的文件为准。先搜索并确认以下文件的位置:
where proc
where oci.dll
where orasql*.lib
where oci.lib
不要随意选择搜索到的 .lib 文件进行链接。先查看 Precompiler Package 附带的 Windows 示例和构建脚本,确认该版本要求的库名。不同版本可能使用 orasql19.lib、其他带版本号的 orasql*.lib,也可能由示例指定相应的 OCI 库。
可以只为当前命令行临时设置环境,以免影响 SSRS:
set "IC_HOME=C:\oracle\instantclient_19_23"
set "PATH=%IC_HOME%;%PATH%"
set "TNS_ADMIN=C:\oracle\network\admin"
使用 Instant Client 时通常无需设置 ORACLE_HOME。如果机器上装有多套 Oracle Client,错误的 ORACLE_HOME 可能导致工具加载另一套配置。
运行以下命令,确认当前调用的是哪一个预编译器:
where proc
proc
先验证数据库连接
可以直接使用 Easy Connect,无需先创建 tnsnames.ora:
dbhost.example.com:1521/ORCLPDB1
最后一段通常是 service name,不一定是 SID。主机名、端口、service name、TLS 要求和账号权限应向数据库管理员确认。
如果旧程序依赖 TNS 别名,则在 %TNS_ADMIN%\tnsnames.ora 中添加配置:
REPORTDB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = ORCLPDB1)
)
)
如果安装包中带有 SQLPlus,可以先用它测试连接。ProC 本身不依赖 SQL*Plus,但先用已有的 Oracle 工具排除网络、服务名和账号问题,后续排查会更容易。
最小 Pro*C 示例
下面的程序只连接数据库并查询 DUAL:
#include <stdio.h>
#include <string.h>
EXEC SQL INCLUDE sqlca;
static void print_sql_error(void)
{
fprintf(
stderr,
"Oracle error %ld: %.*s\n",
sqlca.sqlcode,
(int)sqlca.sqlerrm.sqlerrml,
sqlca.sqlerrm.sqlerrmc
);
}
int main(int argc, char **argv)
{
VARCHAR username[128];
VARCHAR password[128];
VARCHAR connect_string[256];
int value = 0;
if (argc != 4) {
fprintf(stderr, "Usage: sample.exe username password host:port/service\n");
return 2;
}
username.len = (unsigned short)strlen(argv[1]);
memcpy(username.arr, argv[1], username.len);
password.len = (unsigned short)strlen(argv[2]);
memcpy(password.arr, argv[2], password.len);
connect_string.len = (unsigned short)strlen(argv[3]);
memcpy(connect_string.arr, argv[3], connect_string.len);
EXEC SQL WHENEVER SQLERROR DO print_sql_error();
EXEC SQL CONNECT :username
IDENTIFIED BY :password
USING :connect_string;
if (sqlca.sqlcode < 0) {
return 1;
}
EXEC SQL SELECT 1 INTO :value FROM dual;
if (sqlca.sqlcode < 0) {
EXEC SQL ROLLBACK WORK RELEASE;
return 1;
}
printf("Value: %d\n", value);
EXEC SQL COMMIT WORK RELEASE;
return 0;
}
将文件保存为 sample.pc。示例通过命令行接收密码,仅用于演示。正式程序不要把密码写入源码、项目属性、批处理文件或版本库。命令行参数也可能被本机的其他工具读取。
预编译为 C
在适用于 x64 的 “Developer Command Prompt for VS 2022” 中运行:
proc INAME=sample.pc ONAME=sample.c LINES=YES
如果预编译器找不到头文件,可以显式指定目录:
proc INAME=sample.pc ONAME=sample.c LINES=YES ^
INCLUDE="C:\oracle\instantclient_19_23\sdk\include" ^
INCLUDE="C:\oracle\instantclient_19_23\precomp\public"
proc.exe 还会读取 pcscfg.cfg。该文件一般位于 precomp\admin 下,可能需要在其中设置 Microsoft C 头文件路径,例如 sys_include。不同 Oracle Client 版本的配置格式和默认内容可能不同。先复制安装包自带的配置,再根据需要局部修改。
仅做语法级预编译时通常无需连接数据库。使用 SQLCHECK=SEMANTICS 检查数据库对象和 SQL 语义时,预编译阶段可能需要数据库连接和相应权限,例如:
proc INAME=sample.pc ONAME=sample.c SQLCHECK=SEMANTICS USERID=user@REPORTDB
不要把密码直接写入构建日志。首次搭建环境时,可以先使用默认设置或 SQLCHECK=SYNTAX,等编译链正常运行后再考虑语义检查。
使用 MSVC 编译和链接
先找到 Precompiler Package 提供的导入库。如果该版本附带 orasql19.lib,命令可能如下:
cl /nologo /TC /Zi /Od /MD ^
/I"C:\oracle\instantclient_19_23\sdk\include" ^
/I"C:\oracle\instantclient_19_23\precomp\public" ^
sample.c ^
/link /DEBUG ^
/LIBPATH:"C:\oracle\instantclient_19_23\sdk\lib\msvc" ^
orasql19.lib ^
/OUT:sample.exe
各参数的作用如下:
/TC强制按 C 源文件编译。/Zi生成调试信息。/Od关闭优化,便于单步调试。/MD使用动态 C 运行库。/DEBUG生成调试所需的 PDB 信息。
库名和 /LIBPATH 不能直接照抄,应根据下载版本中的 precomp\demo 示例、README 或 makefile 调整。如果目录中只有 oci.lib,也不能直接认定它可以替代 Pro*C 示例要求的 orasql*.lib。
运行程序时,oci.dll 所在目录必须包含在 PATH 中:
sample.exe report_user password dbhost.example.com:1521/ORCLPDB1
如果程序启动时报“找不到 DLL”或 0xc000007b,先执行以下检查:
where oci.dll
where sample.exe
dumpbin /headers sample.exe | findstr machine
0xc000007b 通常表示 x86 与 x64 混用,或程序加载了另一套 Oracle Client DLL。
在 Visual Studio 2022 中调试
可以先在命令行生成 sample.c,再按以下步骤配置项目:
- 创建一个 Empty Project。
- 将
sample.c加入项目。 - 将项目平台设置为
x64。 - 在 Additional Include Directories 中加入 SDK 和
precomp\public。 - 在 Additional Library Directories 中加入对应的 MSVC 库目录。
- 在 Additional Dependencies 中加入 Oracle 示例要求的
.lib。 - 在 Debugging 的 Environment 中设置正确的
PATH和TNS_ADMIN。 - 在 Command Arguments 中填写测试参数。
完成后,可以在生成的 C 文件、普通 C 代码、错误处理函数和 Oracle 调用附近设置断点,并观察主机变量、返回值、sqlca.sqlcode 和 sqlca.sqlerrm 等信息。
Pro*C 会将 EXEC SQL 转换成底层运行库调用,所以调试器无法像执行普通 C 语句那样逐层执行一条 SQL。LINES=YES 会保留源行映射信息,方便定位编译和调试问题。不过,能否在 .pc 文件上稳定设置断点,取决于 proc.exe 生成的 #line 指令、MSVC 版本和 PDB 映射。实际排查时通常仍要查看生成的 .c 文件。
环境稳定后,可以将 .pc 文件配置为 Visual Studio 的 Custom Build Tool:
"$(OracleInstantClient)\proc.exe" ^
INAME="%(FullPath)" ^
ONAME="$(IntDir)%(Filename).c" ^
LINES=YES
输出声明为:
$(IntDir)%(Filename).c
还要确认生成的 .c 文件确实进入了后续编译流程。Visual Studio 对动态生成源文件的项目配置比较繁琐,刚开始可以分别执行“预编译”和“C 编译”,这样更容易判断错误出在哪一步。
旧代码常见的兼容问题
旧 Pro*C 项目即使安装环境正确,也可能遇到以下问题:
- 源代码依赖旧版 Oracle 头文件、宏或运行库。
- 代码按 32-bit 类型宽度编写,在 x64 下出现指针或整数截断。
- 原项目使用特定的
MODE、DBMS、CHAR_MAP、CODE或PARSE选项。 pcscfg.cfg中保存了已经不存在的 Visual C++ 目录。- 老代码包含 Unix 专用头文件、信号处理、路径或 shell 调用。
- 程序依赖外部配置文件、报表模板、环境变量或批处理脚本。
- SQL 使用数据库中的同义词、数据库链接、临时表或特定 NLS 设置。
- 连接可以成功,但较新的客户端与旧数据库之间存在认证协议限制。
不要一开始就大范围修改源码。先找到原有的 makefile、构建日志、批处理文件、pcscfg.cfg 和部署说明,确认项目使用的 Oracle Client 版本、预编译参数、位数和链接库。Pro*C 的行为由 .pc 文件和预编译参数共同决定,预编译参数也是构建结果的一部分。
更安全的验证方式
调试时应使用开发库、测试库或数据库快照,不要直接使用生产库。即使程序名称或用途看起来只是“生成报表”,旧程序也可能执行以下操作:
- 创建或清理临时表。
- 更新任务状态和运行日志。
- 调用带副作用的存储过程。
- 执行
COMMIT。 - 生成、覆盖或删除本地文件。
首次运行前,先搜索 INSERT、UPDATE、DELETE、MERGE、COMMIT、ROLLBACK、EXECUTE 和动态 SQL,同时检查程序是否调用了其他可执行文件或脚本。数据库账号最好只授予验证所需的最小权限。
建议先建立独立的 x64 Instant Client 目录,再验证远程连接并运行最小 Pro*C 示例,最后按照旧项目原有的预编译参数逐步接入代码。按这个顺序操作,可以分别排查客户端安装、MSVC 链接和旧代码兼容问题。
备注:内容仅供参考。
