开发者

C# 4.0 dynamic object and WinAPI interfaces like IShellItem (without defining them in the C# source)

开发者 https://www.devze.com 2023-01-30 12:24 出处:网络
Is it possible (with the new dynamic keyword in C# 4.0) to use interfaces (like IShellItem or other WinAPI interfaces) without defining them in my C# source code? Or at least not define the interface

Is it possible (with the new dynamic keyword in C# 4.0) to use interfaces (like IShellItem or other WinAPI interfaces) without defining them in my C# source code? Or at least not define the interface members?

I'm trying something like:

        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));
        }

where

    [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);

T开发者_如何转开发he SHCreateItemFromParsingName works fine and I'm getting an object if the file exists (and a correct error message if the file does not exist), however, trying to call nativeShellItem.GetDisplayName gives me a runtime exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled: 'System.__ComObject' does not contain a definition for 'GetDisplayName'

although IShellItem has a method called GetDisplayName, and my nativeShellItem dynamic object returned by SHCreateItemFromParsingName should implement this interface.


Yes, this cannot work, the DLR has no type info available about the COM object. This is a notorious problem in shell programming, it uses interfaces that are derived from IUnknown, not IDispatch so late binding isn't supported. And there is no type library available for them that lets .NET easily generate an interop library for the interface types.

IShellItem is declared in ShObjIdl.idl, it is filled with cpp_quote(). Which defeats any attempt to use midl.exe to generate a type library, only a .h file can be created. Already provided btw, ShObjIdl.h. Only a C++ compiler can use it.

Re-declaring the interface in C# is technically possible with the [ComImport], [Guid] and [PreserveSig] attributes. You have to do so very carefully, luckily IShellItem derives directly from IUnknown so you can dodge the multiple inheritance bullet. What doesn't help is that the interface method uses native C types that don't marshal automatically. Notably GetDisplayName() takes an LPWSTR, a raw pointer to a Unicode string. Not a BSTR, the automation compatible string type. That requires you to mess with unsafe pointers in your C# code. Best thing to do is to declare it IntPtr, allocate a chunk of memory with Marshal.AllocCoTaskMem() and marshal the string yourself after the call with Marshal.PtrToStringUni().

Yuck, shell programming is a complete pita. Google the heck out of this so you can copy/paste something that's known to work. If that comes up empty, using C++/CLI so you can use ShObjIdl.h is definitely a better way. Good luck with it.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号