Fix missing paths when items dropped from archive (#14648)

Grab all paths from `DROPFILES` struct provided in drag event data

`GetStorageItemsAsync()` only giving up to 16 items when items are dropped from any archives
- When this occurs, we should look into `FileDrop` key for a stream of the [`DROPFILES struct`](https://learn.microsoft.com/en-us/windows/win32/shell/clipboard#cf_hdrop)
- This struct contains a null-character delimited string of paths which we can just read out

## Validation Steps Performed
* [X] Unit tests pass locally
* [X] Drag and drop paths work for both archives and non-archives files, folders, shortcuts, etc.

Closes #14628
This commit is contained in:
Jie 'Jason' Liu
2023-01-10 17:40:22 -07:00
committed by GitHub
parent 7b9ec0ed6a
commit b7e537e5e7
3 changed files with 53 additions and 9 deletions

View File

@@ -25,6 +25,7 @@ using namespace winrt::Windows::UI::ViewManagement;
using namespace winrt::Windows::UI::Input;
using namespace winrt::Windows::System;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::Storage::Streams;
// The minimum delay between updates to the scroll bar's values.
// The updates are throttled to limit power usage.
@@ -2526,17 +2527,58 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (items.Size() > 0)
{
std::wstring allPaths;
for (auto item : items)
std::vector<std::wstring> fullPaths;
// GH#14628: Workaround for GetStorageItemsAsync() only returning 16 items
// at most when dragging and dropping from archives (zip, 7z, rar, etc.)
if (items.Size() == 16 && e.DataView().Contains(winrt::hstring{ L"FileDrop" }))
{
auto fileDropData = co_await e.DataView().GetDataAsync(winrt::hstring{ L"FileDrop" });
if (fileDropData != nullptr)
{
auto stream = fileDropData.as<IRandomAccessStream>();
stream.Seek(0);
const uint32_t streamSize = gsl::narrow_cast<uint32_t>(stream.Size());
const Buffer buf(streamSize);
const auto buffer = co_await stream.ReadAsync(buf, streamSize, InputStreamOptions::None);
const HGLOBAL hGlobal = buffer.data();
const auto count = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0);
fullPaths.reserve(count);
for (unsigned int i = 0; i < count; i++)
{
std::wstring path;
path.resize(wil::max_path_length);
const auto charsCopied = DragQueryFileW(static_cast<HDROP>(hGlobal), i, path.data(), wil::max_path_length);
if (charsCopied > 0)
{
path.resize(charsCopied);
fullPaths.emplace_back(std::move(path));
}
}
}
}
else
{
fullPaths.reserve(items.Size());
for (const auto& item : items)
{
fullPaths.emplace_back(item.Path());
}
}
std::wstring allPathsString;
for (auto& fullPath : fullPaths)
{
// Join the paths with spaces
if (!allPaths.empty())
if (!allPathsString.empty())
{
allPaths += L" ";
allPathsString += L" ";
}
std::wstring fullPath{ item.Path() };
// Fix path for WSL
// In the fullness of time, we should likely plumb this up
// to the TerminalApp layer, and have it make the decision
@@ -2591,10 +2633,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
fullPath += L"\"";
}
allPaths += fullPath;
allPathsString += fullPath;
}
_core.PasteText(winrt::hstring{ allPaths });
_core.PasteText(winrt::hstring{ allPathsString });
}
}
}

View File

@@ -88,7 +88,7 @@
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;user32.lib;kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;user32.lib;shell32.lib;kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!--
ControlLib contains a DllMain that we need to force the use of.
If you don't have this, then you'll see an error like

View File

@@ -48,6 +48,7 @@
#include <winrt/Windows.ui.xaml.shapes.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.UI.Xaml.Shapes.h>
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
@@ -59,6 +60,7 @@
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider);
#include <telemetry/ProjectTelemetry.h>
#include <shellapi.h>
#include <ShlObj_core.h>
#include <WinUser.h>