背景:我正在用Python语言编写代码,并try 使用Win32 IFileOperation来执行shell 文件操作.特别是,将文件或目录移动/复制到不存在的位置需要获取引用目标目录的IShellItem(或IShellItem2)项,但如果目标目录尚不存在,则SHCreateItemFromParsingName会失败.以下是来自这些相关问题的建议:

我已经处理了WIN32_FIND_DATAW struct 的创建,并通过pywin32的pythoncom.CreateBindCtx()创建了IBindCtx对象.我坚持的一步是用Python语言实现一个IFileSysBindData类.

以下是我目前掌握的情况:

class FileSysBindData:
    _public_methods_ = ['SetFindData', 'GetFindData']
    _reg_clsid_ = '{01E18D10-4D8B-11d2-855D-006008059367}'

    def SetFindData(self, win32_find_data):
        self.win32_find_data = win32_find_data
        return 0

    def GetFindData(self, p_win32_find_data):
        p_win32_find_data.contents = self.win32_find_data
        return 0

bind_data = FileSysBindData()
ibindctx = pythoncom.CreateBindCtx()
# TODO: set the bind data
ibindctx.RegisterObjectParam('File System Bind Data', bind_data)

最后一行给出:

ValueError: argument is not a COM object (got type=FileSysBindData)

因此,要让pywin32以一种与COM兼容的方式创建它,显然还有很多工作要做.

但我不知道如何使用pywin32(或其他解决方案)将其实际创建为COM对象,这样我就可以将其传递给IBindCtx实例.

总的来说,我对COM不是很熟悉,所以阅读pywin32中的文档对我帮助不大.我甚至不知道是应该将我的类注册为服务器,还是应该以某种方式将其用作客户端.

推荐答案

困难之处在于您必须使用两个Python包:

  • comtypes来声明和实现自定义COM对象
  • pywin32,因为它有很多已经准备好的互操作类型、接口、类等.

以下是代码,您将看到将comtype的定制COM对象传递给pywin32的诀窍:

import pythoncom
import ctypes
from comtypes.hresult import *
from comtypes import IUnknown, GUID, COMMETHOD, COMObject, HRESULT
from ctypes.wintypes import *
from ctypes import *
from win32com.shell import shell
from os import fspath

# ripped from
# <python install path>\Lib\site-packages\comtypes\test\test_win32com_interop.py
# We use the PyCom_PyObjectFromIUnknown function in pythoncomxxx.dll to
# convert a comtypes COM pointer into a pythoncom COM pointer.
# This is the C prototype; we must pass 'True' as third argument:
# PyObject *PyCom_PyObjectFromIUnknown(IUnknown *punk, REFIID riid, BOOL bAddRef)
_PyCom_PyObjectFromIUnknown = PyDLL(
    pythoncom.__file__).PyCom_PyObjectFromIUnknown
_PyCom_PyObjectFromIUnknown.restype = py_object
_PyCom_PyObjectFromIUnknown.argtypes = (POINTER(IUnknown), c_void_p, BOOL)


def comtypes2pywin(ptr, interface=None):
    """Convert a comtypes pointer 'ptr' into a pythoncom PyI<interface> object.
    'interface' specifies the interface we want; it must be a comtypes
    interface class.  The interface must be implemented by the object and
    the interface must be known to pythoncom.
    If 'interface' is not specified, comtypes.IUnknown is used.
    """
    if interface is None:
        interface = IUnknown
    return _PyCom_PyObjectFromIUnknown(ptr, byref(interface._iid_), True)


class IFileSystemBindData(IUnknown):
    """The IFileSystemBindData interface
     https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesystembinddata"""
    _iid_ = GUID('{01e18d10-4d8b-11d2-855d-006008059367}')
    _methods_ = [
        COMMETHOD([], HRESULT, 'SetFindData',
                  (['in'], POINTER(WIN32_FIND_DATAW), 'pfd')),
        COMMETHOD([], HRESULT, 'GetFindData',
                  (['out'], POINTER(WIN32_FIND_DATAW), 'pfd'))
    ]


class FileSystemBindData(COMObject):
    """Implements the IFileSystemBindData interface:
     https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesystembinddata"""
    _com_interfaces_ = [IFileSystemBindData]

    def IFileSystemBindData_SetFindData(self, this, pfd):
        self.pfd = pfd
        return S_OK

    def IFileSystemBindData_GetFindData(self, this, pfd):
        pfd = self.pfd
        return S_OK


find_data = WIN32_FIND_DATAW()  # from wintypes
bind_data = FileSystemBindData()

# to avoid this long thing, we could declare a shorter helper on
# FileSystemBindData
bind_data.IFileSystemBindData_SetFindData(bind_data, ctypes.byref(find_data))
ctx = pythoncom.CreateBindCtx()

# we need this conversion to avoid
# "ValueError: argument is not a COM object (got type=FileSystemBindData)"
# error from pythoncom
pydata = comtypes2pywin(bind_data)
ctx.RegisterObjectParam('File System Bind Data', pydata)

item = shell.SHCreateItemFromParsingName(
    fspath("z:\\blah\\blah"), ctx, shell.IID_IShellItem2)

SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000
print(item.GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING))  # prints Z:\blah\blah

Python-3.x相关问答推荐

如何在python中有效地使用多处理和pytube库来加快下载速度?

Paramiko SFTPClient get()和put()函数的通过/失败结果?

按一列分组,如果日期列相同,则在数字列中填写缺少的值

我不能使用拆分来分隔数据

DataFrame列中如何迭代重复值?

如何在python 3.10中将列表项(字符串类型)转换为模块函数

通过点和线计算CV2 Homography

根据另一列值对多个数据框列进行分组

RGB 图像中最主要的 colored颜色 - OpenCV / NumPy / Python

在 jupyter notebook 的单元格中使用 sudo

python 3集合中的Discard()和Remove()函数有什么区别

如何模拟 Django 模型对象(及其方法)?

Pandas 的 EMA 与股票的 EMA 不匹配?

如何在元素列表中找到最大的数字,可能是非唯一的?

为什么 2to3 将 mydict.keys() 更改为 list(mydict.keys())?

Python在OrderedDict中 Select 第i个元素

导入父目录进行简要测试

Django Rest 框架 ListField 和 DictField

在 Keras 中训练神经网络的零精度

在 Python 中生成马尔可夫转移矩阵