从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
相关产品推荐
相关产品推荐

