Skip to content Skip to sidebar Skip to footer

C Dll Crack When Called From Python

I've a DLL that embeds Python interpreter using the C/Python API. The DLL works fine if called one time, but if the DLL is called twice, the code cracks and my program catch memory

Solution 1:

Listing [Python 3.Docs]: ctypes - A foreign function library for Python.

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#if defined(__cplusplus)extern"C" {
#endif

DLL00_EXPORT_API intdll00Func00(double t, double delta, unsignedint size, constdouble *pIn, double *pOut);

#if defined(__cplusplus)
}
#endif

dll00.c:

#include<stdio.h>#include"Python.h"#define DLL0_EXPORTS#include"dll00.h"#define C_TAG "From C .dll"intdll00Func00(double t, double delta, unsignedint size, constdouble *pIn, double *pOut) {
    int res = 0;
    printf("%s: in function\n", C_TAG);
    constint isInit = Py_IsInitialized();
    // Modify array calling Python functionsif (!isInit) {
        printf("%s: initializing Python interpreter\n", C_TAG);
        Py_Initialize();
    }
    res = PyRun_SimpleString("print(\"From Python (within C .dll): test\")");
    for (unsignedint i = 0; i < size; i++) {
        pOut[i] = pIn[i] * t + delta;
    }
    if (!isInit) {
        printf("%s: uninitializing Python interpreter\n", C_TAG);
        Py_Finalize();
    }
    return0;
}

main00.c:

#include<stdio.h>#include"dll00.h"#define SIZE 4intmain() {
    int res = 0;
    double in[SIZE] = { 10.0, 11.0, 12.0, 13.0 };
    double out[SIZE] = { 0 };
    res = dll00Func00(2, 0.5, SIZE, in, out);
    printf("Output array:\n");
    for (unsignedint i = 0; i < SIZE; i++) {
        printf("%.03f ", out[i]);
    }
    printf("\n");
    return0;
}

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct


DLL_NAME = "./dll00.dll"


def main(*argv):
    DblPtr = ct.POINTER(ct.c_double)
    size = 5
    DblArr = ct.c_double * size

    dll00 = ct.PyDLL(DLL_NAME)
    dll00Func00 = dll00.dll00Func00
    dll00Func00.argtypes = (ct.c_double, ct.c_double, ct.c_uint, DblPtr, DblPtr)
    dll00Func00.restype = ct.c_int

    in_arr = DblArr(*range(size))
    out_arr = DblArr()
    print("Output array:")
    for i in range(size):
        print("{:.3f}".format(out_arr[i]), end=" ")
    print("\n")
    res = dll00Func00(2, 2.5, size, in_arr, out_arr)
    print("Output array:")
    for i in range(size):
        print("{:.3f}".format(out_arr[i]), end=" ")
    print()



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")), 64if sys.maxsize > 0x100000000else32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

Notes:

  • In Python, use ctypes.PyDLL as you're (indirectly) calling Python API functions
  • In the .dll, use [Python 3.Docs]: Initialization, Finalization, and Threads - intPy_IsInitialized()
    • As a side note, the if test is not needed in Py_Initialize's case as Py_Initialize simply returns if the interpreter is already initialized (so I left it there just for consistency), but it is needed for Py_Finalize as one wouldn't want to uninitialize the interpreter while still in Python. So Py_Initialize / Py_Finalize pair doesn't work on "reference count" (every Py_Initialize call requires an Py_Finalize one)
  • Calling Py_Initialize / Py_Finalize in the function, seems like an overkill (if the function is being called multiple times). I'd do 2 wrapper functions in the .dll and call:

    • one at the beginning
    • the other at the end

    of the (C) program

Output:

e:\Work\Dev\StackOverflow\q059937552>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.19
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

[prompt]> dir /b
code00.py
dll00.c
dll00.h
main00.c

[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\03.07.06\include" dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll /LIBPATH:"c:\Install\pc064\Python\Python\03.07.06\libs"
dll00.c
   Creating library dll00.lib and object dll00.exp

[prompt]> cl /nologo /MD /W0 main00.c  /link /NOLOGO /OUT:main00_064.exe dll00.lib
main00.c

[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.h
dll00.lib
dll00.obj
main00.c
main00.obj
main00_064.exe

[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

Output array:
0.000 0.000 0.000 0.000 0.000

From C .dll: in function
From Python (within C .dll): test
Output array:
2.500 4.500 6.500 8.500 10.500

Done.

[prompt]> set PATH=%PATH%;c:\Install\pc064\Python\Python\03.07.06

[prompt]> main00_064.exe
From C .dll: in function
From C .dll: initializing Python interpreter
From Python (within C .dll): test
From C .dll: uninitializing Python interpreter
Output array:
20.500 22.500 24.500 26.500

Post a Comment for "C Dll Crack When Called From Python"