Skip to content

Commit

Permalink
Fix missing paths when items dropped from archive (#14648)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jiejasonliu authored Jan 11, 2023
1 parent 7b9ec0e commit b7e537e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 9 deletions.
58 changes: 50 additions & 8 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -2526,16 +2527,57 @@ 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" }))
{
// Join the paths with spaces
if (!allPaths.empty())
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)
{
allPaths += L" ";
fullPaths.emplace_back(item.Path());
}
}

std::wstring fullPath{ item.Path() };
std::wstring allPathsString;
for (auto& fullPath : fullPaths)
{
// Join the paths with spaces
if (!allPathsString.empty())
{
allPathsString += L" ";
}

// Fix path for WSL
// In the fullness of time, we should likely plumb this up
Expand Down Expand Up @@ -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 });
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/dll/TerminalControl.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -59,6 +60,7 @@
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider);
#include <telemetry/ProjectTelemetry.h>

#include <shellapi.h>
#include <ShlObj_core.h>
#include <WinUser.h>

Expand Down

0 comments on commit b7e537e

Please sign in to comment.