Calling argc/argv function with ctypes - python

I am creating a wrapper to a code in c for Python. The c code basically runs in terminal and has the following main function prototype:
void main(int argc, char *argv[]){
f=fopen(argv[1],"r");
f2=fopen(argv[2],"r");
So basically arguments read are strings in terminal. I created following python ctype wrapper, but it appears I am using wrong type. I know the arguments passed from the terminal is read as characters but an equivalent python side wrapper is giving following error:
import ctypes
_test=ctypes.CDLL('test.so')
def ctypes_test(a,b):
_test.main(ctypes.c_char(a),ctypes.c_char(b))
ctypes_test("323","as21")
TypeError: one character string expected
I have tried adding one character, just to check if shared object gets executed, it does as print commands work but momentarily till the section of the code in shared object needs file name. I also tried
ctypes.c_char_p but get.
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
Updated as per the suggestion in the comments to the following:
def ctypes_test(a,b):
_test.main(ctypes.c_int(a),ctypes.c_char_p(b))
ctypes_test(2, "323 as21")
Yet getting the same error.

Using this test DLL for Windows:
#include <stdio.h>
__declspec(dllexport)
void main(int argc, char* argv[])
{
for(int i = 0; i < argc; ++i)
printf("%s\n", argv[i]);
}
This code will call it. argv is basically a char** in C, so the ctypes type is POINTER(c_char_p). You also have to pass bytes strings and it can't be a Python list. It has to be an array of ctypes pointers.
>>> from ctypes import *
>>> dll = CDLL('./test')
>>> dll.main.restype = None
>>> dll.main.argtypes = c_int, POINTER(c_char_p)
>>> args = (c_char_p * 3)(b'abc', b'def', b'ghi')
>>> dll.main(len(args), args)
abc
def
ghi

Related

Embed / Include Python.h into C++ [Full Guide] (Python 3.9) (Windows) (Qt 5.15) [duplicate]

This question already has answers here:
how can i include python.h in QMake
(1 answer)
Embedding python 3.4 into C++ Qt Application?
(4 answers)
Closed 2 years ago.
When I was trying to embed a Python script into my Qt C++ program, I run into multiple problems when trying to include Python.h.
The following features, I would like to provide:
Include python.h
Execute Python Strings
Execute Python Scripts
Execute Python Scripts with Arguments
It should also work when Python is not installed on the deployed machine
Therefore I searched around the Internet to try to find a solution. And found a lot of Questions and Blogs, but non have them covered all my Problems and it still took me multiple hours and a lot of frustration.
That's why I have to write down a StackOverflow entry with my full solution so it might help and might accelerate all your work :)
(This answer and all its code examples work also in a non-Qt environment. Only 2. and 4. are Qt specific)
Download and install Python https://www.python.org/downloads/release
Alter the .pro file of your project and add the following lines (edit for your correct python path):
INCLUDEPATH = "C:\Users\Public\AppData\Local\Programs\Python\Python39\include"
LIBS += -L"C:\Users\Public\AppData\Local\Programs\Python\Python39\libs" -l"python39"
Example main.cpp code:
#include <QCoreApplication>
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
/*!
* \brief runPy can execut a Python string
* \param string (Python code)
*/
static void runPy(const char* string){
Py_Initialize();
PyRun_SimpleString(string);
Py_Finalize();
}
/*!
* \brief runPyScript executs a Python script
* \param file (the path of the script)
*/
static void runPyScript(const char* file){
FILE* fp;
Py_Initialize();
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
runPy("from time import time,ctime\n"
"print('Today is', ctime(time()))\n");
//uncomment the following line to run a script
//runPyScript("test/decode.py");
return a.exec();
}
Whenever you #include <Python.h> use the following code instead. (The Slots from Python will otherwise conflict with the Qt Slots
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
After compiling, add the python3.dll, python39.dll, as well as the DLLs and Lib Python folders to your compilation folder. You can find them in the root directory of your Python installation. This will allow you to run the embedded c++ code even when python is not installed.
With these steps, I was able to get python running in Qt with the 64 bit MinGW and MSVC compiler. Only the MSVC in debug mode got still a problem.
FURTHER:
If you want to pass arguments to the python script, you need the following function (It can be easy copy-pasted into your code):
/*!
* \brief runPyScriptArgs executs a Python script and passes arguments
* \param file (the path of the script)
* \param argc amount of arguments
* \param argv array of arguments with size of argc
*/
static void runPyScriptArgs(const char* file, int argc, char *argv[]){
FILE* fp;
wchar_t** wargv = new wchar_t*[argc];
for(int i = 0; i < argc; i++)
{
wargv[i] = Py_DecodeLocale(argv[i], nullptr);
if(wargv[i] == nullptr)
{
return;
}
}
Py_SetProgramName(wargv[0]);
Py_Initialize();
PySys_SetArgv(argc, wargv);
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
for(int i = 0; i < argc; i++)
{
PyMem_RawFree(wargv[i]);
wargv[i] = nullptr;
}
delete[] wargv;
wargv = nullptr;
}
To use this function, call it like this (For example in your main):
int py_argc = 2;
char* py_argv[py_argc];
py_argv[0] = "Progamm";
py_argv[1] = "Hello";
runPyScriptArgs("test/test.py", py_argc, py_argv);
Together with the test.py script in the test folder:
import sys
if len(sys.argv) != 2:
sys.exit("Not enough args")
ca_one = str(sys.argv[0])
ca_two = str(sys.argv[1])
print ("My command line args are " + ca_one + " and " + ca_two)
you get the following output:
My command line args are Progamm and Hello

boost python - nullptr while extracting ndarray

I have a C++ code which execute python script with boost_python package. Everything is fine, as longa as I extract int, string, or other not-array variables from python. However I have to extract a numpy::ndarray and convert it to cpp vector. I tried as follow:
main.cpp
#include <iostream>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
using namespace boost::python;
int main()
double t_end=7
try
{
Py_Initialize();
object module = import("__main__");
object name_space = module.attr("__dict__");
exec_file("MyModule.py", name_space, name_space);
object MyFunc = name_space["MyFunc"];
object result = MyFunc(t_end);
auto result_array = extract<numpy::ndarray>(result);
const numpy::ndarray& ret = result_array();
int input_size = ret.shape(0);
double* input_ptr = reinterpret_cast<double*>(ret.get_data());
std::vector<double> v(input_size);
for (int i = 0; i < input_size; ++i)
v[i] = *(input_ptr + i);
}
catch (error_already_set)
{
PyErr_Print();
}
Py_Finalize();
And example py script:
MyModule.py
import numpy as np
def MyFunc(t_end):
result = np.array([2,3,1,t_end])
return results
However it ends with error:
read access violation BOOST_NUMPY_ARRAY_API was nullptr
I also was trying to declare numpy::ndarray directly like numpy::ndarray result_array = extract<numpy::ndarray>(result); But the error is exactly the same. I've checked if my ndarray is not empty by printing it directly from python, and it is not. At the python step all seems to be correct. So what is causing the violation and how to fix it?
That error occurs since you're using the numpy module without first initializing it.
Notice the beginning of the official tutorial:
Initialise the Python runtime, and the numpy module. Failure to call these results in segmentation errors:
namespace np = boost::python::numpy;
int main(int argc, char **argv)
{
Py_Initialize();
np::initialize();
Your code is lacking the call to np::initialize();.

python wrong subprocess.call return code

Look at this 2 simples programs in C:
#include <stdio.h>
int main(int argc, char *argv[]) {
return -1;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
return 1337;
}
Now look this very basic python script:
>>> import subprocess
>>> r=subprocess.call(['./a.out'])
I do not understand why but the python script r value contains:
255 for the first C program. It should be -1
57 for the second C program. It should be 1337
Have i something wrong ?
Thanks
Python has nothing to do with it. This is system dependent.
On Unix/Linux systems, the return code is stored on 1 byte, and is unsigned, so truncation occurs outside 0-255 range.
So -1 becomes 255, 1337 becomes 57 (can be checked by applying modulus 256).
note that in Windows, return codes can be higher than 255 (was able to pass 100000000, but still not negative)
The conclusion is: don't rely on return codes too much to pass information. Print something on the console instead.
Related: https://unix.stackexchange.com/questions/37915/why-do-i-get-error-255-when-returning-1

how to call a exe from python with integer input arguments and return the .exe output to python?

I checked already a lot of posts and the subprocess documentation but non of them provided a solution to my problem. At least, i can't find one.
Anyway, here is my problem description:
I would like to call a .exe from a .py file. The .exe needs a integer input argument and returns also an integer value, which i would like to use for further calculations in python.
In order to keep things simple, i would like to use a minimun working example of my "problem"-code (see below). If i run this code, then .exe crashes and i don't know why. Maybe i just missed something but i don't know what!? So here is what i did:
c++ code which i use to generate: MyExe.exe
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string>
int main(int argc, char* argv[])
{
int x = atoi(argv[1]);
return x;
}
My python code:
from subprocess import Popen, PIPE
path = 'Path to my MyExe.exe'
def callmyexe(value):
p = Popen([path], stdout=PIPE, stdin=PIPE)
p.stdin.write(bytes(value))
return p.stdout.read
a = callmyexe(5)
b = a + 1
print(b)
I use MSVC 2015 and Python 3.6.
You have to use cout for output:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string>
int main(int argc, char* argv[])
{
int x = atoi(argv[1]);
cout << x;
}
And command line parameters for the input:
from subprocess import check_output
path = 'Path to my MyExe.exe'
def callmyexe(value):
return int(check_output([path, str(value)]))
a = callmyexe(5)
b = a + 1
print(b)

KeyboardHookProc in DLL doesn't do anything when called from python

I've been trying to write a DLL in C.
Install hook sets up the KeyboardProc. Calling the InstallHook() and UninstallHook() functions from Python always returns a 0, which I guess is because my callback function KeyboardProc isn't working.
The following is my C code for the DLL:
#include "stdafx.h"
#include <windows.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "ourdll.h"
//#pragma comment(linker, "/SECTION:.SHARED,RWS")
//#pragma data_seg(".SHARED")
HHOOK hKeyboardHook = 0;
int keypresses = 0;
HMODULE hInstance = 0;
//#pragma data_seg()
BOOL WINAPI DllMain (HANDLE hModule, DWORD dwFunction, LPVOID lpNot)
{
hInstance = hModule; //Edit
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int hookCode, WPARAM vKeyCode, LPARAM flags)
{
if(hookCode < 0)
{
return CallNextHookEx(hKeyboardHook, hookCode, vKeyCode, flags);
}
keypresses++;;
return CallNextHookEx(hKeyboardHook, hookCode, vKeyCode, flags);
}
__declspec(dllexport) void InstallHook(void)
{
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, 0);
}
__declspec(dllexport) int UninstallHook(void)
{
UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
return keypresses;
}
The Python code to use this is as follows:
>>> from ctypes import *
>>> dll = CDLL('C:\...\OurDLL.dll')
>>> dll.InstallHook()
[Type something at this point]
>>> result = dll.UninstallHook()
>>> result
0
EDIT: I should probably mention that I've also tried out a LowLevelKeyboardHook. I understand that the LowLevel hook is global and will catch all keystrokes, but that just caused my dll.InstallHook() Python code to freeze for a second or two before returning zero.
I am no expert in C, so any help would be greatly appreciated. Thanks.
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, 0);
SetWindowsHookEx requires a hModule - save the hModule from DllMain and pass it here. (You can pass NULL only if the thread id is your own thread.)
One exception to this is for the _LL hook types; these don't need a hmodule param since these hook don't inject into the target process - that's why your code using KEYBOARD_LL is 'succeeding'.
As for why it might be blocking when you use KEYBOARD_LL - docs for LowLevelKeyboardHookProc mention that the thread that installs the hook (ie. calls SetWindowsHookEx) must have a message loop, which you might not have in your python code.
Debugging tips: it looks like SetWindowsHookEx should be returning NULL (with GetLastError() returning a suitable error code); while developing code, using some combination of assert/printf/OutputDebugString as appropriate to check these return values is a good way to ensure that your assumptions are correct and give you some clues as to where things are going wrong.
BTW, one other thing to watch for with KEYBOARD vs KEYBOARD_LL: the KEYBOARD hook gets loaded into the target process - but only if it's the same bitness - so a 32-bit hook only sees keys pressed by other 32-bit processes. OTOH, KEYBOARD_LL is called back in your own process, so you get to see all keys - and also don't need to deal with the shared segment (though as far as I know it's also less efficient as a KEYBOARD hook).

Categories