You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python调用C编译DLL的hello函数触发OSError访问违例问题排查

问题:调用DLL中printf函数触发访问违例错误

执行python build.py可正常生成DLL,但运行python main.py时,add_numbers函数可正常工作,调用hello函数却触发错误:“OSError: exception: access violation writing 0x0000000000002C44”。请问遗漏了什么?是否需要特殊处理stdio.h头文件?


相关代码

C代码(my_module.c)

// my_module.c

#include <stdio.h>

__declspec(dllexport)
void hello() {
    printf("Hello World!\n");
}

__declspec(dllexport)
int add_numbers(int a, int b) {
    return a + b;
}

// entry point
int main() {
    return 0;
}

构建脚本(build.py)

# build.py

from setuptools._distutils.ccompiler import new_compiler

compiler = new_compiler()
compiler.compile(["my_module.c"])
compiler.link_shared_lib(["my_module.obj"], "my_module")

主脚本(main.py)

# main.py

import ctypes

my_module = ctypes.CDLL("./my_module.dll")

my_module.add_numbers.argtypes = ctypes.c_int, ctypes.c_int
my_module.add_numbers.restype = ctypes.c_int

my_module.hello.argtypes = ()
my_module.hello.restype = None

result = my_module.add_numbers(3, 4)
print(type(result), result)

my_module.hello()

问题原因

核心问题是DLL未正确初始化C标准库的I/O环境:

  • add_numbers是纯数值计算函数,不依赖C标准库的任何初始化资源,因此可以正常运行。
  • printf属于C标准库函数,需要依赖stdio缓冲区、文件描述符等已初始化的资源才能工作。默认编译配置下,DLL没有触发C标准库的初始化流程,导致printf执行时访问未初始化的内存,触发访问违例。

解决方案

方法1:修改构建脚本,显式配置编译参数

修改build.py,添加编译参数指定使用动态C运行时库,并指定DLL入口点,确保标准库初始化:

# build.py
from setuptools._distutils.ccompiler import new_compiler

compiler = new_compiler()
# 使用动态链接的C运行时库,确保标准库资源能被正确初始化
compiler.compile(["my_module.c"], extra_preargs=["/MD"])
# 指定DLL入口点为DllMainCRTStartup,触发标准库初始化逻辑
compiler.link_shared_lib(
    ["my_module.obj"], 
    "my_module",
    extra_link_args=["/ENTRY:DllMainCRTStartup"]
)

方法2:在C代码中添加DllMain入口函数

在my_module.c中添加DLL的入口函数DllMain,让系统加载DLL时自动完成C标准库的初始化:

// my_module.c
#include <stdio.h>
#include <windows.h>

// DLL入口函数,系统加载DLL时会调用此函数
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    switch(fdwReason) {
        case DLL_PROCESS_ATTACH:
            // 进程加载DLL时,C标准库会自动完成初始化
            break;
        case DLL_PROCESS_DETACH:
            // 进程卸载DLL时,自动清理标准库资源
            break;
    }
    return TRUE;
}

__declspec(dllexport)
void hello() {
    printf("Hello World!\n");
}

__declspec(dllexport)
int add_numbers(int a, int b) {
    return a + b;
}

int main() {
    return 0;
}

修改后原构建脚本可直接使用,或搭配方法1的/MD参数效果更稳定。


内容的提问来源于stack exchange,提问作者Carlos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:28:35