Domanda

I'm trying to use Python's C-Types to call GetModuleHandleA on the kernel32 library. I'd like to get a handle to the library so I can use it to call GetProcAddress for LoadLibraryA. Below is my code...

import sys
from ctypes

kernel32 = windll.kernel32
print("The kernel32 is %s" % kernel32)
#The kernel32 is <WinDLL 'kernel32', handle 765b0000 at 1c2a9f0>

h_kernel32 = kernel32.GetModuleHandleA("C:\\Windows\\System32\\kernel32.dll")
if h_kernel32 == False:
        error = GetLastError()
        print("ERROR: %d - %s" % (error, FormatError(error)))

I'm getting an error, "ERROR: 126 - The specified module could not be found". I've also tried "C:/Windows/System32/kernel32.dll" and just "kernel32". I'm using Python 3.2 and this is on a Windows 7 machine. I've verified that the dll is there and in the path that I have set in the code above. I've been doing some research and can't seem to find out what the issue is. Any help is greatly appreciated. Thanks!

È stato utile?

Soluzione

The handle is stored in kernel32._handle. Calling GetModuleHandle should return the same value, but make sure you set restype and argtypes for type safety [*]:

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

kernel32.GetModuleHandleW.restype = wintypes.HMODULE
kernel32.GetModuleHandleW.argtypes = [wintypes.LPCWSTR]

hMod = kernel32.GetModuleHandleW('kernel32.dll')

Notice the 'W' suffix instead of 'A'. Python 3 uses Unicode strings, for which ctypes creates a c_wchar_p (LPCWSTR). There's no reason to call the [A]NSI version since it's just a wrapper around the [W]ide string version. But if you must, then you need to use bytes:

kernel32.GetModuleHandleA.restype = wintypes.HMODULE
kernel32.GetModuleHandleA.argtypes = [wintypes.LPCSTR]

hMod = kernel32.GetModuleHandleA(b'kernel32.dll')

[*] I recommend using kernel32 = WinDLL('kernel32', use_last_error=True) instead of windll.kernel32. This avoids conflicts with other modules that use windll. It also enables protection for the thread's LastErrorValue. In this case, use ctypes.get_last_error() and ctypes.set_last_error(err) instead of directly calling WinAPI GetLastError and SetLastError.

Altri suggerimenti

As for how it's done internally, ctypes.windll.kernel32 is WinDLL("kernel32"). WinDLL inherits CDLL, whose __init__ opens a handle the the kernel32. The CDLL__getattr__ looks up the stringified attribute "GetModuleHandle" when you use it on kernel32 and creates a _FuncPtr for it, which inherits _CFuncPtr, which is a _ctypes.CFuncPtr.

A _ctypes.CFuncPtr type object (PyTypeObject) was created directly in C instead of indirectly through defining a python class (which the runtime would create a type object for). Remember that the runtime creates PyObjects for all objects (including functions) in python, and a PyTypeObject for all types. Because CFuncPtr is inherited, it locates the constructor in the CFuncPtr type object, which was set in C to PyCFuncPtr_new when the type object was initialised, which takes the args, in this case the kernel32 WinDLL object and "GetModuleHandle" and calls PyCFuncPtr_FromDll to get the address using GetProcAddress on the handle to the module set up in the CDLL __init__. What it looks like to me is that _dlopen in the CDLL __init__ creates a handle object for kernel32 from an internally created type object, which calls an internal C constructor to perform something like LoadLibrary, and through python code assigns the relevant PyObject to self._handle in the kernel32 WinDLL PyObject, and then PyCFuncPtr_FromDll uses the kernel32 WinDLL object passed to get the handle object from _handle and that object will contain the address from LoadLibrary, which it will call GetProcAddress, and eventually the CFuncPtr type object constructor returns the PyObject for the function pointer instance to be returned from CDLL __getattr__, and a call performed on it will cause python to call the address in this object, which will be GetProcAddress instead of an address of a function that interprets the bytecode at the bytecode address?. I can't be arsed to verify the minutia right now but to me, that's how I see the possibility of interfacing with the windows API from the Python runtime.

Next time the __getattr__ is not called because the attribute now exists.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top