Windows 公共项对话框:ctypes + COM 访问冲突 [英] Windows Common Item Dialog: ctypes + COM access violation

查看:20
本文介绍了Windows 公共项对话框:ctypes + COM 访问冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 ctypes 模块来调用 Windows 的 公共项目对话框 API.下面显示的代码大致基于 MSDN 文档.它唯一的依赖项是 comtypes.GUID 模块.

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)

一切正常,直到第一次调用 show(0):

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

 WindowsError: exception: access violation reading 0xXXXXXXXX

(输出可能会有所不同.)为了比较,我在 AutoHotkey_L 中执行了相同的步骤,它可以直接访问 COM.

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

生成的宏按预期弹出打开文件对话框.两种情况下的 vtable 指针偏移量相同,但只有 Python 版本会引发访问冲突.

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.

有人能解释一下吗?

[我很抱歉没有在适当的地方添加更多超链接,但作为新用户,我一次只能添加两个.]

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

背景:我正在组合一个轻量级模块,它提供了一个用于 Python 脚本的本机保存/打开文件对话框.到目前为止,我一直无法在纯 Python 中找到实现.现有的那些依赖于 UI 工具包,例如 Tkinter 或 wxPython.

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.

推荐答案

解决方案如下:

COM 方法采用一个附加参数:this"指针.当您从 C++ 调用该方法时,它是隐式的,在 C(和 ctypes)中,您必须自己提供它.

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.

换行

func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)

进入

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

还有这一行

show(0)

进入

show(ptr, 0)

并且您的代码将起作用.

and your code will work.

这篇关于Windows 公共项对话框:ctypes + COM 访问冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆