microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Accessing property IShellFolderViewDual.Application causes InvalidOleVariantTypeException "Specified OLE variant is invalid"

jnm2 opened this issue · comments

#1050 broke this at the same time as fixing #862. It changed both interfaces from InterfaceIsIDispatch to InterfaceIsDual, but IShellFolderViewDual currently only works as InterfaceIsIDispatch.

Repro

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta" PrivateAssets="all" />
  </ItemGroup>

</Project>
using System;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.UI.Shell;
using IServiceProvider = Windows.Win32.System.Com.IServiceProvider;

var shellWindows = (IShellWindows)new ShellWindows();

var serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(
    PInvoke.CSIDL_DESKTOP,
    pvarLocRoot: null,
    ShellWindowTypeConstants.SWC_DESKTOP,
    phwnd: out _,
    ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH);

serviceProvider.QueryService(PInvoke.SID_STopLevelBrowser, typeof(IShellBrowser).GUID, out var shellBrowserAsObject);
var shellBrowser = (IShellBrowser)shellBrowserAsObject;

shellBrowser.QueryActiveShellView(out var shellView);

var iid_IDispatch = new Guid("00020400-0000-0000-C000-000000000046");
shellView.GetItemObject((uint)_SVGIO.SVGIO_BACKGROUND, iid_IDispatch, out var folderViewAsObject);
var folderView = (IShellFolderViewDual)folderViewAsObject;

_ = folderView.Application; // Throws InvalidOleVariantTypeException "Specified OLE variant is invalid"
ShellWindows
IShellWindows
CSIDL_DESKTOP
IServiceProvider
ShellWindowTypeConstants
ShellWindowFindWindowOptions
IShellBrowser
SID_STopLevelBrowser
_SVGIO
IShellFolderViewDual

Workaround

Declare the same interface but with InterfaceIsIDispatch:

[Guid("E7A1AF80-4D96-11CF-960C-0080C7F4EE85"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), ComImport]
interface IShellFolderViewDual
{
    /// <summary>Gets the application object.</summary>
    /// <returns>
    /// <para>Type: <b>HRESULT</b> If this method succeeds, it returns <b xmlns:loc="http://microsoft.com/wdcml/l10n">S_OK</b>. Otherwise, it returns an <b xmlns:loc="http://microsoft.com/wdcml/l10n">HRESULT</b> error code.</para>
    /// </returns>
    /// <remarks>
    /// <para><see href="https://docs.microsoft.com/windows/win32/api//shldisp/nf-shldisp-ishellfolderviewdual-get_application">Learn more about this API from docs.microsoft.com</see>.</para>
    /// </remarks>
    object Application { get; }

    // Other members omitted     
}

@reflectronic suggested something that worked, and opens up a new avenue to explore:

[Guid("E7A1AF80-4D96-11CF-960C-0080C7F4EE85"), InterfaceType(ComInterfaceType.InterfaceIsDual), ComImport]
interface IShellFolderViewDual
{
    /// <summary>Gets the application object.</summary>
    /// <returns>
    /// <para>Type: <b>HRESULT</b> If this method succeeds, it returns <b xmlns:loc="http://microsoft.com/wdcml/l10n">S_OK</b>. Otherwise, it returns an <b xmlns:loc="http://microsoft.com/wdcml/l10n">HRESULT</b> error code.</para>
    /// </returns>
    /// <remarks>
    /// <para><see href="https://docs.microsoft.com/windows/win32/api//shldisp/nf-shldisp-ishellfolderviewdual-get_application">Learn more about this API from docs.microsoft.com</see>.</para>
    /// </remarks>
    object Application
    {
        [return: MarshalAs(UnmanagedType.IUnknown)]
        get;
    }

    // Other members omitted     
}

I think we can tell that this MarshalAs is needed due to the documented signature of get_Application at https://learn.microsoft.com/en-us/windows/win32/api/shldisp/nf-shldisp-ishellfolderviewdual-get_application:

HRESULT get_Application(
  [out] IDispatch **ppid
);