You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从IShellFolder获取Printer Friendly Name遇类型转换异常求助

问题描述

尝试获取打印机友好名称(Printer Friendly Name),采用某Stack Overflow方案实现时,调用代码抛出异常:无法将System.__ComObject类型的对象转换为System.Windows.IDataObject类型,相关代码片段如下:

printersShellFolder.GetUIObjectOf(IntPtr.Zero, 1, rgelt, ref IID_IDataObject,
                                  0, out var ppv);
var objDataType = Marshal.GetTypedObjectForIUnknown(ppv, DataObjectType);
var dataType = (IDataObject)objDataType;
var printerFriendlyName = dataType.GetData("PrinterFriendlyName");
问题原因

你混淆了两个不同的IDataObject接口:

  • System.Windows.IDataObject是WPF框架封装的托管接口,仅适用于WPF内部数据交互场景。
  • Shell组件通过GetUIObjectOf返回的是原生COM标准的IDataObject接口,对应.NET中的System.Runtime.InteropServices.ComTypes.IDataObject类型,两者属于不同类型体系,直接强制转换必然失败。
修复方案

1. 改用原生COM的IDataObject接口

确保代码引用System.Runtime.InteropServices.ComTypes命名空间,调整类型转换逻辑:

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

// ... 其他原有代码 ...

// 调用GetUIObjectOf获取原生COM对象指针
printersShellFolder.GetUIObjectOf(IntPtr.Zero, 1, rgelt, ref IID_IDataObject, 0, out var ppv);

// 转换为原生COM的IDataObject接口
var dataObject = (IDataObject)Marshal.GetTypedObjectForIUnknown(ppv, typeof(IDataObject));

2. 正确读取PrinterFriendlyName数据

原生COM的IDataObject没有WPF简化的GetData方法,需通过FORMATETC和STGMEDIUM结构体读取数据:

// 注册PrinterFriendlyName对应的剪贴板格式
int printerFriendlyFormat = RegisterClipboardFormat("PrinterFriendlyName");

// 定义数据格式参数
FORMATETC fmt = new FORMATETC
{
    cfFormat = (short)printerFriendlyFormat,
    dwAspect = DVASPECT.DVASPECT_CONTENT,
    lindex = -1,
    ptd = IntPtr.Zero,
    tymed = TYMED.TYMED_HGLOBAL
};

STGMEDIUM stgMedium = new STGMEDIUM();
dataObject.GetData(ref fmt, out stgMedium);

// 从HGLOBAL中读取友好名称
try
{
    IntPtr dataPtr = Marshal.UnlockHGLOBAL(stgMedium.hGlobal);
    string friendlyName = Marshal.PtrToStringUni(dataPtr);
    // 使用获取到的friendlyName
}
finally
{
    // 释放资源
    Marshal.FreeHGLOBAL(stgMedium.hGlobal);
}

// 释放COM对象
Marshal.ReleaseComObject(dataObject);
Marshal.Release(ppv);

3. 补充必要的P/Invoke声明

添加RegisterClipboardFormat的平台调用声明:

[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int RegisterClipboardFormat(string lpszFormat);

内容的提问来源于stack exchange,提问作者ProgrammingParadise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:40:32