是否可以(使用 C# 4.0 中的新动态关键字)使用接口(interface)(如 IShellItem 或其他 WinAPI 接口(interface)),而无需在 C# 源代码中定义它们?或者至少不定义接口(interface)成员?
我正在尝试这样的事情:
const string IShellItemGuid = "43826D1E-E718-42EE-BC55-A1E261C37BFE";
Guid guid = new Guid(IShellItemGuid);
dynamic nativeShellItem = null;
// Create and initializes a Shell item object (IShellItem) from a parsing name
SHCreateItemFromParsingName(@"M:\TEST.TXT", IntPtr.Zero, guid, out nativeShellItem);
if (nativeShellItem != null)
{
MessageBox.Show(nativeShellItem.GetDisplayName(0));
}
哪里
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
static extern void SHCreateItemFromParsingName(
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
[In] IntPtr pbc,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid iIdIShellItem,
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out dynamic iShellItem);
SHCreateItemFromParsingName 工作正常,如果文件存在,我会得到一个对象(如果文件不存在,我会得到一个正确的错误消息),但是,尝试调用nativeShellItem.GetDisplayName 给了我一个运行时异常:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled: 'System.__ComObject' does not contain a definition for 'GetDisplayName'
虽然IShellItem有一个名为GetDisplayName的方法,并且SHCreateItemFromParsingName返回的我的nativeShellItem动态对象应该实现这个界面。
最佳答案
是的,这不起作用,DLR 没有关于 COM 对象的可用类型信息。这是 shell 编程中一个臭名昭著的问题,它使用派生自 IUnknown 的接口(interface),而不是 IDispatch,因此不支持后期绑定(bind)。并且没有可用的类型库可以让 .NET 轻松生成接口(interface)类型的互操作库。
IShellItem在ShObjIdl.idl中声明,它由cpp_quote()填充。这会挫败任何使用 midl.exe 生成类型库的尝试,只能创建 .h 文件。顺便说一句,已经提供了 ShObjIdl.h。只有 C++ 编译器可以使用它。
在 C# 中重新声明接口(interface)在技术上可以通过 [ComImport]、[Guid] 和 [PreserveSig] 属性实现。您必须非常小心地这样做,幸运的是 IShellItem 直接从 IUnknown 派生,因此您可以避开多重继承。没有帮助的是接口(interface)方法使用不会自动编码(marshal)的 native C 类型。值得注意的是,GetDisplayName() 采用 LPWSTR,即指向 Unicode 字符串的原始指针。不是 BSTR(自动化兼容字符串类型)。这需要您处理 C# 代码中的不安全指针。最好的办法是声明它 IntPtr,使用 Marshal.AllocCoTaskMem() 分配一 block 内存,并在使用 Marshal.PtrToStringUni() 调用后自行编码(marshal)字符串。
哎呀,shell 编程就是一个完整的皮塔饼。谷歌一下这个,这样你就可以复制/粘贴一些已知有效的东西。如果结果为空,使用 C++/CLI 以便您可以使用 ShObjIdl.h 绝对是更好的方法。祝你好运。
关于C# 4.0 动态对象和 WinAPI 接口(interface),如 IShellItem(无需在 C# 源代码中定义它们),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4450121/