Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions src/Files.Uwp/BaseLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,21 +837,15 @@ protected virtual void Page_CharacterReceived(CoreWindow sender, CharacterReceiv
}
}

protected async void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
// Only support IStorageItem capable paths
e.Items.OfType<ListedItem>().ForEach(item => SelectedItems.Add(item));

try
{
// Get the log file to use its properties. For some reason the drag and drop operation
// requires a BasicProperties object even though does not seem to be used.
// We supply it regardless for every VirtualStorageItem because it is checked for
var fakeFilePropsItem = await StorageFile.GetFileFromPathAsync(Path.Combine(ApplicationData.Current.LocalFolder.Path, "debug.log"));
var props = await fakeFilePropsItem.GetBasicPropertiesAsync();
var itemList = e.Items.OfType<ListedItem>().Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcutItem)).Select(x => new VirtualStorageItem(x, props));
// Only support IStorageItem capable paths
var itemList = e.Items.OfType<ListedItem>().Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcutItem)).Select(x => VirtualStorageItem.FromListedItem(x));
e.Data.SetStorageItems(itemList, false);
//e.Data.RequestedOperation = DataPackageOperation.Move;
}
catch (Exception)
{
Expand Down
109 changes: 93 additions & 16 deletions src/Files.Uwp/Filesystem/StorageItems/VirtualStorageItem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System;
using Files.Uwp.Helpers;
using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.FileProperties;
using static Files.Uwp.Helpers.NativeFindStorageItemHelper;

namespace Files.Uwp.Filesystem.StorageItems
{
Expand All @@ -13,13 +17,86 @@ namespace Files.Uwp.Filesystem.StorageItems
/// </summary>
public class VirtualStorageItem : IStorageItem
{
private readonly ListedItem item;
private readonly BasicProperties props;
private static BasicProperties props;

public VirtualStorageItem(ListedItem item, BasicProperties props)
public Windows.Storage.FileAttributes Attributes { get; init; }

public DateTimeOffset DateCreated { get; init; }

public string Name { get; init; }

public string Path { get; init; }

private VirtualStorageItem() { }

public static VirtualStorageItem FromListedItem(ListedItem item)
{
return new VirtualStorageItem()
{
Name = item.ItemNameRaw,
Path = item.ItemPath,
DateCreated = item.ItemDateCreatedReal,
Attributes = item.IsZipItem || item.PrimaryItemAttribute == StorageItemTypes.File ? Windows.Storage.FileAttributes.Normal : Windows.Storage.FileAttributes.Directory
};
}

public static VirtualStorageItem FromPath(string path)
{
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
IntPtr hFile = FindFirstFileExFromApp(path, findInfoLevel, out WIN32_FIND_DATA findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, additionalFlags);
if (hFile.ToInt64() != -1)
{
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
bool isReparsePoint = ((System.IO.FileAttributes)findData.dwFileAttributes & System.IO.FileAttributes.ReparsePoint) == System.IO.FileAttributes.ReparsePoint;
bool isSymlink = isReparsePoint && findData.dwReserved0 == NativeFileOperationsHelper.IO_REPARSE_TAG_SYMLINK;
bool isHidden = ((System.IO.FileAttributes)findData.dwFileAttributes & System.IO.FileAttributes.Hidden) == System.IO.FileAttributes.Hidden;
bool isDirectory = ((System.IO.FileAttributes)findData.dwFileAttributes & System.IO.FileAttributes.Directory) == System.IO.FileAttributes.Directory;

if (!(isHidden && isSymlink))
{
DateTime itemCreatedDate;

try
{
FileTimeToSystemTime(ref findData.ftCreationTime, out SYSTEMTIME systemCreatedDateOutput);
itemCreatedDate = systemCreatedDateOutput.ToDateTime();
}
catch (ArgumentException)
{
// Invalid date means invalid findData, do not add to list
return null;
}

return new VirtualStorageItem()
{
Name = findData.cFileName,
Path = path,
DateCreated = itemCreatedDate,
Attributes = isDirectory ? Windows.Storage.FileAttributes.Directory : Windows.Storage.FileAttributes.Normal
};
}

FindClose(hFile);
}

return null;
}

private async void StreamedFileWriter(StreamedFileDataRequest request)
{
this.item = item;
this.props = props;
try
{
using (var stream = request.AsStreamForWrite())
{
await stream.FlushAsync();
}
request.Dispose();
}
catch (Exception)
{
request.FailAndClose(StreamedFileFailureMode.Incomplete);
}
}

public IAsyncAction RenameAsync(string desiredName)
Expand All @@ -44,20 +121,20 @@ public IAsyncAction DeleteAsync(StorageDeleteOption option)

public IAsyncOperation<BasicProperties> GetBasicPropertiesAsync()
{
return Task.FromResult(props).AsAsyncOperation();
return AsyncInfo.Run(async (cancellationToken) =>
{
async Task<BasicProperties> GetFakeBasicProperties()
{
var streamedFile = await StorageFile.CreateStreamedFileAsync(Name, StreamedFileWriter, null);
return await streamedFile.GetBasicPropertiesAsync();
}
return props ?? (props = await GetFakeBasicProperties());
});
}

public bool IsOfType(StorageItemTypes type)
{
return item.PrimaryItemAttribute == type;
return Attributes.HasFlag(Windows.Storage.FileAttributes.Directory) ? type == StorageItemTypes.Folder : type == StorageItemTypes.File;
}

public FileAttributes Attributes => item.PrimaryItemAttribute == StorageItemTypes.File ? FileAttributes.Normal : FileAttributes.Directory;

public DateTimeOffset DateCreated => item.ItemDateCreatedReal;

public string Name => item.ItemName;

public string Path => item.ItemPath;
}
}