ctypes python3.11 access violation reading 0x00000206196E7050, 0x00000206194E2168, 0x000002067FF23088

столкнулся с проблемой, что моя дллка(собранная release x64) не хочет работать с моим кодом на питоне. Вроде бы все правильно объявил, но в определенном моменте программа падает.

Падает и ноль ошибок "access violation reading 0x..."

либо

Прога падает с 3+ ошибками "access violation reading 0x...".

либо

Иногда не падает и завершает работу до конца, но с ошибками "access violation reading 0x..." и неправильно выполнив поставленную задачу.

py

import ctypes

lib = ctypes.PyDLL(r"тут длл")

# Объявляем сигнатуру calc_weight_sim_c
lib.calc_weight_sim_c.argtypes = [
    ctypes.POINTER(ctypes.c_char_p),
    ctypes.c_int,
    ctypes.POINTER(ctypes.c_char_p),
    ctypes.c_int
]
lib.calc_weight_sim_c.restype = ctypes.c_double

def readFiles(filePath):
    sql_code = None
    for encoding in ['utf-8', 'cp1251', 'latin1']:
        try:
            with open(filePath, 'r', encoding=encoding) as sql_file:
                sql_code = sql_file.read()
                break
        except UnicodeDecodeError:
            continue

    if not sql_code:
        return []

    return [line for line in sql_code.split('\n')]

def calcWeightSim(block1, block2):
    block1_bytes = [s.encode('utf-8', errors='replace') for s in block1]
    block2_bytes = [s.encode('utf-8', errors='replace') for s in block2]

    # Создаём массив (c_char_p * N), сразу инициализируя его контентом
    arr1 = (ctypes.c_char_p * len(block1_bytes))(*block1_bytes)
    arr2 = (ctypes.c_char_p * len(block2_bytes))(*block2_bytes)

    try:
        score = lib.calc_weight_sim_c(arr1, len(block1), arr2, len(block2))
        return score * 100.0
    except Exception as e:
        print("Ошибка при вызове DLL:", e)
        return 0.0

def test_simple_calc():
    block1 = readFiles("E:\\codding\\py\\SSC\\sql\\test\\file1.txt")

    block2 = readFiles("E:\\codding\\py\\SSC\\sql\\test\\file2.txt")


    sim = calcWeightSim(block1, block2)
    print("TEST SIM =", sim)

if __name__ == '__main__':
    test_simple_calc()

cpp

#include <Eigen/Dense>
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>
#include <cmath>
#include <algorithm>
#include <iostream>
#include "Boshka.h"
#include <cstring>

// -----------------------------------------------------------------------------
// Вспомогательные функции
// -----------------------------------------------------------------------------

// Разбиваем строку на токены (слова), приводя её к нижнему регистру
static std::vector<std::string> tokenize(const std::string& text)
{
    std::vector<std::string> tokens;
    if (text.empty())
        return tokens;

    // Просто разбиваем по пробелам вручную (без tolower)
    std::stringstream ss(text);
    std::string token;
    while (ss >> token) {
        tokens.push_back(token);
    }
    return tokens;
}

// Косинусная близость двух векторов Eigen
static double cosine_similarity(const Eigen::VectorXd& v1, const Eigen::VectorXd& v2) {
    if (v1.norm() == 0 || v2.norm() == 0) {
        return 0.0;
    }
    if (v1.size() != v2.size()) {
        std::cerr << "[cosine_similarity] size mismatch: "
            << v1.size() << " vs " << v2.size() << "\n";
        return 0.0;
    }
    double dot = v1.dot(v2);
    double norm_prod = v1.norm() * v2.norm();
    if (norm_prod == 0.0) {
        std::cerr << "[cosine_similarity] norm_prod=0 => return 0\n";
        return 0.0;
    }
    return dot / norm_prod;
}

// Считаем TF-IDF для набора строк
static void computeTfIdf(
    const std::vector<std::string>& lines,
    std::unordered_map<std::string, size_t>& vocabulary,
    Eigen::MatrixXd& tfidfMatrix
) {
    if (lines.empty()) {
        std::cerr << "[computeTfIdf] #3: The line list is empty => TF-IDF matrix empty.\n";
        return;
    }
    // 1) Собираем словарь
    for (auto& line : lines) {
        auto tokens = tokenize(line);
        for (auto& token : tokens) {
            if (!vocabulary.count(token)) {
                vocabulary[token] = vocabulary.size();
            }
        }
    }
    const size_t vocabSize = vocabulary.size();
    const size_t nLines = lines.size();

    std::cerr << "[computeTfIdf] nLines=" << nLines
        << " vocabSize=" << vocabSize << "\n";

    if (vocabSize == 0) {
        std::cerr << "[computeTfIdf] #4: The vocabulary is empty.\n";
        return;
    }

    Eigen::MatrixXd tfMatrix = Eigen::MatrixXd::Zero(nLines, vocabSize);
    std::vector<size_t> docCountForTerm(vocabSize, 0);

    // 2) Подсчитываем TF
    for (size_t i = 0; i < nLines; i++) {
        auto tokens = tokenize(lines[i]);
        for (auto& token : tokens) {
            auto it = vocabulary.find(token);
            if (it != vocabulary.end()) {
                size_t termID = it->second;
                tfMatrix(i, termID) += 1.0;
            }
        }
    }

    // 3) Считаем, в скольких документах встречается каждый термин
    for (size_t termID = 0; termID < vocabSize; termID++) {
        for (size_t i = 0; i < nLines; i++) {
            if (tfMatrix(i, termID) > 0.0) {
                docCountForTerm[termID]++;
            }
        }
    }

    // 4) IDF
    Eigen::VectorXd idfVec(vocabSize);
    for (size_t termID = 0; termID < vocabSize; termID++) {
        double val = std::log((1.0 + double(nLines)) / (1.0 + double(docCountForTerm[termID]))) + 1.0;
        idfVec(termID) = val;
    }

    // 5) Формируем итоговую матрицу TF-IDF
    tfidfMatrix = Eigen::MatrixXd::Zero(nLines, vocabSize);
    for (size_t i = 0; i < nLines; i++) {
        for (size_t termID = 0; termID < vocabSize; termID++) {
            tfidfMatrix(i, termID) = tfMatrix(i, termID) * idfVec(termID);
        }
    }
    std::cerr << "[computeTfIdf] => built matrix "
        << nLines << " x " << vocabSize << "\n";
}

// Реализует "взвешенную косинусную схожесть"
static double calc_weight_sim_impl(
    const std::vector<std::string>& block1,
    const std::vector<std::string>& block2
) {
    if (block1.empty() && block2.empty()) {
        std::cerr << "[calc_weight_sim_impl] Both blocks empty => 0.\n";
        return 0.0;
    }
    if (block1.empty() || block2.empty()) {
        std::cerr << "[calc_weight_sim_impl] One block empty => 0.\n";
        return 0.0;
    }

    // Собираем все строки
    std::vector<std::string> allLines;
    allLines.reserve(block1.size() + block2.size());
    for (auto& s : block1) {
        allLines.push_back(s);
    }
    for (auto& s : block2) {
        allLines.push_back(s);
    }

    // TF-IDF
    std::unordered_map<std::string, size_t> vocabulary;
    Eigen::MatrixXd tfidfMatrix;
    computeTfIdf(allLines, vocabulary, tfidfMatrix);

    if (tfidfMatrix.size() == 0) {
        std::cerr << "[calc_weight_sim_impl] TF-IDF is empty => 0.\n";
        return 0.0;
    }

    // Считаем суммарную длину символов
    size_t lenBlock1 = 0;
    for (auto& s : block1) {
        lenBlock1 += s.size();
    }
    size_t lenBlock2 = 0;
    for (auto& s : block2) {
        lenBlock2 += s.size();
    }
    double denom = double(lenBlock1 + lenBlock2);
    if (denom == 0.0) {
        std::cerr << "[calc_weight_sim_impl] denom=0 => 0.\n";
        return 0.0;
    }

    // Вычисляем взвешенную косинусную похожесть
    double weightedSim = 0.0;
    size_t minSize = std::min(block1.size(), block2.size());
    for (size_t i = 0; i < minSize; i++) {
        double weight = double(block1[i].size() + block2[i].size()) / denom;
        size_t idx2 = block1.size() + i;
        Eigen::VectorXd vec1 = tfidfMatrix.row(i);
        Eigen::VectorXd vec2 = tfidfMatrix.row(idx2);
        double simRatio = cosine_similarity(vec1, vec2);

        // Отладочный вывод по желанию
        /*
        std::cerr << "[calc_weight_sim_impl] i=" << i
                  << " line1='" << block1[i] << "' (" << block1[i].size() << " chars)"
                  << " line2='" << block2[i] << "' (" << block2[i].size() << " chars)"
                  << " simRatio=" << simRatio
                  << " weight=" << weight << "\n";
        */

        weightedSim += simRatio * weight;
    }

    std::cerr << "[calc_weight_sim_impl] => weightedSim=" << weightedSim << "\n";
    return weightedSim;
}

// -----------------------------------------------------------------------------
// safeCopy: Дополняем отладочным выводом realLen
// -----------------------------------------------------------------------------
static std::string safeCopy(const char* p, size_t maxLen = 65536)
{
    if (!p) {
        std::cerr << "[safeCopy] p==nullptr => return empty.\n";
        return std::string();
    }
    size_t realLen = strnlen(p, maxLen);
    // Чтобы не засорять лог в случае очень больших строк, выведем только часть
    std::string preview(p, (realLen > 200 ? 200 : realLen)); // максимум 200 символов
    std::cerr << "[safeCopy] realLen=" << realLen
        << " preview='" << preview << "'\n";

    return std::string(p, realLen);
}

// -----------------------------------------------------------------------------
// Экспортируемая функция, точка входа для Python
// -----------------------------------------------------------------------------
extern "C" __declspec(dllexport)
double calc_weight_sim_c(
    const char** block1, int sizeB1,
    const char** block2, int sizeB2
)
{
    std::cerr << "\n[calc_weight_sim_c] => start. sizeB1=" << sizeB1
        << " sizeB2=" << sizeB2 << "\n";

    std::vector<std::string> vecB1;
    vecB1.reserve(sizeB1);

    for (int i = 0; i < sizeB1; i++) {
        if (!block1[i]) {
            std::cerr << "[calc_weight_sim_c] WARNING: block1[" << i
                << "] == nullptr\n";
            vecB1.emplace_back("");
        }
        else {
            // Для отладки: выведем адрес указателя
            std::cerr << "[calc_weight_sim_c] block1[" << i << "] ptr="
                << (const void*)block1[i] << "\n";
            // Копируем
            vecB1.emplace_back(safeCopy(block1[i], 1000000));
        }
    }

    std::vector<std::string> vecB2;
    vecB2.reserve(sizeB2);

    for (int i = 0; i < sizeB2; i++) {
        if (!block2[i]) {
            std::cerr << "[calc_weight_sim_c] WARNING: block2[" << i
                << "] == nullptr\n";
            vecB2.emplace_back("");
        }
        else {
            std::cerr << "[calc_weight_sim_c] block2[" << i << "] ptr="
                << (const void*)block2[i] << "\n";
            vecB2.emplace_back(safeCopy(block2[i], 1000000));
        }
    }

    if (vecB1.empty() && vecB2.empty()) {
        std::cerr << "[calc_weight_sim_c] #10 => both blocks empty =>0.\n";
        return 0.0;
    }

    double result = 0.0;
    try {
        result = calc_weight_sim_impl(vecB1, vecB2);
    }
    catch (std::exception& ex) {
        std::cerr << "[calc_weight_sim_c] EXCEPTION: "
            << ex.what() << "\n";
        return 0.0;
    }
    catch (...) {
        std::cerr << "[calc_weight_sim_c] UNKNOWN EXCEPTION.\n";
        return 0.0;
    }

    std::cerr << "[calc_weight_sim_c] => result=" << result << "\n";
    return result;
}

Boshka.h

#pragma once
#ifndef CALC_WEIGHT_SIM_H
#define CALC_WEIGHT_SIM_H

#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif

#ifdef __cplusplus

#endif

    extern "C" __declspec(dllexport) double calc_weight_sim_c(
        const char** block1, int sizeB1,
        const char** block2, int sizeB2
    );

#ifdef __cplusplus

#endif

#endif // CALC_WEIGHT_SIM_H

все собрано правильно. Кучу форумов облазил чтобы найти решение этой проблемы и ничего не помогает.


Ответы (1 шт):

Автор решения: Alex Titov

Невозможно проверить Ваш код, поскольку в нем что-то вызывается и, возможно, там и падает. Начинайте постепенно усложнять рабочий (у меня по крайней мере) код, примерно тот, который дает для питона GigaChat:

import ctypes

lib = ctypes.CDLL(r'C:\Users\Alex\PycharmProjects\SO_tests\pydll1.dll')
lib.calc_weight_sim_c.argtypes = (ctypes.POINTER(ctypes.c_char_p), ctypes.c_int)
lib.calc_weight_sim_c.restype = ctypes.c_double

py_strings = ["Hello ", "World! ", "На фига? "]

c_strings = (ctypes.c_char_p * len(py_strings))()
for i, string in enumerate(py_strings):
    c_strings[i] = string.encode('utf-8')

print(lib.calc_weight_sim_c(c_strings,3))

cpp:

#include <iostream>

extern "C" __declspec(dllexport) double calc_weight_sim_c(const char **pp, int n)
{
    for (int i = 0; i < n; i++, pp++)
        std::cout << *pp;
    return 3.1415;
}
→ Ссылка