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()
1

There are 1 best solutions below

2
On BEST ANSWER

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)