使用ctypes调用EnumRunning方法时,ValueError异常“过程可能调用的参数不足缺少4个字节”

问题描述

在Python中,我尝试使用EnumRunning methodIRunningObjectTable interface模块调用GetRunningObjectTable function获得的comtypes的Windows API ctypes (我想避免使用其他非标准模块,例如pythoncom)。

我主要从页面moniker.pypersist.py以及C:\Program Files\Windows Kits\10\Include\10.0.17763.0\um中的objbase.h header file检索和修改代码

当我尝试调用EnumRunning方法时,会引发ValueError异常:

调用的程序可能没有足够的参数(缺少4个字节)

该异常的消息来自文件_ctypes.pyd,因为如果我在文件中对其进行了修改,它的确会更改,但是我无法读取其中包含的已编译Python代码来尝试理解为什么会引发该异常。

ValueError异常的文档是:

在操作或函数接收到类型正确但值不合适的参数时引发,并且这种情况没有通过诸如IndexError之类的更精确的异常来描述。

所以问题可能出在我从IRunningObjectTable函数获得的GetRunningObjectTable接口的值上。尽管该函数确实返回了HRESULT S_OK value,所以我不确定。

我已经搜索了异常消息,但是我得到的信息与调用约定有关,并且缺少一些经过检查的参数,看来并非如此。

如果我尝试使用另一个参数调用它,则会出现TypeError异常:

函数需要1个参数(给定2个参数)

只有一个参数,因此顺序正确,尝试使用cdlloledll而不是windll也不起作用。

这是代码

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',('atime',('grfMode',('grfLocksSupported',('clsid',CLSID),('grfStateBits',('reserved',DWORD)]

class BIND_OPTS(Structure):
  _fields_ = [
    ('cbStruct',('grfFlags',('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,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'),'Load',[POINTER(IStream)]),'Save',[POINTER(IStream),BOOL]),'GetSizeMax',[POINTER(ULARGE_INTEGER)])]

class IMoniker(IPersistStream):
  _iid_ = GUID('{0000000F-0000-0000-C000-000000000046}')

IEnumString._methods_ = IUnkNown._methods_ + [
  STDMETHOD(HRESULT,'Next',[ULONG,POINTER(LPOLESTR),'Skip',[ULONG]),'Reset'),'Clone',[POINTER(POINTER(IEnumString))])]

IStream._methods_ = IUnkNown._methods_ + [
  STDMETHOD(HRESULT,'Seek',[LARGE_INTEGER,POINTER(ULARGE_INTEGER)]),'SetSize',[ULARGE_INTEGER]),'copyTo',[ULARGE_INTEGER,'Commit',[DWORD]),'Revert'),'LockRegion',DWORD]),'UnlockRegion','Stat',[POINTER(STATSTG),[POINTER(POINTER(IStream))])]

IEnumMoniker._methods_ = IUnkNown._methods_ + [
  STDMETHOD(HRESULT,POINTER(POINTER(IMoniker)),[POINTER(POINTER(IEnumMoniker))])]

IBindCtx._methods_ = IUnkNown._methods_ + [
  STDMETHOD(HRESULT,'RegisterObjectBound',[POINTER(IUnkNown)]),'RevokeObjectBound','ReleaseBoundobjects'),'SetBindOptions',[POINTER(BIND_OPTS)]),'GetBindOptions','GetRunningObjectTable',[POINTER(POINTER(IRunningObjectTable))]),'RegisterObjectParam',[LPOLESTR,POINTER(IUnkNown)]),'GetobjectParam',POINTER(POINTER(IUnkNown))]),'EnumObjectParam',[POINTER(POINTER(IEnumString))]),'RevokeObjectParam',[LPOLESTR])]

IRunningObjectTable._methods_ = IUnkNown._methods_ + [
  STDMETHOD(HRESULT,'Register',[DWORD,POINTER(IUnkNown),POINTER(IMoniker),POINTER(DWORD)]),'Revoke','IsRunning',[POINTER(IMoniker)]),'Getobject',[POINTER(IMoniker),'NoteChangeTime',POINTER(FILETIME)]),'GetTimeOfLastChange','EnumRunning',[POINTER(POINTER(IEnumMoniker))])]

IMoniker._methods_ = IPersistStream._methods_ + [
  STDMETHOD(HRESULT,'BindToObject',[POINTER(IBindCtx),REFIID,POINTER(c_void_p)]),'BindToStorage','Reduce',POINTER(POINTER(IMoniker))]),'ComposeWith',BOOL,'Enum',[BOOL,POINTER(POINTER(IEnumMoniker))]),'IsEqual','Hash',[POINTER(DWORD)]),POINTER(IMoniker)]),'Inverse',[POINTER(POINTER(IMoniker))]),'CommonPrefixWith','RelativePathTo','GetdisplayName',POINTER(LPOLESTR)]),'ParsedisplayName',POINTER(ULONG),'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()

解决方法

我找到了NV Access的NVDA(非可视桌面访问)GitHub存储库,其中文件objidl.py包含了其实现,并且在对我的操作进行了一点点修改之后,我发现问题在于我正在添加每个接口方法的IUnknown._methods_

因此,无需声明这样的方法:

IRunningObjectTable._methods_ = IUnknown._methods_ + [
  STDMETHOD(HRESULT,'Register',[DWORD,POINTER(IUnknown),POINTER(IMoniker),POINTER(DWORD)]),STDMETHOD(HRESULT,'Revoke',[DWORD]),'IsRunning',[POINTER(IMoniker)]),'GetObject',[POINTER(IMoniker),POINTER(POINTER(IUnknown))]),'NoteChangeTime',POINTER(FILETIME)]),'GetTimeOfLastChange','EnumRunning',[POINTER(POINTER(IEnumMoniker))])]

我应该这样声明他们:

IRunningObjectTable._methods_ = [
  STDMETHOD(HRESULT,[POINTER(POINTER(IEnumMoniker))])]

(我编辑了答案,因为起初我以为解决问题的方法是使用COMMETHOD函数而不是STDMETHOD函数,但现在发现情况并非如此)