Windows Common Item Dialog: ctypes + COM access violation

前提是你 提交于 2020-06-24 12:12:55

问题


I am trying to use the ctypes module to make calls to Windows' Common Item Dialog API. The code shown below is roughly based on the steps outlined in the MSDN documentation. Its only dependency is the comtypes.GUID module.

import ctypes
from ctypes import byref, POINTER, c_int, c_long
from ctypes.wintypes import HWND, HRESULT
from comtypes import GUID

CLSID_FileOpenDialog = '{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}'
IID_IFileDialog = '{42F85136-DB7E-439C-85F1-E4075D135FC8}'
#IID_IFileOpenDialog = '{D57C7288-D4AD-4768-BE02-9D969532D960}'
CLSCTX_SERVER = 5
COINIT_APARTMENTTHREADED = 2
FOS_PICKFOLDERS = 32
FOS_FORCEFILESYSTEM = 64

ole32 = ctypes.windll.ole32
CoCreateInstance = ole32.CoCreateInstance
CoInitializeEx = ole32.CoInitializeEx

CoInitializeEx(None, COINIT_APARTMENTTHREADED)

ptr = c_int()
error = CoCreateInstance(
    byref(GUID(CLSID_FileOpenDialog)), None, CLSCTX_SERVER,
    byref(GUID(IID_IFileDialog)), byref(ptr))
assert error == 0

ptr = ptr.value
c_long_p = ctypes.POINTER(ctypes.c_int)
print('Pointer to COM object: %s' % ptr)
vtable = ctypes.cast(ptr, c_long_p).contents.value
print('Pointer to vtable: %s' % vtable)

func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)

# Calculating function pointer offset: 3rd entry in vtable; 32-bit => 4 bytes
show_p = ctypes.cast(vtable + 3*4, c_long_p).contents.value
print('Pointer to show(): %s' % show_p)
show = func_proto(show_p)
show(0)

Everything works as intended until the first call to show(0):

 WindowsError: exception: access violation reading 0xXXXXXXXX

(Output may vary.) For comparison, I have carried out the same steps in AutoHotkey_L, which has direct access to COM.

CLSID := "{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}"
IID := "{42F85136-DB7E-439C-85F1-E4075D135FC8}"

ptr := ComObjCreate(CLSID, IID)
vtable := NumGet(ptr + 0, 0, "Ptr")
    show := NumGet(vtbl + 0, 3 * A_PtrSize, "Ptr")

MsgBox ptr: %ptr% vtable: %vtable% show: %A_PtrSize%

DllCall(show, "Ptr", ptr, "Ptr", 44)

The resulting macro pops up an Open File dialog, as expected. The vtable pointer offsets are the same in both cases, but only the Python version throws up an access violation.

Can anyone shed some light on this?

[I apologize for not adding more hyperlinks where appropriate, but as a new user I am limited to two at a time.]

Background: I am putting together a lightweight module which provides a native save/open file dialog for use in Python scripts. So far I have been unable to find an implementation in pure Python. Those that exist rely on UI toolkits such as Tkinter or wxPython.


回答1:


Here is the solution:

COM methods take an additional parameter: The 'this' pointer. It is implicit when you call the method from C++, in C (and in ctypes) you must supply it yourself.

Change the line

func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)

into

func_proto = ctypes.WINFUNCTYPE(HRESULT, c_long, HWND)

and this line

show(0)

into

show(ptr, 0)

and your code will work.



来源:https://stackoverflow.com/questions/12632519/windows-common-item-dialog-ctypes-com-access-violation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!