问题描述
在Python中,我尝试使用EnumRunning
method和IRunningObjectTable
interface模块调用从GetRunningObjectTable
function获得的comtypes
的Windows API ctypes
(我想避免使用其他非标准模块,例如pythoncom
)。
我主要从页面moniker.py
和persist.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个参数)
只有一个参数,因此顺序正确,尝试使用cdll
或oledll
而不是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
函数,但现在发现情况并非如此)