本文是将pytorch模型(手部识别)生成动态链接,以便用C++语言调用

开发环境

电脑有NVIDIA GPU
Anaconda
pycharm专业版
python3.12
Visual Studio 2019

1. 环境配置

1.1 python环境搭建

pytorch环境搭建见博文安装Pytorch(GPU)
与此博文不同的是本文的PyCharm必须是专业版
原因如下(.pyx文件在用cython编译时需要):

*.pyx files are supported by PyCharm Professional

1.2 安装cython库

环境搭建好后,在新建的虚拟环境里安装cython库

pip install cython

或者

conda install cython

查看安装库

conda list

2. 方法一:通过VS编译得到动态链接库dll

此方法的局限性
1)此方法的动态库无法在没有安装python的机器上面运行
2)此方法相当于将原始python函数进行修改,再用cython编译成.c和.h文件,最后生成动态链接库dll,用于第三方调用。总体来说挺麻烦,不如用博文c++调用python里的方法,此方法是直接将原始python函数当作库进行调用,无需中间的编译过程。

使用python创建生成动态链接库dll

2.1 使用Cython编译

2.1.1 run.pyx

新建一个python脚本,add.py

def str_add(str1, str2):
  return int(str1) + int(str2)

为了使该函数能够被Cython编译,需要新建一个run.pyx脚本

cdef public int str_add(const char* str1,const char* str2):
  return int(str1) + int(str2)

2.1.2 run.h和run.c

cdefpublic等都是cython关键字,这些关键字可以帮助函数可以被外部调用。然后在PyCharm中使用下面的命令编译,生成run.h和run.c两个文件
激活虚拟环境

conda activate u-net

进入项目路径

cd D:\SMY

编译

cython run.pyx

在这里插入图片描述

2.2 VS编译得到动态链接库

2.2.1 动态库项目SMY_dll

1)新建VS空项目,本文项目名为SMY_dll
项目SMY_dll属性
配置类型改为动态库,点击应用

在这里插入图片描述

2)将刚刚的.c和.h文件复制到项目存放代码的文件夹并添加到项目中
在这里插入图片描述

3)在项目上右键添加现有项 ,然后选择编译后的.c和.h文件
或者直接用快捷键Shift+Alt+A
在这里插入图片描述
添加后如下
在这里插入图片描述

2.2.2 项目配置属性

将python的include路径放在包含目录里,python库文件放在库目录里,将python3.lib和python312.lib添加到依赖项里,详细步骤可参考c++调用python里的1.环境配置

包含目录

C:\Users\PC\anaconda3\envs\u-net\include

库目录

C:\Users\PC\anaconda3\envs\u-net\libs

在这里插入图片描述
依赖项

C:\Users\PC\anaconda3\envs\u-net\libs\python3.lib
C:\Users\PC\anaconda3\envs\u-net\libs\python312.lib

在这里插入图片描述

2.2.3 dllmain.cpp

新建dllmain.cpp脚本
在项目上右键添加新建项

#include <Python.h>
#include <Windows.h>
#include "run.h"

// 提前注册模块初始化函数
static struct _inittab run_inittab[] = {
    {"run", PyInit_run},
    {NULL, NULL}
};

extern "C"
{
    __declspec(dllexport) int __stdcall _str_add(const char* a, const char* b)
    {
        return str_add(a, b);
    }
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        // 在初始化Python之前注册模块
        PyImport_ExtendInittab(run_inittab);
        // 初始化Python解释器
        Py_Initialize();
        // 确保模块被正确导入
        PyImport_ImportModule("run");
        break;
    case DLL_PROCESS_DETACH:
        // 关闭Python解释器
        Py_Finalize();
        break;
    }
    return TRUE;
}

2.2.4 生成dll文件

1)编译的版本选择Release,架构选择x64
在这里插入图片描述
2)生成dll文件
在这里插入图片描述
编译后可以得到dll文件

C:\Users\PC\Desktop\2.0_SMY_dll\SMY_dll\x64\Release\SMY_dll.dll
在这里插入图片描述

想要知道动态链接dll是否导出成功,可以参考博文CFF Explorer.exe验证 DLL 导出的函数名进行验证

2.3 dll的动态调用

建立另一个项目对刚生成的dll进行测试,本文此处的项目名依旧为SMY_dll

注意:
1)属性配置,与2.2.2一致
2)版本和架构配置,与2.2.3一致
3)将SMY_dll.dll文件复制在可执行文件(项目名.exe)所在的目录(项目名\x64\Release)

添加脚本main.cpp


#include <Windows.h>
#include <iostream>

int main()
{
    // 定义函数指针类型
    typedef int(__stdcall *pAdd)(const char* a, const char* b);

    // 调用dll测试
    // 使用 LoadLibraryA 并传递一个普通的 const char*
    HINSTANCE hDLL = LoadLibraryA("SMY_dll.dll");


    std::cout << "hDLL: " << hDLL << std::endl;

    if (hDLL != NULL)
    {
        // 获取DLL中需要调用的函数的地址
        // GetProcAddress 的第二个参数也应该是 const char*
        pAdd pFun = (pAdd)GetProcAddress(hDLL, "_str_add");
        std::cout << "pFun: " << pFun << std::endl;

        const char* stra = "12";
        const char* strb = "22";

        if (pFun != NULL)
        {
            int i = pFun(stra, strb);
            std::cout << "i = " << i << std::endl;
        }
        else
        {
            // 如果函数没找到,打印错误信息
            DWORD dwError = GetLastError();
            std::cerr << "Failed to get function address. Error code: " << dwError << std::endl;
        }
        
        // 释放DLL
        FreeLibrary(hDLL);
    }
    else
    {
        // 如果 DLL 加载失败,打印错误信息
        DWORD dwError = GetLastError();
        std::cerr << "Failed to load DLL. Error code: " << dwError << std::endl;
    }

    system("pause");
    return 0;
}

运行后得到结果“i = 34”
在这里插入图片描述

3. 方法二:python编译得到动态链接库(.pyd)

将训练好的模型best_model.pth 和预测脚本predict.py 打包成动态链接库的详细步骤

此方法的特性
1)此方法的动态库无法在没有安装python的机器上面运行
2)此方法无需改变原始python函数。总体来说与博文c++调用python差不多,但是博文的方法不需要pycharm是专业版。

3.1 组织python代码

3.1.1 项目结构

D:\09_Pytorch-UNet-master\01_SMY_4
|-- best_model.pth
|-- unet\model.py # 包含 UNet 类的定义
|-- hand_segmentation.py # 核心推理逻辑 (新建或重构)
|-- wrapper.pyx # Cython 包装器 (新建)
|-- setup.py # 编译脚本 (新建)

3.1.2 hand_segmentation.py

将模型加载和推理逻辑封装起来,这样可以避免全局变量污染,也更清晰

import logging
from pathlib import Path

import cv2
import numpy as np
import torch
import torch.nn.functional as F
from PIL import Image



class HandSegmentationModel:
    """手部分割模型封装"""

    def __init__(self, model_path, device, scale_factor=0.5, threshold=0.5,
                 classes=1, bilinear=False, use_ghost=True, dropout_p=0.3):
        self.device = device
        self.scale_factor = scale_factor
        self.threshold = threshold
        self.classes = classes

        from unet.model import UNet  # 确保该包在 PYTHONPATH 下可 import

        self.model = UNet(
            n_channels=3,
            n_classes=classes,
            bilinear=bilinear,
            use_ghost=use_ghost,
            dropout_p=dropout_p
        )

        self.model = self.model.to(memory_format=torch.channels_last)
        self.model.to(device=self.device)

        state_dict = torch.load(model_path, map_location=device)
        if isinstance(state_dict, dict) and 'model_state_dict' in state_dict:
            self.model.load_state_dict(state_dict['model_state_dict'])
            if 'best_dice' in state_dict:
                logging.info(f'best_dice={state_dict["best_dice"]:.4f}')
        else:
            if isinstance(state_dict, dict) and 'mask_values' in state_dict:
                del state_dict['mask_values']
            self.model.load_state_dict(state_dict)

        self.model.eval()
        logging.info(f"Model loaded: {model_path}")

    def predict(self, color_image_bgr: np.ndarray) -> np.ndarray:
        """输入 BGR uint8 [H,W,3],输出 mask uint8 [H,W] 0/255"""

        if color_image_bgr is None or color_image_bgr.ndim != 3 or color_image_bgr.shape[2] != 3:
            raise ValueError("Input must be BGR image with shape [H,W,3].")

        img_rgb = cv2.cvtColor(color_image_bgr, cv2.COLOR_BGR2RGB)
        img = img_rgb.transpose(2, 0, 1).astype(np.float32) / 255.0  # [C,H,W]

        img_tensor = torch.from_numpy(img).unsqueeze(0).to(
            device=self.device, dtype=torch.float32, memory_format=torch.channels_last
        )

        with torch.no_grad():
            img_tensor = F.interpolate(
                img_tensor, scale_factor=self.scale_factor,
                mode='bilinear', align_corners=False
            )

            output = self.model(img_tensor)
            if isinstance(output, tuple):
                output = output[0]

            if self.model.n_classes > 1:
                probs = F.softmax(output, dim=1)
                mask = torch.argmax(probs, dim=1).to(torch.float32)  # [N,H,W]
            else:
                probs = torch.sigmoid(output)
                mask = (probs > self.threshold).to(torch.float32)  # [N,1,H,W]

            H, W = color_image_bgr.shape[:2]
            if mask.dim() == 3:
                mask = mask.unsqueeze(1)  # [N,1,H,W]

            mask = F.interpolate(mask, size=(H, W), mode='nearest')
            mask = mask.squeeze().to('cpu').numpy().astype(np.uint8) * 255

        return mask


_model = None

def get_model(model_path: str = "best_model.pth"):
    global _model
    if _model is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

        model_path = Path(model_path)
        if not model_path.is_absolute():
            model_path = Path(__file__).parent / model_path

        if not model_path.exists():
            raise FileNotFoundError(f"Model file not found: {model_path}")

        _model = HandSegmentationModel(model_path=str(model_path), device=device)
    return _model


if __name__ == '__main__':
    model_path = 'best_model.pth'
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = HandSegmentationModel(
        model_path=model_path,
        device=device,
        scale_factor=0.5,
        threshold=0.5
    )

    image_path_str = r"D:\09_Pytorch-UNet-master\01_SMY_4\predict\20250610_123809_581.jpg"
    color_image = cv2.imread(image_path_str)        # 默认就是 BGR, uint8, [H, W, 3] 格式,这正是 model.predict 方法所期望的输入格式
    if color_image is None:
        raise FileNotFoundError(f"无法读取图片,请检查路径是否正确:{image_path_str}")

    pred_mask = model.predict(color_image)


    output_dir = Path(r"D:\09_Pytorch-UNet-master\01_SMY_4\predict")
    image_path = Path(image_path_str)  # 将路径字符串转换为Path对象以便使用 .stem
    mask_save_path = output_dir / f'{image_path.stem}_mask.png'

    # 确保输出目录存在
    output_dir.mkdir(parents=True, exist_ok=True)

    # 直接保存 pred_mask,它已经是 uint8 类型
    Image.fromarray(pred_mask).save(mask_save_path)

    print(f"掩码已保存至: {mask_save_path}")




3.2 Cython编译得到动态库(.pyd)

3.2.1 wrapper.pyx

定义将从 DLL 中导出的 C 语言函数

# wrapper.pyx
# cython: language_level=3
# distutils: language = c++

import numpy as np          # Python 层
cimport numpy as cnp        # Cython 层(用于类型)

cnp.import_array()          # 初始化 NumPy C-API

from hand_segmentation import get_model

def segment_image(cnp.ndarray[cnp.uint8_t, ndim=3] input_bgr not None,
                  model_path="best_model.pth"):
    """
    input_bgr: uint8 HxWx3 (BGR, contiguous)
    return: uint8 HxW mask (0/255)
    """
    if input_bgr.shape[2] != 3:
        raise ValueError("input_bgr must be HxWx3")

    # 若你担心不是连续内存,可强制:
    if not input_bgr.flags["C_CONTIGUOUS"]:
        input_bgr = np.ascontiguousarray(input_bgr)

    model = get_model(model_path)
    mask = model.predict(input_bgr)
    return mask

3.2.2 setup.py

from setuptools import setup, Extension
from Cython.Build import cythonize
import numpy
import sys

# 确定平台,因为链接器参数是平台相关的
# 这里我们只处理 Windows (msvc)
if sys.platform == 'win32':
    link_args = ['/DEF:hand_segmentation_dll.def']
else:
    # สำหรับ Linux/macOS,可能需要 -Wl,--version-script=...
    link_args = []


extensions = [
    Extension(
        "hand_segmentation_dll",
        ["wrapper.pyx"],
        include_dirs=[numpy.get_include()],
        extra_link_args=link_args  # <--- 添加这一行
    )
]

setup(
    ext_modules=cythonize(
        extensions,
        compiler_directives={'language_level': "3"}
    ),
)

3.2.3 编译生成pyd

1)激活虚拟环境

conda activate u-net

2)先进入项目目录

cd D:\09_Pytorch-UNet-master\01_SMY_4

3)运行编译命令:

python setup.py build_ext --inplace

若是出错需要再次编译时,可以在编译前清理旧文件

python setup.py clean --all

在这里插入图片描述

4)编译完成,当前目录下可以看到** hand_segmentation_dll.cp312-win_amd64.pyd**
在这里插入图片描述

5)复制一份.pyd 文件,将其重命名为 hand_segmentation_dll.pyd

3.3 dll的动态调用

建立一个C++项目对刚生成的动态链接进行测试,本文此处的项目名依旧为SMY_dll

注意:
1)属性配置,与2.2.2一致
包含目录还需加上以下路径(C++代码需用到numpy)
C:\Users\PC\anaconda3\envs\u-net\Lib\site-packages\numpy_core\include

2)版本和架构配置,与2.2.3一致
3)将hand_segmentation_dll.pyd文件复制在可执行文件(项目名.exe)所在的目录(项目名\x64\Release)
4)此项目脚本用到OpenCV 库,所以需要配置对应的属性,详细步骤可参考Windows10_visual studio 2019+opencv 4.5.2此处配置的OpenCV 库链接不能有 Debug 库(*.lib 文件名以 ‘d’ 结尾),Python 和它的生态系统全部都是在 Release 模式下编译的

3.3.1 项目结构

将python项目的相关文件复制在VS项目里,项目结构如下

C:\Users\PC\Desktop\SMY_dll
|-- main.cpp
|-- x64\Release\best_model.pth
|-- x64\Release\hand_segmentation_dll.pyd
|-- x64\Release\hand_segmentation.py
|-- x64\Release\python312.dll
|-- x64\Release\python3.dll
|-- x64\Release\unet\model.py

3.3.2 main.cpp

因为后续运行总是出现找不到python的标准库的提示,因此这里直接将环境路径写在代码里了

添加脚本main.cpp

#define PY_SSIZE_T_CLEAN
#include <Python.h>

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

#include <opencv2/opencv.hpp>

#ifdef _WIN32
#include <Windows.h>
#endif

#include <iostream>
#include <string>

// ===== 路径配置(按机器写死)=====
static const wchar_t* PY_HOME = L"C:\\Users\\PC\\anaconda3\\envs\\u-net";
static const wchar_t* PY_LIB = L"C:\\Users\\PC\\anaconda3\\envs\\u-net\\Lib";
static const wchar_t* PY_DLLS = L"C:\\Users\\PC\\anaconda3\\envs\\u-net\\DLLs";
static const wchar_t* PY_SITE = L"C:\\Users\\PC\\anaconda3\\envs\\u-net\\Lib\\site-packages";
static const wchar_t* APP_DIR = L"C:\\Users\\PC\\Desktop\\SMY_dll\\x64\\Release";
static const wchar_t* CONDA_BIN = L"C:\\Users\\PC\\anaconda3\\envs\\u-net\\Library\\bin";

static std::string narrow_from_wide_simple(const std::wstring& ws) {
    return std::string(ws.begin(), ws.end());
}

static void add_dll_dir_win(const wchar_t* dir) {
#ifdef _WIN32
    SetDllDirectoryW(dir);
#else
    (void)dir;
#endif
}

static void set_workdir_release() {
#ifdef _WIN32
    SetCurrentDirectoryW(APP_DIR);
#endif
}

static int init_python_embedded() {
    add_dll_dir_win(CONDA_BIN);
    set_workdir_release();

    PyStatus status;
    PyConfig config;
    PyConfig_InitPythonConfig(&config);

    config.use_environment = 0;
    config.site_import = 0;
    config.user_site_directory = 0;
    config.isolated = 1;

    status = PyConfig_SetString(&config, &config.home, PY_HOME);
    if (PyStatus_Exception(status)) { PyConfig_Clear(&config); return -1; }

    config.module_search_paths_set = 1;
    status = PyWideStringList_Append(&config.module_search_paths, PY_LIB);
    if (PyStatus_Exception(status)) { PyConfig_Clear(&config); return -2; }
    status = PyWideStringList_Append(&config.module_search_paths, PY_DLLS);
    if (PyStatus_Exception(status)) { PyConfig_Clear(&config); return -3; }
    status = PyWideStringList_Append(&config.module_search_paths, PY_SITE);
    if (PyStatus_Exception(status)) { PyConfig_Clear(&config); return -4; }
    status = PyWideStringList_Append(&config.module_search_paths, APP_DIR);
    if (PyStatus_Exception(status)) { PyConfig_Clear(&config); return -5; }

    status = Py_InitializeFromConfig(&config);
    PyConfig_Clear(&config);
    if (PyStatus_Exception(status)) {
        Py_ExitStatusException(status);
        return -6;
    }

    if (_import_array() < 0) {
        PyErr_Print();
        return -7;
    }
    return 0;
}

// capsule 析构:释放我们 new 出来的 cv::Mat*
static void capsule_cleanup(PyObject* capsule) {
    void* p = PyCapsule_GetPointer(capsule, "cv_mat_ptr");
    auto* m = reinterpret_cast<cv::Mat*>(p);
    delete m;
}

// cv::Mat(BGR, uint8, HxWx3, continuous) -> numpy ndarray (H,W,3) uint8
// numpy 持有 capsule,确保底层内存有效
static PyObject* mat_to_numpy_uint8_3c_owned(const cv::Mat& mat_bgr_u8) {
    if (mat_bgr_u8.empty()) return nullptr;
    if (mat_bgr_u8.type() != CV_8UC3) return nullptr;
    if (!mat_bgr_u8.isContinuous()) return nullptr;

    // 拷贝一个 header(共享同一块 data),并用 new 保存,交给 capsule 管
    auto* heapMat = new cv::Mat(mat_bgr_u8);

    npy_intp dims[3] = { heapMat->rows, heapMat->cols, 3 };
    npy_intp strides[3] = {
        (npy_intp)heapMat->step, // 每行字节数
        3,                       // 每列像素前进 3 字节
        1                        // 每通道前进 1 字节
    };

    PyArray_Descr* descr = PyArray_DescrFromType(NPY_UINT8);
    if (!descr) {
        delete heapMat;
        return nullptr;
    }

    PyObject* arr = PyArray_NewFromDescr(
        &PyArray_Type,
        descr,
        3,
        dims,
        strides,
        (void*)heapMat->data,
        0,
        nullptr
    );
    if (!arr) {
        delete heapMat;
        return nullptr;
    }

    PyObject* capsule = PyCapsule_New((void*)heapMat, "cv_mat_ptr", capsule_cleanup);
    if (!capsule) {
        Py_DECREF(arr);
        delete heapMat;
        return nullptr;
    }

    // 把 capsule 挂到 ndarray.base 上:ndarray 销毁时会触发 capsule_cleanup
    if (PyArray_SetBaseObject((PyArrayObject*)arr, capsule) < 0) {
        Py_DECREF(capsule);
        Py_DECREF(arr);
        return nullptr;
    }

    return arr; // new reference
}

// Python mask -> cv::Mat CV_8UC1(拷贝)
static bool py_mask_to_cv8uc1(PyObject* py_mask, cv::Mat& out_mask) {
    if (!py_mask) return false;

    PyObject* arr_obj = PyArray_FromAny(
        py_mask, nullptr,
        2, 2,
        NPY_ARRAY_CARRAY,
        nullptr
    );
    if (!arr_obj) { PyErr_Print(); return false; }

    PyArrayObject* arr = (PyArrayObject*)arr_obj;
    npy_intp h = PyArray_DIM(arr, 0);
    npy_intp w = PyArray_DIM(arr, 1);

    if (PyArray_TYPE(arr) != NPY_UINT8) {
        PyObject* u8 = PyArray_Cast(arr, NPY_UINT8);
        Py_DECREF(arr_obj);
        if (!u8) { PyErr_Print(); return false; }
        arr_obj = u8;
        arr = (PyArrayObject*)arr_obj;
        h = PyArray_DIM(arr, 0);
        w = PyArray_DIM(arr, 1);
    }

    unsigned char* data = (unsigned char*)PyArray_DATA(arr);

    out_mask = cv::Mat((int)h, (int)w, CV_8UC1);
    std::memcpy(out_mask.data, data, (size_t)h * (size_t)w);

    Py_DECREF(arr_obj);
    return true;
}

int wmain(int argc, wchar_t** argv) {
    std::wstring input_path =  L"D:\\09_Pytorch-UNet-master\\01_SMY_4\\predict\\20250610_123809_581.jpg";
    std::wstring output_path =  L"C:\\Users\\PC\\Desktop\\SMY_dll\\output\\mask.png";
    std::wstring model_path = L"best_model.pth";

    if (argc >= 2) input_path = argv[1];
    if (argc >= 3) output_path = argv[2];
    if (argc >= 4) model_path = argv[3];

    int rc = init_python_embedded();
    if (rc != 0) {
        std::cerr << "init_python_embedded failed, rc=" << rc << "\n";
        return 1;
    }

    cv::Mat img = cv::imread(narrow_from_wide_simple(input_path), cv::IMREAD_COLOR);
    if (img.empty()) {
        std::cerr << "Failed to read image: " << narrow_from_wide_simple(input_path) << "\n";
        Py_Finalize();
        return 2;
    }
    if (!img.isContinuous()) img = img.clone(); // 保证连续

    PyObject* mod = PyImport_ImportModule("hand_segmentation_dll");
    if (!mod) {
        std::cerr << "Import hand_segmentation_dll failed\n";
        PyErr_Print();
        Py_Finalize();
        return 3;
    }

    PyObject* func = PyObject_GetAttrString(mod, "segment_image");
    if (!func || !PyCallable_Check(func)) {
        std::cerr << "segment_image not found/callable\n";
        PyErr_Print();
        Py_XDECREF(func);
        Py_DECREF(mod);
        Py_Finalize();
        return 4;
    }

    PyObject* np_img = mat_to_numpy_uint8_3c_owned(img);
    if (!np_img) {
        std::cerr << "Mat->numpy failed\n";
        PyErr_Print();
        Py_DECREF(func);
        Py_DECREF(mod);
        Py_Finalize();
        return 5;
    }

    PyObject* args = PyTuple_Pack(1, np_img);
    Py_DECREF(np_img);

    PyObject* kwargs = PyDict_New();
    PyObject* py_model_path = PyUnicode_FromWideChar(model_path.c_str(), (Py_ssize_t)model_path.size());
    PyDict_SetItemString(kwargs, "model_path", py_model_path);
    Py_DECREF(py_model_path);

    PyObject* py_mask = PyObject_Call(func, args, kwargs);
    Py_DECREF(kwargs);
    Py_DECREF(args);
    Py_DECREF(func);
    Py_DECREF(mod);

    if (!py_mask) {
        std::cerr << "segment_image call failed\n";
        PyErr_Print();
        Py_Finalize();
        return 6;
    }

    cv::Mat mask;
    bool ok = py_mask_to_cv8uc1(py_mask, mask);
    Py_DECREF(py_mask);

    if (!ok || mask.empty()) {
        std::cerr << "mask convert failed\n";
        Py_Finalize();
        return 7;
    }

    if (!cv::imwrite(narrow_from_wide_simple(output_path), mask)) {
        std::cerr << "Failed to write: " << narrow_from_wide_simple(output_path) << "\n";
        Py_Finalize();
        return 8;
    }

    std::cout << "OK. mask saved to: " << narrow_from_wide_simple(output_path) << "\n";
    Py_Finalize();
    return 0;
}


调用成功
在这里插入图片描述

常见错误

2.3 调用出错 Could not find platform independent 1ibraries

Could not find platform independent 1ibraries
Python path configuration: PYTHONHOME = (not set) PYTHONPATH = (not set)
Fatal Python error: init-fs_encoding: failed to get the Python codec of the filesystem encoding ModuleNotFoundError: No module named ‘encodings’
sys.prefix = ‘C:\Users\PC\Desktop\SMY_dl1’

C++ 程序成功加载了 SMY_dll.dll,然后 SMY_dll.dll 尝试启动 Python,但 Python 像一个“失忆”的人,不知道自己的核心文件(标准库)放在哪里,导致启动失败
在这里插入图片描述
解决方法:
依次执行以下步骤

1)将虚拟环境目录里的Lib 文件夹和DLLs 文件夹复制在可执行文件所在的目录

可执行文件(项目名.exe)所在的目录

C:\Users\PC\Desktop\SMY_dll\x64\Release

虚拟环境目录

C:\Users\PC\anaconda3\envs\u-net

2)确保以下文件也在.exe文件所在的目录

python3.dll
python312.dll
SMY_dll.dll
SMY_dll.exe

在这里插入图片描述

3.3 调用出错 0xC0000005

0x00007FF9594D855B (ucrtbased.dll)处(位于 SMY_dll.exe 中)引发的异常: 0xC0000005: 读取位置 0x0000000000920090 时发生访问冲突。

这个错误信息里的 ucrtbased.dll 是铁证。那个尾缀的 d 代表 “Debug”。这明确地告诉我们,尽管 Visual Studio 项目配置设置为了 “Release”,但由于某种原因,进程在运行时仍然加载了 Debug 版本的 C 运行时库
只要 ucrtbased.dll 被加载进来了,与 Python 环境的 Release 库(ucrtbase.dll,没有’d’)冲突就不可避免,崩溃是必然的

解决方法:
依次执行以下步骤
1)打开VS项目里的属性页面
2)检查运行时库设置
确保顶部的 配置 (Configuration) 设置为 活动(Release),平台 (Platform) 设置为 活动(x64)
导航到 C/C++ -> 代码生成 (Code Generation)
检查 运行时库 (Runtime Library) ,必须 是 多线程 DLL (/MD)
3)清理链接器输入
导航到 链接器 (Linker) -> 输入 (Input)
查看 附加依赖项 (Additional Dependencies)
删除所有以 d.lib 结尾的库
4)重新生成解决方案
右键项目,选择重新生成解决方案

参考文章

参考视频
Unity常用API讲解

参考博文
python打包dll:https://blog.csdn.net/2301_79605567/article/details/141613618
手把手教你将Python程序打包为DLL:https://blog.csdn.net/zmr1994/article/details/90703017
用cython将python程序打包成C++动态库(windows+Vistual Studio2017平台):https://blog.csdn.net/weixin_45805339/article/details/147312793

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐