In Python I'm trying to call the Windows API EnumRunning
method of the IRunningObjectTable
interface that I get from the GetRunningObjectTable
function using the comtypes
and ctypes
modules (I want to avoid using other non-standard modules such as pythoncom
).
I mainly retrieved and modified the code from the pages moniker.py
and persist.py
and the objbase.h
header file in C:\Program Files\Windows Kits\10\Include\10.0.17763.0\um
.
When I try to call the EnumRunning
method a ValueError
exception is raised:
Procedure probably called with not enough arguments (4 bytes missing)
The exception's message comes from the file _ctypes.pyd
since it does change if I modify it in the file but I can't read the compiled Python code contained in it to try to understand why it's being raised.
The documentation of the ValueError
exception is:
Raised when an operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.
So the issue might be the value of the IRunningObjectTable
interface that I get from the GetRunningObjectTable
function. Though the function does return the HRESULT
S_OK
value so I'm not sure.
I have searched for the message of the exception but the information I got was about the calling convention and that some argument was missing which I have checked and does not seem to be the case.
If I try calling it with another argument I get the TypeError
exception:
this function takes 1 argument (2 given)
There's only one argument so the order is correct and trying to use cdll
or oledll
instead of windll
didn't worked either.
Here's the code:
from comtypes import GUID, IUnknown, STDMETHOD
from ctypes import byref, c_void_p, HRESULT, POINTER, Structure, windll
from ctypes.wintypes import BOOL, DWORD, FILETIME, LARGE_INTEGER, LPCVOID, LPOLESTR, ULARGE_INTEGER, ULONG
CLSID = GUID
IID = GUID
REFIID = POINTER(IID)
class STATSTG(Structure):
_fields_ = [
('pwcsName', LPOLESTR),
('type', DWORD),
('cbSize', ULARGE_INTEGER),
('mtime', FILETIME),
('ctime', FILETIME),
('atime', FILETIME),
('grfMode', DWORD),
('grfLocksSupported', DWORD),
('clsid', CLSID),
('grfStateBits', DWORD),
('reserved', DWORD)]
class BIND_OPTS(Structure):
_fields_ = [
('cbStruct', DWORD),
('grfFlags', DWORD),
('grfMode', DWORD),
('dwTickCountDeadline', DWORD)]
class IEnumString(IUnknown):
_iid_ = GUID('{00000101-0000-0000-C000-000000000046}')
class ISequentialStream(IUnknown):
_iid_ = GUID('{0c733a30-2a1c-11ce-ade5-00aa0044773d}')
_methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Read', [c_void_p, ULONG, POINTER(ULONG)]),
STDMETHOD(HRESULT, 'Write', [LPCVOID, ULONG, POINTER(ULONG)])]
class IStream(ISequentialStream):
_iid_ = GUID('{0000000C-0000-0000-C000-000000000046}')
_methods_ = IUnknown._methods_
class IEnumMoniker(IUnknown):
_iid_ = GUID('{00000102-0000-0000-C000-000000000046}')
class IBindCtx(IUnknown):
_iid_ = GUID('{0000000E-0000-0000-C000-000000000046}')
class IRunningObjectTable(IUnknown):
_iid_ = GUID('{00000010-0000-0000-C000-000000000046}')
class IPersist(IUnknown):
_iid_ = GUID('{0000010C-0000-0000-C000-000000000046}')
_methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'GetClassID', [POINTER(GUID)])]
class IPersistStream(IPersist):
_iid_ = GUID('{00000109-0000-0000-C000-000000000046}')
_methods_ = IPersist._methods_ + [
STDMETHOD(HRESULT, 'IsDirty'),
STDMETHOD(HRESULT, 'Load', [POINTER(IStream)]),
STDMETHOD(HRESULT, 'Save', [POINTER(IStream), BOOL]),
STDMETHOD(HRESULT, 'GetSizeMax', [POINTER(ULARGE_INTEGER)])]
class IMoniker(IPersistStream):
_iid_ = GUID('{0000000F-0000-0000-C000-000000000046}')
IEnumString._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Next', [ULONG, POINTER(LPOLESTR), POINTER(ULONG)]),
STDMETHOD(HRESULT, 'Skip', [ULONG]),
STDMETHOD(HRESULT, 'Reset'),
STDMETHOD(HRESULT, 'Clone', [POINTER(POINTER(IEnumString))])]
IStream._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Seek', [LARGE_INTEGER, DWORD, POINTER(ULARGE_INTEGER)]),
STDMETHOD(HRESULT, 'SetSize', [ULARGE_INTEGER]),
STDMETHOD(HRESULT, 'CopyTo', [ULARGE_INTEGER, ULARGE_INTEGER, POINTER(ULARGE_INTEGER)]),
STDMETHOD(HRESULT, 'Commit', [DWORD]),
STDMETHOD(HRESULT, 'Revert'),
STDMETHOD(HRESULT, 'LockRegion', [ULARGE_INTEGER, ULARGE_INTEGER, DWORD]),
STDMETHOD(HRESULT, 'UnlockRegion', [ULARGE_INTEGER, ULARGE_INTEGER, DWORD]),
STDMETHOD(HRESULT, 'Stat', [POINTER(STATSTG), DWORD]),
STDMETHOD(HRESULT, 'Clone', [POINTER(POINTER(IStream))])]
IEnumMoniker._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Next', [ULONG, POINTER(POINTER(IMoniker)), POINTER(ULONG)]),
STDMETHOD(HRESULT, 'Skip', [ULONG]),
STDMETHOD(HRESULT, 'Reset'),
STDMETHOD(HRESULT, 'Clone', [POINTER(POINTER(IEnumMoniker))])]
IBindCtx._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'RegisterObjectBound', [POINTER(IUnknown)]),
STDMETHOD(HRESULT, 'RevokeObjectBound', [POINTER(IUnknown)]),
STDMETHOD(HRESULT, 'ReleaseBoundObjects'),
STDMETHOD(HRESULT, 'SetBindOptions', [POINTER(BIND_OPTS)]),
STDMETHOD(HRESULT, 'GetBindOptions', [POINTER(BIND_OPTS)]),
STDMETHOD(HRESULT, 'GetRunningObjectTable', [POINTER(POINTER(IRunningObjectTable))]),
STDMETHOD(HRESULT, 'RegisterObjectParam', [LPOLESTR, POINTER(IUnknown)]),
STDMETHOD(HRESULT, 'GetObjectParam', [LPOLESTR, POINTER(POINTER(IUnknown))]),
STDMETHOD(HRESULT, 'EnumObjectParam', [POINTER(POINTER(IEnumString))]),
STDMETHOD(HRESULT, 'RevokeObjectParam', [LPOLESTR])]
IRunningObjectTable._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Register', [DWORD, POINTER(IUnknown), POINTER(IMoniker), POINTER(DWORD)]),
STDMETHOD(HRESULT, 'Revoke', [DWORD]),
STDMETHOD(HRESULT, 'IsRunning', [POINTER(IMoniker)]),
STDMETHOD(HRESULT, 'GetObject', [POINTER(IMoniker), POINTER(POINTER(IUnknown))]),
STDMETHOD(HRESULT, 'NoteChangeTime', [DWORD, POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'GetTimeOfLastChange', [POINTER(IMoniker), POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'EnumRunning', [POINTER(POINTER(IEnumMoniker))])]
IMoniker._methods_ = IPersistStream._methods_ + [
STDMETHOD(HRESULT, 'BindToObject', [POINTER(IBindCtx), POINTER(IMoniker), REFIID, POINTER(c_void_p)]),
STDMETHOD(HRESULT, 'BindToStorage', [POINTER(IBindCtx), POINTER(IMoniker), REFIID, POINTER(c_void_p)]),
STDMETHOD(HRESULT, 'Reduce', [POINTER(IBindCtx), DWORD, POINTER(POINTER(IMoniker)), POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'ComposeWith', [POINTER(IMoniker), BOOL, POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'Enum', [BOOL, POINTER(POINTER(IEnumMoniker))]),
STDMETHOD(HRESULT, 'IsEqual', [POINTER(IMoniker)]),
STDMETHOD(HRESULT, 'Hash', [POINTER(DWORD)]),
STDMETHOD(HRESULT, 'IsRunning', [POINTER(IBindCtx), POINTER(IMoniker), POINTER(IMoniker)]),
STDMETHOD(HRESULT, 'GetTimeOfLastChange', [POINTER(IBindCtx), POINTER(IMoniker), POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'Inverse', [POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'CommonPrefixWith', [POINTER(IMoniker), POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'RelativePathTo', [POINTER(IMoniker), POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'GetDisplayName', [POINTER(IBindCtx), POINTER(IMoniker), POINTER(LPOLESTR)]),
STDMETHOD(HRESULT, 'ParseDisplayName', [POINTER(IBindCtx), POINTER(IMoniker), LPOLESTR, POINTER(ULONG), POINTER(POINTER(IMoniker))]),
STDMETHOD(HRESULT, 'IsSystemMoniker', [POINTER(DWORD)])]
S_OK = 0
RunningObjectTable = POINTER(IRunningObjectTable)()
GetRunningObjectTable = windll.ole32.GetRunningObjectTable
GetRunningObjectTable.argtypes = [DWORD, POINTER(POINTER(IRunningObjectTable))]
GetRunningObjectTable.restype = HRESULT
if GetRunningObjectTable(0, byref(RunningObjectTable)) != S_OK:
raise Exception('GetRunningObjectTable failed.')
EnumMoniker = POINTER(IEnumMoniker)()
try:
RunningObjectTable.EnumRunning(byref(EnumMoniker))
except ValueError as exception:
print(exception)
input()
I found NV Access's NVDA (NonVisual Desktop Access) GitHub repository where in it the file objidl.py
contained their implementation and after modifying mine bit by bit to how they do it I found that the issue is that I was adding the IUnknown._methods_
to each interface's methods.
So instead of declaring the methods like this:
IRunningObjectTable._methods_ = IUnknown._methods_ + [
STDMETHOD(HRESULT, 'Register', [DWORD, POINTER(IUnknown), POINTER(IMoniker), POINTER(DWORD)]),
STDMETHOD(HRESULT, 'Revoke', [DWORD]),
STDMETHOD(HRESULT, 'IsRunning', [POINTER(IMoniker)]),
STDMETHOD(HRESULT, 'GetObject', [POINTER(IMoniker), POINTER(POINTER(IUnknown))]),
STDMETHOD(HRESULT, 'NoteChangeTime', [DWORD, POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'GetTimeOfLastChange', [POINTER(IMoniker), POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'EnumRunning', [POINTER(POINTER(IEnumMoniker))])]
I should declare them like this:
IRunningObjectTable._methods_ = [
STDMETHOD(HRESULT, 'Register', [DWORD, POINTER(IUnknown), POINTER(IMoniker), POINTER(DWORD)]),
STDMETHOD(HRESULT, 'Revoke', [DWORD]),
STDMETHOD(HRESULT, 'IsRunning', [POINTER(IMoniker)]),
STDMETHOD(HRESULT, 'GetObject', [POINTER(IMoniker), POINTER(POINTER(IUnknown))]),
STDMETHOD(HRESULT, 'NoteChangeTime', [DWORD, POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'GetTimeOfLastChange', [POINTER(IMoniker), POINTER(FILETIME)]),
STDMETHOD(HRESULT, 'EnumRunning', [POINTER(POINTER(IEnumMoniker))])]
(I edited the answer because at first I thought that what had solved the issue was using the COMMETHOD
function instead of the STDMETHOD
function but now noticed that that wasn't the case after all)