Листинг [Python 3.Docs]: ctypes - библиотека сторонних функций для Python.
Я должен сказать, что не смог воспроизвести поведение ( даже без исправления ошибок ниже), используя либо «обычный» Python, либо I Python. Возможно, в реализации dll_ctypes_test больше, чем кажется на первый взгляд.
Текущие проблемы:
- dll_ctypes_test ожидает c_out указатель , но вы передаете обычный c_out экземпляр . Вы должны использовать ctypes.byref (или ctypes.pointer ). Не знаю, почему CTypes не жалуется из-за этого
- C и Python определения структур не совпадают , Одним из примеров является arr , который содержит элемент массив in C и элемент массив указателей ( ctypes.POINTER ) в Python. Это Неопределенное поведение , значение 2 должно быть синхронизировано c
- Вы пометили функцию экспорта как __stdcall , но вы загружаете . DLL с CDLL . Вы должны использовать WinDLL . Но поскольку вы используете 64bit (в зависимости от ваших путей), это не имеет большого значения
Ниже приведен пример (модифицированная версия вашего кода ).
dll00.h :
#pragma once
#if defined(_WIN32)
# if defined DLL0_EXPORTS
# define DLL00_EXPORT_API __declspec(dllexport)
# else
# define DLL00_EXPORT_API __declspec(dllimport)
# endif
#else
# define DLL00_EXPORT_API
#endif
#define ELEMENT_COUNT 1000
#define ARRAY_COUNT 50
typedef struct {
double var0, var1, var2, var3, var4,
var5, var6, var7, var8, var9;
} Element;
typedef struct {
int size;
Element data[ELEMENT_COUNT];
} Array1D;
typedef struct {
int size;
Array1D data[ARRAY_COUNT];
} Array2D;
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API void __stdcall dll00Func00(double in, Array2D *pOut);
#if defined(__cplusplus)
}
#endif
dll00. c:
#define DLL0_EXPORTS
#include "dll0.h"
#include <stdio.h>
void dll00Func00(double in, Array2D *pOut) {
if (pOut == NULL) {
printf("From C - NULL array passed\n");
return;
};
Array2D arr2d;
printf("From C - Outer array size: %d\n", pOut->size);
}
code00.py :
#!/usr/bin/env python
import sys
import ctypes as ct
ELEMENT_COUNT = 1000
ARRAY_COUNT = 50
class Element(ct.Structure):
_fields_ = list(("var{0:d}".format(i), ct.c_double) for i in range(10))
class Array1D(ct.Structure):
_fields_ = [
("size", ct.c_int),
("data", Element * ELEMENT_COUNT),
]
class Array2D(ct.Structure):
_fields_ = [
("size", ct.c_int),
("data", Array1D * ARRAY_COUNT),
]
DLL0_NAME = "./dll00.dll"
def main(*argv):
dll0 = ct.WinDLL(DLL0_NAME)
dll00Func00 = dll0.dll00Func00
dll00Func00.argtypes = [ct.c_double, ct.POINTER(Array2D)]
#dll00Func00.argtypes = [ct.c_double, Array2D] # !!! Defining the 2nd argument whitout POINTER, triggers the error !!!
mat = Array2D()
mat.size = 7
print("Array sizeof: {0:d} (0x{1:08X})".format(ct.sizeof(mat), ct.sizeof(mat)))
dll00Func00(5, ct.byref(mat))
#dll00Func00(5, mat)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
Вывод :
e:\Work\Dev\StackOverflow\q060297181>sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.20
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
[prompt]> dir /b
code00.py
dll0.h
dll00.c
[prompt]> cl /nologo /MD /DDLL dll00.c /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
Creating library dll00.lib and object dll00.exp
[prompt]> dir /b
code00.py
dll0.h
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Array sizeof: 4000408 (0x003D0A98)
From C - Outer array size: 7
Done.
При вызове функции или метода (есть некоторые исключения, но они не имеют значения здесь), стек (специальная область памяти) используется для хранения. Сохраняемые общие данные:
- Аргументы (и возвращаемое значение), которые представляют данные, которыми обмениваются вызывающий и вызываемый
- Local переменные (в callee ), которые не являются ни stati c, ни явно размещенными в куче (через mallo c, new , .. .)
- Другие данные (невидимые для программиста, такие как Указатель инструкций , ...)
Как и ожидалось, стек ограничен, поэтому он может хранить максимальное количество данных. Когда объем данных, которые необходимо сохранить, превышает максимальный, происходит переполнение стека (наиболее распространенный сценарий возникновения - во время рекурсии, когда слишком много повторяющихся вызовов, которым необходимо хранить слишком много данных).
Максимальный размер стека определяется параметрами сборки каждого приложения, а значения по умолчанию варьируются в зависимости от компилятора, OS , et c. Начиная с [MS.Docs]: / STACK (распределение стека) ( выделение принадлежит мне):
Резервное значение указывает общее распределение стека в виртуальной среде. Память. Для компьютеров ARM, x86 и x64 размер стека по умолчанию составляет 1 МБ .
Та же информация находится в [MS.Docs]: / F (Установить Размер стека) .
Как видно, Array2D занимает почти 4 МиБ (поэтому он не подходит, если / при попытке храниться в стеке).
Как я указал в комментарии (от code00.py ), определение dll00Func00 2 nd argtype без ct.POINTER вызывает ошибку. Может быть, в коде, который вы фактически запускаете, есть такая опечатка?
В любом случае, некоторые общие рекомендации, чтобы избежать этой ошибки:
- Избегайте передачи (в качестве аргументов / возвращаемого типа) больших сумм данных по значению (последние 2 подпункта также применяются к локально определенным переменным):
- Использовать указатели (или ссылки в C ++ )
- Распределять их динамически (в куче) )
- Сделать их stati c (менее желательно, так как stati c сегмент данных также ограничен)
- Убедитесь, что рекурсия не go слишком глубокая (где применимо)
- Увеличение ("вручную") размера стека при создании приложения
В качестве дополнительного замечания я не думаю, что возможно увеличить (изменить) текущий размер стека процесса в C при Win ( Visual C). Однако это возможно в C#, а также в Nix ( setrlimit ).