C# 4.0 动态对象和 WinAPI 接口(interface),如 IShellItem(无需在 C# 源代码中定义它们)

标签 c# winapi interface dynamic interop

是否可以(使用 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/

相关文章:

c# - 是否实现两个接口(interface)?

c# - Reflection.Emit 实现接口(interface)的泛型方法约束

c# - T 应该支持什么类型,以便 List<T> 可以执行 OrderBy

c# - 在调用 ToUpper() 等之后,旧字符串对象是否会被垃圾回收?

c# - String.Split 效率问题

c# - MVC 常规和属性路由不能一起工作

c++ - 捕获在 Windows 上显示动画系统光标的哪一步

c - 在恢复线程时将参数传递给线程。 (WinAPI)

c++ - WinApi 线程(指针错误)

c# - 在接口(interface)中使用泛型方法根据文件输入返回变量类型列表