Search code examples
pythonwinapictypescomtypes

ValueError exception "Procedure probably called with not enough arguments (4 bytes missing)" when calling the EnumRunning method using ctypes


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

Solution

  • 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)