Error in using C SDK in python - python

I'm trying to use a SDK with python.
I have multiple dll files in the SPK.
My script:
import ctypes
import os
malib = ctypes.WinDLL(os.path.join('D:\Downloads\Aura API\sdk\AURA_SDK.dll'))
print(malib.GetClaymoreKeyboardLedCount(1)) #function in the dll
I get the error :
WindowsError: exception: access violation reading 0x00000005
I can use some of the functions normaly but for others I get this issue. Also there are different dll's in the SDK and I think the problem could come from the communication between these dll (I only open one of these dll in the script) also because function not working seem to be using the other dll or/and communication with the computer.
Thanks if you got advices

You haven't set the argtypes and restype for the functions you're calling.
This means that, instead of knowing what C types to convert your arguments to, ctypes has to guess based on the Python types you pass it. If it guesses wrong, you will either pass garbage, or, worse, corrupt the stack, leading to an access violation.
For example, imagine this C function:
void func(int64_t n, char *s);
If you do this:
lib = # however you load the library
lib.func(2, 'abc')
… then ctypes, it will convert that 2 to a 32-bit int, not a 64-bit one. If you're using a 32-bit Python and DLL, that means n will get the 2 and the pointer to 'abc' crammed into one meaningless number, and s will be an uninitialized pointer to some arbitrary location in memory that, if your lucky, won't be mapped to anything and will raise an access violation.
But if you first do this:
lib = # however you load the library
lib.func.argtypes = [ctypes.c_int64, ctypes.c_char_p]
lib.func.restype = None
lib.func(2, 'abc')
… then ctypes will convert the 2 to a 64-bit int, so n will get 2 and s will get 'abc' and everyone will be happy.

Related

Issues with communicating with USB device through Ctypes and precompiled binaries

I am having issues using ctypes to connect with a gaussmeter (F.W. Bell 5180). The manufacturer provides DLLs for the interface and a header file called FWB5180.h:
FWB5180.h:
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the USB5100_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// USB5100_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef USB5100_EXPORTS
#define USB5100_API __declspec(dllexport)
#else
#define USB5100_API __declspec(dllimport)
#endif
extern "C" USB5100_API unsigned int openUSB5100(void);
extern "C" USB5100_API void closeUSB5100(unsigned int fwb5000ID);
extern "C" USB5100_API int scpiCommand(unsigned int usbID, char* cmd, char* result, int len);
My ctypes Python code is the following, where all I try to do is initialize the device then close it:
import ctypes,time
# open DLL
path = "C:\\Users\\Roger\\fw_bell_magnetic_field_probe\\usb5100-x64\\x64-dist\\usb5100.dll"
fwbell = ctypes.WinDLL(path)
# define open and close functions with argument and return types
openUSB5100 = fwbell.openUSB5100
openUSB5100.argtypes = None
openUSB5100.restype = ctypes.c_int
closeUSB5100 = fwbell.closeUSB5100
closeUSB5100.argtypes = [ctypes.c_int]
closeUSB5100.restype = None
# open device
idn = openUSB5100()
print(idn, type(idn))
# close device
time.sleep(0.1)
closeUSB5100(idn)
Expected behavior: it says elsewhere in the documentation that idn is a four bit unsigned integer, so it should return a number like 10203045. So I would expect an idn like that, and no errors while closing the connection.
Actual behavior: In my code, openUSB5100 always returns 0, whether the device is plugged in or not. The print statement always outputs 0 <class 'int'>. Then, the closeUSB5100 function errors out with something like OSError: exception: access violation reading 0x0000000000000028. I have also tried using different types for idn like c_ulong, but that didn't seem to help.
I'm on Windows 10 using python 3.9. Both are 64-bit, as are the DLLs.
I will note that I can use the gaussmeter using their provided dummy programs so its not strictly a connectivity issue. Though, I think they are using a 32-bit application with 32 bit drivers because when I try to use the DLLs that they seem to use, I get the following error: OSError: [WinError 193] %1 is not a valid Win32 application. When I use the DLLs marked as 64 bit I don't get this error.
Any tips or things to try with my code? This is my first foray into ctypes, so I accept there is likely egregious errors.
EDIT:
Here is the exact error message that I am getting.
PS C:\Users\Roger\fw_bell_magnetic_field_probe\usb5100-x64\x64-dist> python .\fw_bell_py.py
Traceback (most recent call last):
File "C:\Users\Roger\fw_bell_magnetic_field_probe\usb5100-x64\x64-dist\fw_bell_py.py", line 30, in <module>
idn = openUSB5100()
OSError: exception: access violation reading 0x00000000B56B1D68
The last value, 0x ... , typically changes a little bit if I run the code more than once.
Additionally, I have discovered that apparently the gauss meter might be really slow at being ready to use after being detected by the PC. So if I wait a long time between plugging in the device (or monitoring with usb.core.list_devices until something shows up) and running this code, the code always errors out on the open command, it doesn't make it to the close command.
There were some issues with the code (see the discussion), but the larger problem is that there is some problem with the DLL files themselves.

Call a C# DLL function in Python 3.6

I have this part of a C# DLL from a larger project. It has no dependencies, runs in either .NET 5.0 or .NET 6.0 C# without anything else in it. It looks something like this:
namespace Some.Namespace {
public static class Rename{
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
However, no matter what way we attempt to add the reference to Python 3.6 (ArcGIS Pro Distribution) using Python.NET...
sys.path.append(os.path.join(os.getcwd(),"Project_Folder","bin"))
clr.AddReference("SomeDLL")
from Some.Namespace import Rename
It throws this error on the 3rd line.
Exception has occurred: ModuleNotFoundError No module named Some
We've tried just about every possible means to load the DLL at this point and none of them have worked (Yes I know that 2.5.x Python.NET doesn't support .NET 6.0 - we switched to 5.0 and it didn't work either) Exposing the function through Robert's DllExport using ctypes throws a CLR Exception that we can't debug because it's cross-environment whenever the function is run.
Here's that attempt.
//The DLL With Robert's DLLExport
namespace Some.Namespace {
public static class Rename{
//We've also tried CallingConvention.Cdecl
[DllExport("Rename", System.Runtime.InteropServices.CallingConvention.StdCall)]
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
#In Python
#We've tried WINDLL
dll_utils = CDLL(os.path.join(os.getcwd(),"project_folder","bin","SomeDLL.dll"))
#We've tried using CFUNCTTYPE here too
encrypt_proto = ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
encrypt_args = (1, "p1", 0),(1, "p2", 0),
#It fails here when we add the encrypt_args flags to it, saying they don't match up,
#but there's no way to figure out how many it wants, we've tried many variations of
#the above
encrypt_call = encrypt_proto(("Rename",dll_utils),encrypt_args)'
#So because of that on one attempt we removed these two lines
p2 = ctypes.c_char_p(text_to_obfuscate)
p1 = ctypes.c_char_p(text_to_obfuscate)
#And the second argument here
return_value = encrypt_call(p1,p2)
#And...
OSError: [WinError -532462766] Windows Error 0xe0434352
Attempting to write a second function in the DLL to convert the C# string to a byte array and back and using a ctypes pointer doesn't work - it throws the same error just posted when it's called. Someone suggested in another question here IronPython next - which we want to avoid, or to try to use comtypes which I've never used before. At one point we even tried to do a COM decoration and it claimed something I've seen on an answer here is obsolete. I've researched this for 2-3 days and haven't been able to solve this.
At this point I'm at a loss. Is there any effective, simple way, to get an external .NET C# function in a DLL to call a function and pass a string in Python without installing any major dependencies? Just out of the box? There's gotta be something simple I'm missing.

cppyy unable to open shared library

I am trying to load a .so in cppyy, but getting below error.
Is there any way to see what exact error is there, due to which Load() is failing
load_my_lib.py:57: in <module>
cppyy.load_library("mylib.so")
.venv/lib/python3.6/site-packages/cppyy/__init__.py:219: in load_library
sc = gSystem.Load(name)
E cppyy.gbl.std.exception: int CppyyLegacy::TSystem::Load(const char* module, const char* entry = "", CppyyLegacy::Bool_t system = kFALSE) =>
E exception: std::exception
It looks like the loading of the library results in a std::exception being thrown that has an empty result out of it's what().
Both the use of std::exception rather than one of its derived classes, as well as having no message returned from what() are a bit of an uncommon use, and I'm not aware of anything in the load library call itself that can cause that. Thus, my best guess would be that it gets thrown during the creation of a global or static variable that lives in mylib.so.
Do you know whether there are any static or global variables in that library? These may also live in a library that mylib.so is linked with, and which gets pulled in when loading.
Another way that sometimes gets a better diagnostic (although I doubt it in this case, as it doesn't handle C++ exceptions) is to load the library with ctypes instead:
import ctypes
d = ctypes.CDLL("mylib.so", ctypes.RTLD_GLOBAL)
(ctypes.CDLL does not have portable behavior, which is why load_library exists, but on most, not all, Linux distros it's a perfectly fine alternative.)

Interacting with AURA_SDK.dll through python using ctypes

I'm trying to control my ASUS ROG Flare keyboard LED colors using python.
I downloaded the Aura Software Developer Kit from the ASUS website.
link here: https://www.asus.com/campaign/aura/us/SDK.php
inside the kit there is a menu guide and a dll file called AURA_SDK.dll. The guide says that with the mentioned dll the keyboard can be controlled.
I'm using the ctypes python package and succeeded in loading the package, but when I'm calling the first function to obtain control on the keyboard the program fails because I don't fully understand the argument the function needs to run.
Documentation from the guide:
Code I am trying:
import ctypes
path_dll = 'AURA_SDK.dll'
dll = ctypes.cdll.LoadLibrary(path_dll)
res = dll.CreateClaymoreKeyboard() # fails here
Any ideas on how to create this argument?
Thanks in advance.
This should do it. A good habit to get into is always define .argtypes and .restype for the functions you call. This will make sure parameters are converted correctly between Python and C types, and provide better error checking to help catch doing something incorrectly.
There are also many pre-defined Windows types in wintypes so you don't have to guess what ctype-type to use for a parameter.
Also note that WINAPI is defined as __stdcall calling convention and should use WinDLL instead of CDLL for loading the DLL. On 64-bit systems there is no difference between standard C calling convention (__cdecl) and __stdcall, but it will matter if you are using 32-bit Python or desire portability to 32-bit Python.
import ctypes as ct
from ctypes import wintypes as w
dll = ct.WinDLL('./AURA_SDK') # Use WinDLL for WINAPI calls.
dll.CreateClaymoreKeyboard.argtypes = ct.POINTER(ct.c_void_p), # tuple of arguments
dll.CreateClaymoreKeyboard.restype = w.DWORD
handle = ct.c_void_p() # Make an instance to pass by reference and receive the handle.
res = dll.CreateClaymoreKeyboard(ct.byref(handle))
# res is non-zero on success

Strange altered behaviour when linking from .so file with ctypes in python

I am writing a program to handle data from a high speed camera for my Ph.D. project. This camera comes with a SDK in the form a .so file on Linux, for communicating with the camera and getting images out. As said it is a high speed camera delivering lots of data, (several GB a minute). To handle this amount of data the SDK has a very handy spool function that spools data directly to the hard drive via DMA, in the form of a FITS file, a raw binary format with a header that is used in astronomy.
This function works fine when I write a small C program, link the .so file in and call the spool function this way. But when I wrap the .so file with ctypes and call the functions from python, all the functions are working except the spool function. When I call the spool function it returns no errors, but the spooled data file are garbled up, the file has the right format but half of all the frames are 0's.
In my world it does not make sense that a function in a .so file should behave different depending on which program its called from, my own little C program or python which after all is only a bigger C program. Does any body have any clue as to what is different when calling the .so from different programs?
I would be very thankful for any suggestions
Even though the camera is commercial, some the driver is GPLed and available, though a bit complicated. (unfortunately not the spool function it seems) I have an object in python for Handel the camera.
The begining of the class reads:
class Andor:
def __init__(self,handle=100):
#cdll.LoadLibrary("/usr/local/lib/libandor.so")
self.dll = CDLL("/usr/local/lib/libandor.so")
error = self.dll.SetCurrentCamera(c_long(handle))
error = self.dll.Initialize("/usr/local/etc/andor/")
cw = c_int()
ch = c_int()
self.dll.GetDetector(byref(cw), byref(ch))
The relevant function reads:
def SetSpool(self, active, method, path, framebuffersize):
error = self.dll.SetSpool(active, method, c_char_p(path), framebuffersize)
self.verbose(ERROR_CODE[error], sys._getframe().f_code.co_name)
return ERROR_CODE[error]
And in the corresponding header it reads:
unsigned int SetSingleTrackHBin(int bin);
unsigned int SetSpool(int active, int method, char * path, int framebuffersize);
unsigned int SetStorageMode(at_32 mode);
unsigned int SetTemperature(int temperature);
The code to get the camera running would read something like:
cam = andor.Andor()
cam.SetReadMode(4)
cam.SetFrameTransferMode(1)
cam.SetShutter(0,1,0,0)
cam.SetSpool(1,5,'/tmp/test.fits',10);
cam.GetStatus()
if cam.status == 'DRV_IDLE':
acquireEvent.clear()
cam.SetAcquisitionMode(3)
cam.SetExposureTime(0.0)
cam.SetNumberKinetics(exposureNumber)
cam.StartAcquisition()
My guess is that it isn't the call to the spooling function itself, but a call series which results in corrupted values being fed to/from the library.
Are you on a 64-bit platform? Not specifying restype for anything which returns a 64-bit integer (long with gcc) or pointer will result in those values being silently truncated to 32 bits. Additionally, ctypes.c_voidp handling is a little surprising — restype values of ctypes.c_voidp aren't truncated, but are returned in the Python interpreter as type int, with predictably hilarious results if high pointers are fed back as parameters to other functions without a cast.
I haven't tested it, but both these conditions might also affect 32-bit platforms for values larger than sys.maxint.
The only way to be 100% certain you're passing and receiving the values you expect is to specify the argtypes and restype for all the functions you call. And that includes creating Structure classes and associated POINTERs for all structs those functions operate on, even opaque structs.

Categories