PAYATHON 2026

如何在 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 包:

  1. Basic Package,提供运行时 DLL。
  2. SDK Package,提供 C 头文件和链接库。
  3. 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,再按以下步骤配置项目:

  1. 创建一个 Empty Project。
  2. 将 sample.c 加入项目。
  3. 将项目平台设置为 x64。
  4. 在 Additional Include Directories 中加入 SDK 和 precomp\public。
  5. 在 Additional Library Directories 中加入对应的 MSVC 库目录。
  6. 在 Additional Dependencies 中加入 Oracle 示例要求的 .lib。
  7. 在 Debugging 的 Environment 中设置正确的 PATH 和 TNS_ADMIN。
  8. 在 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 链接和旧代码兼容问题。

备注:内容仅供参考。