Skip to content

Support Windows shell virtual files in drag and drop - #21907

Open
YoshihiroIto wants to merge 10 commits into
AvaloniaUI:mainfrom
YoshihiroIto:agent/windows-virtual-file-drop
Open

Support Windows shell virtual files in drag and drop#21907
YoshihiroIto wants to merge 10 commits into
AvaloniaUI:mainfrom
YoshihiroIto:agent/windows-virtual-file-drop

Conversation

@YoshihiroIto

@YoshihiroIto YoshihiroIto commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What does the pull request do?

Adds Windows shell virtual-file support to the OLE data-transfer backend. Applications can now receive files dragged from sources that do not expose a filesystem path yet, including entries dragged from ZIP folders in Windows Explorer.

Virtual entries are exposed through the existing DataFormat.File / IStorageFile API, so no new public API is required.

What is the current behavior?

OleDataObjectToDataTransferWrapper only recognizes physical files supplied through CF_HDROP. A Windows shell source that supplies FileGroupDescriptorW plus indexed FileContents therefore does not produce DataFormat.File items.

What is the updated/expected behavior with this PR?

  • FileGroupDescriptorW and FileContents are recognized as file data.
  • Each descriptor is exposed as an IStorageFile, including its name and optional size.
  • FileContents supports both IStream and HGLOBAL providers.
  • Streams can be consumed on a worker thread without violating COM apartment ownership.
  • IDataObjectAsyncCapability keeps the source operation alive after Drop returns and is completed when all returned storage items are disposed.

How was the solution implemented (if it's not obvious)?

The Win32 backend parses the shell FILEGROUPDESCRIPTORW payload and requests each indexed FileContents entry. COM IStream and IDataObjectAsyncCapability interfaces are explicitly marshaled between apartments. The unmarshaled stream is owned by a generated MicroCom proxy, retaining trimming and NativeAOT compatibility while also providing finalizer-backed COM lifetime management. IStream.Read preserves the raw HRESULT so the documented S_FALSE end-of-stream result is handled correctly, and Length/Seek/Position delegate to the underlying stream.

The implementation requests TYMED_ISTREAM first and retries with TYMED_HGLOBAL, because some valid IDataObject implementations provide only the latter.

The non-file transfer item now receives only its actual non-file formats, preventing a duplicate DataFormat.File item when virtual files are present. A failure to materialize one virtual file is logged as a warning and does not prevent the remaining files from being returned.

Validation performed:

  • Avalonia.Win32 Release builds for net8.0 and net10.0: succeeded.
  • Avalonia.IntegrationTests.Win32: 242 passed, including virtual-file contents/size, truncated descriptor input, and completing IDataObjectAsyncCapability from a worker thread.
  • win-x64 NativeAOT smoke publish with OleVirtualFileData rooted: succeeded, and the generated executable ran successfully. The root-all harness surfaces an existing Enum.GetValues(Type) IL3050 warning outside this implementation.

Checklist

Breaking changes

None.

Obsoletions / Deprecations

None.

Fixed issues

None.

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068018-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@YoshihiroIto
YoshihiroIto marked this pull request as ready for review August 1, 2026 07:35
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068022-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul added enhancement os-windows backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch labels Aug 3, 2026
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Windows shell virtual-file support to Avalonia’s Win32 OLE data-transfer backend, enabling drag-and-drop of files that don’t yet have filesystem paths (e.g., items dragged out of ZIP folders in Windows Explorer) by exposing them through the existing DataFormat.File / IStorageFile APIs.

Changes:

  • Add OleVirtualFileData to parse FileGroupDescriptorW and fetch indexed FileContents as IStorageFile instances, including COM apartment-safe marshaling for IStream.
  • Extend OleDataObjectToDataTransferWrapper to surface virtual files as DataFormat.File and avoid duplicating file formats in the non-file transfer item.
  • Add Win32 integration tests and grant test assembly access via InternalsVisibleTo; add IDataObjectAsyncCapability to Win32 COM IDL.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Avalonia.IntegrationTests.Win32/OleVirtualFileDataTests.cs Adds integration tests for descriptor parsing, virtual file exposure, and async operation completion.
src/Windows/Avalonia.Win32/Win32Com/win32.idl Introduces IDataObjectAsyncCapability COM interface definition for async drag/drop lifetime management.
src/Windows/Avalonia.Win32/OleVirtualFileData.cs Implements virtual-file parsing, FileContents extraction (IStream/HGLOBAL), and async operation lifetime handling.
src/Windows/Avalonia.Win32/OleDataObjectToDataTransferWrapper.cs Detects virtual-file formats and materializes them as DataFormat.File items without duplicating formats in non-file items.
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj Adds InternalsVisibleTo for the Win32 integration test assembly.

Comment thread tests/Avalonia.IntegrationTests.Win32/OleVirtualFileDataTests.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs
YoshihiroIto and others added 3 commits August 3, 2026 17:40
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068036-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068079-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleDataObjectToDataTransferWrapper.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleDataObjectToDataTransferWrapper.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleDataObjectToDataTransferWrapper.cs
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs
Comment thread tests/Avalonia.IntegrationTests.Win32/OleVirtualFileDataTests.cs Outdated
@YoshihiroIto

Copy link
Copy Markdown
Contributor Author

@MrJul Thank you for the review. I addressed the requested changes in 15ff497:

  • replaced descriptor offsets with a native-layout FILEDESCRIPTORW
  • reused the existing HGLOBAL helper and moved COM P/Invokes to UnmanagedMethods
  • switched IStream access and lifetime management to a generated MicroCom proxy
  • consolidated format detection into one pass and handled the smaller nits
  • removed the offset-based descriptor test

I also merged the latest main. Avalonia.IntegrationTests.Win32 passes 242/242, the net8/net10 Release builds succeed, and a rooted win-x64 NativeAOT smoke publish plus executable run succeed. Could you please take another look?

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0069123-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
@YoshihiroIto

Copy link
Copy Markdown
Contributor Author

Follow-up for the latest review (commit dc7c913):

  • IStream.Read/Write/Seek now preserve the raw int HRESULT; Read accepts both S_OK and the documented S_FALSE EOF result.
  • ComReadStream delegates Seek and Position to the underlying IStream.
  • Removed MarshaledInterface; marshaled streams are held by generated MicroCom IStream proxies with an additional reference passed to CoGetInterfaceAndReleaseStream and deterministic disposal afterward.
  • TryCreateFile now isolates per-file failures, logs warnings, and allows remaining virtual files to be returned.

Validation after these changes: net8.0/net10.0 Release builds succeeded, Avalonia.IntegrationTests.Win32 passed 242/242, and the rooted win-x64 NativeAOT smoke publish and executable run succeeded. Please re-review when the CI checks finish.

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0069381-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last round of review with a few remaining nits, this PR is looking great!

Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
Comment thread src/Windows/Avalonia.Win32/OleVirtualFileData.cs Outdated
@YoshihiroIto

Copy link
Copy Markdown
Contributor Author

Addressed the five remaining review comments in 72ba8a7:

  • Switched the virtual-file creation warning to structured logging.
  • Added the requested spacing around the stream members.
  • Renamed Complete to CompleteFile to clarify its per-file responsibility.
  • Implemented ComReadStream.Length through IStream.Stat(STATFLAG_NONAME) while preserving the raw HRESULT.

Validation on Windows:

  • Avalonia.Win32 Release build: net8.0 and net10.0 passed.
  • Avalonia.IntegrationTests.Win32: 242/242 passed on net10.0.
  • NativeAOT smoke publish and executable run: passed.

All review threads are resolved. Please re-review when the CI checks complete.

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0069443-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested against Explorer's zip handler and Outlook's attachments, with both drag-and-drop and clipboard. It works as expected.

LGTM!

@MrJul
MrJul enabled auto-merge September 7, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch enhancement os-windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants