I am converting a file from python2 to python3 that calls a C function using the ctypes
module.
The below minimal example works in python2, however raises the following error in python3 (3.11)
OSError: exception: access violation writing 0x00000000000094E0
// mydll.c
#include <stdio.h>
void myfunc(char* c, int i, char* c2) {
printf("Hello World");
}
int main() {
return 0;
}
# foo.py
import ctypes
import sys
PY3 = sys.version_info.major == 3
if PY3:
clibrary = ctypes.WinDLL("mydll.dll", winmode=1)
else:
clibrary = ctypes.WinDLL("mydll.dll")
prototype = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p)
c1 = (ctypes.c_char * 512)()
i = ctypes.c_int(0)
c2 = (ctypes.c_char * (600 * 8))()
func = prototype(("myfunc", clibrary))
func(c1, i, c2)
I think this has something to do with unicode vs bytes representation of strings between python versions. From what I gather this looks like dereferencing a null pointer or something of that nature. I've tried using ctypes.create_string_buffer()
but encounter the same error.
I expect the same code to work in both python2 and python3. What is causing the python3 error?
CodePudding user response:
According to [Python.Docs]: ctypes - Loading shared libraries (emphasis is mine):
The winmode parameter is used on Windows to specify how the library is loaded (since mode is ignored). It takes any value that is valid for the Win32 API
LoadLibraryEx
flags parameter. When omitted, the default is to use the flags that result in the most secure DLL load to avoiding issues such as DLL hijacking. Passing the full path to the DLL is the safest way to ensure the correct library and dependencies are loaded.Then from [MS.Learn]: LoadLibraryExA function (libloaderapi.h) (DONT_RESOLVE_DLL_REFERENCES - 0x00000001) (emphasis still mine):
Note Do not use this value; it is provided only for backward compatibility. If you are planning to access only data or resources in the DLL, use LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE or LOAD_LIBRARY_AS_IMAGE_RESOURCE or both. Otherwise, load the library as a DLL or executable module using the LoadLibrary function.
So, get rid of winmode (or set it to a supported value), and you should be fine.
Your example (a bit tweaked):
dll00.c:
#include <stdio.h>
#if defined(_WIN32)
# define DLL00_EXPORT_API __declspec(dllexport)
#else
# define DLL00_EXPORT_API
#endif
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API void func(char *pc0, int i, char *pc1);
#if defined(__cplusplus)
}
#endif
void func(char *pc0, int i, char *pc1)
{
printf("Hello World\n");
}
code00.py:
#!/usr/bin/env python
import ctypes as cts
import sys
DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
def main(*argv):
dll = cts.WinDLL(DLL_NAME)
prototype = cts.WINFUNCTYPE(None, cts.c_char_p, cts.c_int, cts.c_char_p)
c0 = (cts.c_char * 512)()
i = 0
c1 = (cts.c_char * (600 * 8))()
func = prototype(("func", dll))
func(c0, i, c1)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
Output:
(py_pc064_03.11_test0) [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q074867788]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul [prompt]> dir /b code00.py 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 dll00.c dll00.dll dll00.exp dll00.lib dll00.obj [prompt]> [prompt]> "e:\Work\Dev\VEnvs\py_pc064_02.07.18_test0\Scripts\python.exe" ./code00.py Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)] 064bit on win32 Hello World Done. [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.11_test0\Scripts\python.exe" ./code00.py Python 3.11.0rc1 (main, Aug 8 2022, 11:30:54) [MSC v.1932 64 bit (AMD64)] 064bit on win32 Hello World Done.
Notes:
Although not the case here, check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common error encountered when working with CTypes
There are differences between the 2 Python versions regarding strings ([SO]: Passing utf-16 string to a Windows function (@CristiFati's answer)), but those don't impact the current scenario, as you're not using any