Skip to content

Commit

Permalink
Add support for IFileOpenDialog (#5319)
Browse files Browse the repository at this point in the history
* Add support for IFileOpenDialog
CCW Wrapper in the PR was autogenerated using source generator
and tailored based on my previous experience.
As such I have strange local variables sometimes.
Also, I do not implement anything yet for following interfaces
- `Shell32.IFileDialogEvents` (CCW needed)
- `Shell32.IShellItem` (RCW from built-in COM)
- `Shell32.IShellItemArray` (RCW from built-in COM)
I plan to implement them when collect all feedback on quality of this PR so all improvements would be passed to next work.

What to do with code which maybe never called. How to detect that code?  or should I bother with that and provide implementations for all methods?
For example, I do not see errors in tests from `Shell32.IFileDialogEvents`, even if this interface used by `VistaDialogEvents` and passed to `IFileOpenDialog.Advise` method. So, removing code and look where it fails in tests would be not practical.

Would like to ask question about `IShellItem` and `IShellItemArray` relationships. I will look up on MSDN, but maybe collective conscionesness maybe help me better.
So, question: Does these completely unrelated interfaces, or there some `IShellItem` implementation which implement `IShellItemArray` as well. Answer affects how I implement RCW for these interfaces.

* Hello global usings!

* Add missing COM Wrapper instantiation

* Handle empty array marshalling
I implement almost same way as DllImportGenerator handle that
https://github.com/dotnet/runtime/blob/8d5f5267d33f7703c014cc89670f1f7aa47544f9/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ArrayMarshaller.cs#L91-L99

* Perform unwrapping of CCW to IntPtr inside ComWrapper
That, and introducing `SHCreateShellItemViaComWrappers` make Folder browser dialog works. `SHCreateShellItemViaComWrappers` is temporary means until I walk through all paths and replace usages of old function which rely on built-in COM one by one. That's should be easier for me tot test, and for you to review.

Co-authored-by: Igor Velikorossov <RussKie@users.noreply.github.com>
  • Loading branch information
kant2002 and RussKie committed Jan 26, 2022
1 parent c1baa83 commit fc4f862
Show file tree
Hide file tree
Showing 12 changed files with 679 additions and 40 deletions.
15 changes: 12 additions & 3 deletions src/System.Windows.Forms.Primitives/src/Interop/Interop.IID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ internal static class IID
// 618736E0-3C3D-11CF-810C-00AA00389B71
public static Guid IAccessible = new Guid(0x618736E0, 0x3C3D, 0x11CF, 0x81, 0x0C, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);

// D57C7288-D4AD-4768-BE02-9D969532D960
internal static Guid IFileOpenDialog { get; } = new(0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60);

// 973510DB-7D7F-452B-8975-74A85828D354
internal static Guid IFileDialogEvents { get; } = new(0x973510DB, 0x7D7F, 0x452B, 0x89, 0x75, 0x74, 0xA8, 0x58, 0x28, 0xD3, 0x54);

// 00000109-0000-0000-C000-000000000046
public static Guid IPersistStream = new Guid(0x00000109, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
public static Guid IPersistStream { get; } = new(0x00000109, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

// 7BF80980-BF32-101A-8BBB-00AA00300CAB
public static Guid IPicture = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0x00, 0xAA, 0x00, 0x30, 0x0C, 0xAB);
public static Guid IPicture { get; } = new(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0x00, 0xAA, 0x00, 0x30, 0x0C, 0xAB);

// 43826D1E-E718-42EE-BC55-A1E261C37BFE
internal static Guid IShellItem { get; } = new(0x43826D1E, 0xE718, 0x42EE, 0xBC, 0x55, 0xA1, 0xE2, 0x61, 0xC3, 0x7B, 0xFE);

// 0000000C-0000-0000-C000-000000000046
public static Guid IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
public static Guid IStream { get; } = new(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,13 @@ internal static partial class Ole32
CLSCTX dwClsContext,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppv);

[DllImport(Libraries.Ole32, ExactSpelling = true)]
public static extern HRESULT CoCreateInstance(
ref Guid rclsid,
IntPtr punkOuter,
CLSCTX dwClsContext,
ref Guid riid,
out IntPtr ppv);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ internal partial class Interop
{
internal static partial class Shell32
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
[StructLayout(LayoutKind.Sequential)]
public struct COMDLG_FILTERSPEC
{
public string? pszName;
public string? pszSpec;
public IntPtr pszName;
public IntPtr pszSpec;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ public interface IFileDialog
IShellItem psi);

void GetFolder(
out IShellItem ppsi);
out IShellItem? ppsi);

[PreserveSig]
HRESULT GetCurrentSelection(
out IShellItem ppsi);
out IShellItem? ppsi);

void SetFileName(
[MarshalAs(UnmanagedType.LPWStr)] string pszName);

void GetFileName(
[MarshalAs(UnmanagedType.LPWStr)] out string pszName);
[MarshalAs(UnmanagedType.LPWStr)] out string? pszName);

void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
Expand All @@ -73,7 +73,7 @@ public interface IFileDialog
[MarshalAs(UnmanagedType.LPWStr)] string pszLabel);

void GetResult(
out IShellItem ppsi);
out IShellItem? ppsi);

[PreserveSig]
HRESULT AddPlace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ public interface IFileOpenDialog : IFileDialog
IShellItem psi);

void GetFolder(
out IShellItem ppsi);
out IShellItem? ppsi);

[PreserveSig]
HRESULT GetCurrentSelection(
out IShellItem ppsi);
out IShellItem? ppsi);

void SetFileName(
[MarshalAs(UnmanagedType.LPWStr)] string pszName);

void GetFileName(
[MarshalAs(UnmanagedType.LPWStr)] out string pszName);
[MarshalAs(UnmanagedType.LPWStr)] out string? pszName);

void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
Expand All @@ -74,7 +74,7 @@ public interface IFileOpenDialog : IFileDialog
[MarshalAs(UnmanagedType.LPWStr)] string pszLabel);

void GetResult(
out IShellItem ppsi);
out IShellItem? ppsi);

[PreserveSig]
HRESULT AddPlace(
Expand All @@ -98,11 +98,11 @@ public interface IFileOpenDialog : IFileDialog
IntPtr pFilter);

void GetResults(
out IShellItemArray ppenum);
out IShellItemArray? ppenum);

[PreserveSig]
HRESULT GetSelectedItems(
out IShellItemArray ppsai);
out IShellItemArray? ppsai);
}
#pragma warning restore CS0108
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public interface IShellItem

[PreserveSig]
HRESULT GetParent(
out IShellItem ppsi);
out IShellItem? ppsi);

[PreserveSig]
HRESULT GetDisplayName(
SIGDN sigdnName,
[MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
[MarshalAs(UnmanagedType.LPWStr)] out string? ppszName);

[PreserveSig]
HRESULT GetAttributes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ internal static partial class Shell32
[DllImport(Libraries.Shell32, ExactSpelling = true)]
public static extern HRESULT SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi);

[DllImport(Libraries.Shell32, EntryPoint = "SHCreateShellItem", ExactSpelling = true)]
public static extern HRESULT SHCreateShellItemViaComWrappers(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IntPtr ppsi);

public static IShellItem GetShellItemForPath(string path)
{
if (SHParseDisplayName(path, IntPtr.Zero, out IntPtr pidl, 0, out uint _).Succeeded())
Expand All @@ -24,5 +27,21 @@ public static IShellItem GetShellItemForPath(string path)

throw new FileNotFoundException();
}

public static IShellItem GetShellItemForPathViaComWrappers(string path)
{
if (SHParseDisplayName(path, IntPtr.Zero, out IntPtr pidl, 0, out uint _).Succeeded())
{
// No parent specified
if (SHCreateShellItemViaComWrappers(IntPtr.Zero, IntPtr.Zero, pidl, out IntPtr ret).Succeeded())
{
var obj = WinFormsComWrappers.Instance
.GetOrCreateObjectForComInstance(ret, CreateObjectFlags.None);
return (IShellItem)obj;
}
}

throw new FileNotFoundException();
}
}
}

0 comments on commit fc4f862

Please sign in to comment.