See the question and my original answer on StackOverflow

The difficulty is you have to use two python packages:

  • comtypes to declare and implement custom COM objects
  • pywin32 because it has lots of already baked interop types, interfaces, classes, etc.

Here is the code, and you'll see the trick to pass a comtypes's custom COM object to 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