Skip to content

Commit d317985

Browse files
authored
Code: Convert FileOperationsHandler + FileTagsHandler to helpers (#10008)
1 parent 8597ac3 commit d317985

29 files changed

+1906
-1602
lines changed

src/Files.App/App.xaml.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ public App()
9494
InitializeComponent();
9595
this.Services = ConfigureServices();
9696
Ioc.Default.ConfigureServices(Services);
97-
}
97+
}
9898

99-
private IServiceProvider ConfigureServices()
99+
private IServiceProvider ConfigureServices()
100100
{
101101
ServiceCollection services = new ServiceCollection();
102102

@@ -163,7 +163,10 @@ private static async Task EnsureSettingsAndConfigurationAreBootstrapped()
163163
FileTagsManager ??= new FileTagsManager();
164164
SidebarPinnedController ??= new SidebarPinnedController();
165165
TerminalController ??= new TerminalController();
166-
}
166+
167+
//FileTagsHelpers.UpdateTagsDb();
168+
FileOperationsHelpers.WaitForCompletion();
169+
}
167170

168171
private static async Task StartAppCenter()
169172
{
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.InteropServices.ComTypes;
7+
using System.Runtime.Versioning;
8+
using System.Windows.Forms;
9+
using Vanara.PInvoke;
10+
using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
11+
12+
namespace Files.App.DataModels
13+
{
14+
// Class taken from Rx-Explorer (https://github.com/zhuxb711/RX-Explorer)
15+
[SupportedOSPlatform("Windows")]
16+
public class RemoteDataObject
17+
{
18+
/// <summary>
19+
/// Holds the <see cref="System.Windows.IDataObject"/> that this class is wrapping
20+
/// </summary>
21+
private readonly System.Windows.Forms.IDataObject underlyingDataObject;
22+
23+
/// <summary>
24+
/// Holds the <see cref="System.Runtime.InteropServices.ComTypes.IDataObject"/> interface to the <see cref="System.Windows.IDataObject"/> that this class is wrapping.
25+
/// </summary>
26+
private readonly System.Runtime.InteropServices.ComTypes.IDataObject comUnderlyingDataObject;
27+
28+
/// <summary>
29+
/// Holds the internal ole <see cref="System.Windows.IDataObject"/> to the <see cref="System.Windows.IDataObject"/> that this class is wrapping.
30+
/// </summary>
31+
private readonly System.Windows.Forms.IDataObject oleUnderlyingDataObject;
32+
33+
/// <summary>
34+
/// Holds the <see cref="MethodInfo"/> of the "GetDataFromHGLOBAL" method of the internal ole <see cref="System.Windows.IDataObject"/>.
35+
/// </summary>
36+
private readonly MethodInfo getDataFromHGLOBALMethod;
37+
38+
/// <summary>
39+
/// Initializes a new instance of the class.
40+
/// </summary>
41+
/// <param name="underlyingDataObject">The underlying data object to wrap.</param>
42+
public RemoteDataObject(System.Windows.Forms.IDataObject underlyingDataObject)
43+
{
44+
//get the underlying dataobject and its ComType IDataObject interface to it
45+
this.underlyingDataObject = underlyingDataObject;
46+
comUnderlyingDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)this.underlyingDataObject;
47+
48+
//get the internal ole dataobject and its GetDataFromHGLOBAL so it can be called later
49+
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
50+
oleUnderlyingDataObject = (System.Windows.Forms.IDataObject)innerDataField.GetValue(this.underlyingDataObject);
51+
getDataFromHGLOBALMethod = oleUnderlyingDataObject.GetType().GetMethod("GetDataFromHGLOBAL", BindingFlags.NonPublic | BindingFlags.Instance);
52+
}
53+
54+
public IEnumerable<DataPackage> GetRemoteData()
55+
{
56+
string FormatName = string.Empty;
57+
58+
if (GetDataPresent(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW))
59+
{
60+
FormatName = Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW;
61+
}
62+
else if (GetDataPresent(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA))
63+
{
64+
FormatName = Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA;
65+
}
66+
67+
if (string.IsNullOrEmpty(FormatName))
68+
{
69+
yield break;
70+
}
71+
else
72+
{
73+
if (underlyingDataObject.GetData(FormatName, true) is MemoryStream FileGroupDescriptorStream)
74+
{
75+
try
76+
{
77+
byte[] FileGroupDescriptorBytes = FileGroupDescriptorStream.ToArray();
78+
79+
IntPtr FileGroupDescriptorAPointer = Marshal.AllocHGlobal(FileGroupDescriptorBytes.Length);
80+
81+
try
82+
{
83+
Marshal.Copy(FileGroupDescriptorBytes, 0, FileGroupDescriptorAPointer, FileGroupDescriptorBytes.Length);
84+
85+
int ItemCount = Marshal.ReadInt32(FileGroupDescriptorAPointer);
86+
87+
IntPtr FileDescriptorPointer = (IntPtr)(FileGroupDescriptorAPointer.ToInt64() + Marshal.SizeOf(ItemCount));
88+
89+
for (int FileDescriptorIndex = 0; FileDescriptorIndex < ItemCount; FileDescriptorIndex++)
90+
{
91+
Shell32.FILEDESCRIPTOR FileDescriptor = Marshal.PtrToStructure<Shell32.FILEDESCRIPTOR>(FileDescriptorPointer);
92+
93+
if (FileDescriptor.dwFileAttributes.HasFlag(FileFlagsAndAttributes.FILE_ATTRIBUTE_DIRECTORY))
94+
{
95+
yield return new DataPackage(FileDescriptor.cFileName, StorageType.Directroy, null);
96+
}
97+
else
98+
{
99+
yield return new DataPackage(FileDescriptor.cFileName, StorageType.File, GetContentData(Shell32.ShellClipboardFormat.CFSTR_FILECONTENTS, FileDescriptorIndex));
100+
}
101+
102+
FileDescriptorPointer = (IntPtr)(FileDescriptorPointer.ToInt64() + Marshal.SizeOf(FileDescriptor));
103+
}
104+
}
105+
finally
106+
{
107+
Marshal.FreeHGlobal(FileGroupDescriptorAPointer);
108+
}
109+
}
110+
finally
111+
{
112+
FileGroupDescriptorStream.Dispose();
113+
}
114+
}
115+
else
116+
{
117+
yield break;
118+
}
119+
}
120+
}
121+
122+
/// <summary>
123+
/// Retrieves the data associated with the specified data format at the specified index.
124+
/// </summary>
125+
/// <param name="Format">The format of the data to retrieve. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
126+
/// <param name="Index">The index of the data to retrieve.</param>
127+
/// <returns>
128+
/// A <see cref="MemoryStream"/> containing the raw data for the specified data format at the specified index.
129+
/// </returns>
130+
private MemoryStream GetContentData(string Format, int Index)
131+
{
132+
//create a FORMATETC struct to request the data with
133+
FORMATETC Formatetc = new FORMATETC
134+
{
135+
cfFormat = (short)DataFormats.GetFormat(Format).Id,
136+
dwAspect = DVASPECT.DVASPECT_CONTENT,
137+
lindex = Index,
138+
ptd = new IntPtr(0),
139+
tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE | TYMED.TYMED_HGLOBAL
140+
};
141+
142+
//using the Com IDataObject interface get the data using the defined FORMATETC
143+
comUnderlyingDataObject.GetData(ref Formatetc, out STGMEDIUM Medium);
144+
145+
//retrieve the data depending on the returned store type
146+
switch (Medium.tymed)
147+
{
148+
case TYMED.TYMED_ISTORAGE:
149+
{
150+
//to handle a IStorage it needs to be written into a second unmanaged
151+
//memory mapped storage and then the data can be read from memory into
152+
//a managed byte and returned as a MemoryStream
153+
154+
try
155+
{
156+
//marshal the returned pointer to a IStorage object
157+
Ole32.IStorage IStorageObject = (Ole32.IStorage)Marshal.GetObjectForIUnknown(Medium.unionmember);
158+
159+
try
160+
{
161+
//create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store
162+
Ole32.CreateILockBytesOnHGlobal(IntPtr.Zero, true, out Ole32.ILockBytes LockBytes);
163+
Ole32.StgCreateDocfileOnILockBytes(LockBytes, STGM.STGM_READWRITE | STGM.STGM_SHARE_EXCLUSIVE | STGM.STGM_CREATE, ppstgOpen: out Ole32.IStorage IStorageObjectCopy);
164+
165+
try
166+
{
167+
//copy the returned IStorage into the new IStorage
168+
IStorageObject.CopyTo(snbExclude: IntPtr.Zero, pstgDest: IStorageObjectCopy);
169+
LockBytes.Flush();
170+
IStorageObjectCopy.Commit(Ole32.STGC.STGC_DEFAULT);
171+
172+
//get the STATSTG of the LockBytes to determine how many bytes were written to it
173+
LockBytes.Stat(out STATSTG LockBytesStat, Ole32.STATFLAG.STATFLAG_NONAME);
174+
175+
int CbSize = Convert.ToInt32(LockBytesStat.cbSize);
176+
177+
IntPtr LockBytesContentPtr = Marshal.AllocHGlobal(CbSize);
178+
179+
try
180+
{
181+
LockBytes.ReadAt(0, LockBytesContentPtr, Convert.ToUInt32(LockBytesStat.cbSize), out _);
182+
183+
byte[] LockBytesContent = new byte[CbSize];
184+
185+
Marshal.Copy(LockBytesContentPtr, LockBytesContent, 0, LockBytesContent.Length);
186+
187+
return new MemoryStream(LockBytesContent);
188+
}
189+
finally
190+
{
191+
Marshal.FreeHGlobal(LockBytesContentPtr);
192+
}
193+
}
194+
finally
195+
{
196+
Marshal.ReleaseComObject(IStorageObjectCopy);
197+
Marshal.ReleaseComObject(LockBytes);
198+
}
199+
}
200+
finally
201+
{
202+
Marshal.ReleaseComObject(IStorageObject);
203+
}
204+
}
205+
finally
206+
{
207+
Marshal.Release(Medium.unionmember);
208+
}
209+
}
210+
case TYMED.TYMED_ISTREAM:
211+
{
212+
//to handle a IStream it needs to be read into a managed byte and
213+
//returned as a MemoryStream
214+
215+
IStream IStreamObject = (IStream)Marshal.GetObjectForIUnknown(Medium.unionmember);
216+
217+
try
218+
{
219+
//get the STATSTG of the IStream to determine how many bytes are in it
220+
IStreamObject.Stat(out STATSTG iStreamStat, 0);
221+
222+
byte[] IStreamContent = new byte[(Convert.ToInt32(iStreamStat.cbSize))];
223+
224+
IStreamObject.Read(IStreamContent, IStreamContent.Length, IntPtr.Zero);
225+
226+
return new MemoryStream(IStreamContent);
227+
}
228+
finally
229+
{
230+
Marshal.Release(Medium.unionmember);
231+
Marshal.ReleaseComObject(IStreamObject);
232+
}
233+
}
234+
case TYMED.TYMED_HGLOBAL:
235+
{
236+
//to handle a HGlobal the exisitng "GetDataFromHGLOBAL" method is invoked via
237+
//reflection
238+
239+
try
240+
{
241+
return (MemoryStream)getDataFromHGLOBALMethod.Invoke(oleUnderlyingDataObject, new object[] { DataFormats.GetFormat(Formatetc.cfFormat).Name, Medium.unionmember });
242+
}
243+
finally
244+
{
245+
Marshal.Release(Medium.unionmember);
246+
}
247+
}
248+
default:
249+
{
250+
return null;
251+
}
252+
}
253+
}
254+
255+
/// <summary>
256+
/// Determines whether data stored in this instance is associated with, or can be converted to, the specified format.
257+
/// </summary>
258+
/// <param name="format">The format for which to check. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
259+
/// <returns>
260+
/// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise false.
261+
/// </returns>
262+
public bool GetDataPresent(string format)
263+
{
264+
return underlyingDataObject.GetDataPresent(format);
265+
}
266+
267+
public sealed class DataPackage : IDisposable
268+
{
269+
public StorageType ItemType { get; }
270+
271+
public MemoryStream ContentStream { get; }
272+
273+
public string Name { get; }
274+
275+
public DataPackage(string Name, StorageType ItemType, MemoryStream ContentStream)
276+
{
277+
this.Name = Name;
278+
this.ItemType = ItemType;
279+
this.ContentStream = ContentStream;
280+
}
281+
282+
public void Dispose()
283+
{
284+
GC.SuppressFinalize(this);
285+
ContentStream?.Dispose();
286+
}
287+
288+
~DataPackage()
289+
{
290+
Dispose();
291+
}
292+
}
293+
294+
public enum StorageType
295+
{
296+
File = 0,
297+
Directroy = 1
298+
}
299+
}
300+
}

src/Files.App/Files.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
176176
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
177177
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
178+
<PackageReference Include="Tulpep.ActiveDirectoryObjectPicker" Version="3.0.11" />
178179
<PackageReference Include="WinUIEx" Version="1.8.0" />
179180
<PackageReference Include="Vanara.PInvoke.Mpr" Version="3.4.9" />
180181
<PackageReference Include="Vanara.Windows.Shell" Version="3.3.15" />

src/Files.App/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,18 +375,7 @@ public async Task<ReturnResult> CopyItemsFromClipboard(DataPackageView packageVi
375375
var source = await Filesystem.FilesystemHelpers.GetDraggedStorageItems(packageView);
376376

377377
if (handledByFtp)
378-
{
379-
var connection = await ServiceConnection;
380-
if (connection != null)
381-
{
382-
var (status, response) = await connection.SendMessageForResponseAsync(new ValueSet() {
383-
{ "Arguments", "FileOperation" },
384-
{ "fileop", "DragDrop" },
385-
{ "droppath", associatedInstance.FilesystemViewModel.WorkingDirectory } });
386-
return (status == AppServiceResponseStatus.Success && response.Get("Success", defaultJson).GetBoolean()) ? ReturnResult.Success : ReturnResult.Failed;
387-
}
388-
return ReturnResult.Failed;
389-
}
378+
return await FileOperationsHelpers.DragDropAsync(associatedInstance.FilesystemViewModel.WorkingDirectory) ? ReturnResult.Success : ReturnResult.Failed;
390379

391380
if (!source.IsEmpty())
392381
{

0 commit comments

Comments
 (0)