Skip to content

Files.Launcher: Code cleanup #1367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2020
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
62 changes: 38 additions & 24 deletions Files.Launcher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@
using Windows.ApplicationModel.AppService;
using Windows.Foundation.Collections;
using Windows.Storage;
using static Vanara.PInvoke.Shell32;
using Vanara.PInvoke;
using NLog;

namespace FilesFullTrust
{
internal class Program
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

[STAThread]
private static void Main(string[] args)
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config"));
NLog.LogManager.Configuration.Variables["LogPath"] = storageFolder.Path;
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config"));
LogManager.Configuration.Variables["LogPath"] = storageFolder.Path;

AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;

Expand All @@ -41,12 +42,15 @@ private static void Main(string[] args)
// Only one instance of the fulltrust process allowed
// This happens if multiple instances of the UWP app are launched
using var mutex = new Mutex(true, "FilesUwpFullTrust", out bool isNew);
if (!isNew) return;
if (!isNew)
{
return;
}

try
{
// Create shell COM object and get recycle bin folder
recycler = new ShellFolder(KNOWNFOLDERID.FOLDERID_RecycleBinFolder);
recycler = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_RecycleBinFolder);
ApplicationData.Current.LocalSettings.Values["RecycleBin_Title"] = recycler.Name;

// Create filesystem watcher to monitor recycle bin folder(s)
Expand Down Expand Up @@ -98,7 +102,7 @@ private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionE

private static async void Watcher_Changed(object sender, FileSystemEventArgs e)
{
Debug.WriteLine("Reycle bin event: {0}, {1}", e.ChangeType, e.FullPath);
Debug.WriteLine($"Reycle bin event: {e.ChangeType}, {e.FullPath}");
if (connection != null)
{
// Send message to UWP app to refresh items
Expand Down Expand Up @@ -262,15 +266,15 @@ private static async Task parseRecycleBinAction(AppServiceRequestReceivedEventAr
{
case "Empty":
// Shell function to empty recyclebin
SHEmptyRecycleBin(IntPtr.Zero, null, SHERB.SHERB_NOCONFIRMATION | SHERB.SHERB_NOPROGRESSUI);
Shell32.SHEmptyRecycleBin(IntPtr.Zero, null, Shell32.SHERB.SHERB_NOCONFIRMATION | Shell32.SHERB.SHERB_NOPROGRESSUI);
break;

case "Query":
var responseQuery = new ValueSet();
SHQUERYRBINFO queryBinInfo = new SHQUERYRBINFO();
Shell32.SHQUERYRBINFO queryBinInfo = new Shell32.SHQUERYRBINFO();
queryBinInfo.cbSize = (uint)Marshal.SizeOf(queryBinInfo);
var res = SHQueryRecycleBin(null, ref queryBinInfo);
if (res == Vanara.PInvoke.HRESULT.S_OK)
var res = Shell32.SHQueryRecycleBin(null, ref queryBinInfo);
if (res == HRESULT.S_OK)
{
var numItems = queryBinInfo.i64NumItems;
var binSize = queryBinInfo.i64Size;
Expand Down Expand Up @@ -298,13 +302,13 @@ private static async Task parseRecycleBinAction(AppServiceRequestReceivedEventAr
continue;
}
folderItem.Properties.TryGetValue<System.Runtime.InteropServices.ComTypes.FILETIME?>(
Vanara.PInvoke.Ole32.PROPERTYKEY.System.DateCreated, out var fileTime);
Ole32.PROPERTYKEY.System.DateCreated, out var fileTime);
var recycleDate = fileTime?.ToDateTime().ToLocalTime() ?? DateTime.Now; // This is LocalTime
string fileSize = folderItem.Properties.TryGetValue<ulong?>(
Vanara.PInvoke.Ole32.PROPERTYKEY.System.Size, out var fileSizeBytes) ?
folderItem.Properties.GetPropertyString(Vanara.PInvoke.Ole32.PROPERTYKEY.System.Size) : null;
Ole32.PROPERTYKEY.System.Size, out var fileSizeBytes) ?
folderItem.Properties.GetPropertyString(Ole32.PROPERTYKEY.System.Size) : null;
folderItem.Properties.TryGetValue<string>(
Vanara.PInvoke.Ole32.PROPERTYKEY.System.ItemTypeText, out var fileType);
Ole32.PROPERTYKEY.System.ItemTypeText, out var fileType);
folderContentsList.Add(new ShellFileItem(isFolder, recyclePath, fileName, filePath, recycleDate, fileSize, fileSizeBytes ?? 0, fileType));
}
catch (FileNotFoundException)
Expand Down Expand Up @@ -398,32 +402,42 @@ await Win32API.StartSTATask(() =>
}
else
{
var groups = split.GroupBy(x => new {
var groups = split.GroupBy(x => new
{
Dir = Path.GetDirectoryName(x),
Prog = Win32API.GetFileAssociation(x).Result ?? Path.GetExtension(x)
});
foreach (var group in groups)
{
if (!group.Any()) continue;
if (!group.Any())
{
continue;
}

var files = group.Select(x => new ShellItem(x));
using var sf = files.First().Parent;
Vanara.PInvoke.Shell32.IContextMenu menu = null;
Shell32.IContextMenu menu = null;
try
{
menu = sf.GetChildrenUIObjects<Vanara.PInvoke.Shell32.IContextMenu>(null, files.ToArray());
menu.QueryContextMenu(Vanara.PInvoke.HMENU.NULL, 0, 0, 0, Vanara.PInvoke.Shell32.CMF.CMF_DEFAULTONLY);
var pici = new Vanara.PInvoke.Shell32.CMINVOKECOMMANDINFOEX();
pici.lpVerb = Vanara.PInvoke.Shell32.CMDSTR_OPEN;
pici.nShow = Vanara.PInvoke.ShowWindowCommand.SW_SHOW;
menu = sf.GetChildrenUIObjects<Shell32.IContextMenu>(null, files.ToArray());
menu.QueryContextMenu(HMENU.NULL, 0, 0, 0, Shell32.CMF.CMF_DEFAULTONLY);
var pici = new Shell32.CMINVOKECOMMANDINFOEX();
pici.lpVerb = Shell32.CMDSTR_OPEN;
pici.nShow = ShowWindowCommand.SW_SHOW;
pici.cbSize = (uint)Marshal.SizeOf(pici);
menu.InvokeCommand(pici);
}
finally
{
foreach (var elem in files)
{
elem.Dispose();
}

if (menu != null)
{
Marshal.ReleaseComObject(menu);
}
}
}
}
Expand Down Expand Up @@ -483,7 +497,7 @@ private static string GetMtpPath(string executable)
{
if (executable.StartsWith("\\\\?\\"))
{
using var computer = new ShellFolder(Vanara.PInvoke.Shell32.KNOWNFOLDERID.FOLDERID_ComputerFolder);
using var computer = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_ComputerFolder);
using var device = computer.FirstOrDefault(i => executable.Replace("\\\\?\\", "").StartsWith(i.Name));
var deviceId = device?.ParsingName;
var itemPath = Regex.Replace(executable, @"^\\\\\?\\[^\\]*\\?", "");
Expand Down
77 changes: 37 additions & 40 deletions Files.Launcher/Win32API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
using System.Threading;
using System.Threading.Tasks;
using Vanara.Windows.Shell;
using Vanara.PInvoke;
using Windows.System;
using System.IO;

namespace FilesFullTrust
{
Expand All @@ -32,18 +35,23 @@ public static Task<T> StartSTATask<T>(Func<T> func)
return tcs.Task;
}

[DllImport("shell32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);

public static async Task<string> GetFileAssociation(string filename)
{
// Find UWP apps
var uwp_apps = await Windows.System.Launcher.FindFileHandlersAsync(System.IO.Path.GetExtension(filename));
if (uwp_apps.Any()) return uwp_apps.First().PackageFamilyName;
var uwp_apps = await Launcher.FindFileHandlersAsync(Path.GetExtension(filename));
if (uwp_apps.Any())
{
return uwp_apps.First().PackageFamilyName;
}

// Find desktop apps
var lpResult = new StringBuilder();
var hResult = FindExecutable(filename, null, lpResult);
if (hResult.ToInt64() > 32) return lpResult.ToString();
var hResult = Shell32.FindExecutable(filename, null, lpResult);
if (hResult.ToInt64() > 32)
{
return lpResult.ToString();
}

return null;
}

Expand All @@ -53,28 +61,28 @@ public enum PropertyReturnType
DISPLAYVALUE
}

public static List<(Vanara.PInvoke.Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)> RecyledFileProperties =
new List<(Vanara.PInvoke.Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)>
public static List<(Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)> RecyledFileProperties =
new List<(Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)>
{
(Vanara.PInvoke.Ole32.PROPERTYKEY.System.Size, PropertyReturnType.RAWVALUE),
(Vanara.PInvoke.Ole32.PROPERTYKEY.System.Size, PropertyReturnType.DISPLAYVALUE),
(Vanara.PInvoke.Ole32.PROPERTYKEY.System.ItemTypeText, PropertyReturnType.RAWVALUE),
(Ole32.PROPERTYKEY.System.Size, PropertyReturnType.RAWVALUE),
(Ole32.PROPERTYKEY.System.Size, PropertyReturnType.DISPLAYVALUE),
(Ole32.PROPERTYKEY.System.ItemTypeText, PropertyReturnType.RAWVALUE),
(PropertyStore.GetPropertyKeyFromName("System.Recycle.DateDeleted"), PropertyReturnType.RAWVALUE)
};

// A faster method of getting file shell properties (currently non used)
public static IList<object> GetFileProperties(ShellItem folderItem, List<(Vanara.PInvoke.Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)> properties)
public static IList<object> GetFileProperties(ShellItem folderItem, List<(Ole32.PROPERTYKEY propertyKey, PropertyReturnType returnType)> properties)
{
var propValueList = new List<object>(properties.Count);
var flags = Vanara.PInvoke.PropSys.GETPROPERTYSTOREFLAGS.GPS_DEFAULT | Vanara.PInvoke.PropSys.GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY;
var flags = PropSys.GETPROPERTYSTOREFLAGS.GPS_DEFAULT | PropSys.GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY;

Vanara.PInvoke.PropSys.IPropertyStore pStore = null;
PropSys.IPropertyStore pStore = null;
try
{
pStore = ((Vanara.PInvoke.Shell32.IShellItem2)folderItem.IShellItem).GetPropertyStoreForKeys(properties.Select(p => p.propertyKey).ToArray(), (uint)properties.Count, flags, typeof(Vanara.PInvoke.PropSys.IPropertyStore).GUID);
pStore = ((Shell32.IShellItem2)folderItem.IShellItem).GetPropertyStoreForKeys(properties.Select(p => p.propertyKey).ToArray(), (uint)properties.Count, flags, typeof(PropSys.IPropertyStore).GUID);
foreach (var prop in properties)
{
using var propVariant = new Vanara.PInvoke.Ole32.PROPVARIANT();
using var propVariant = new Ole32.PROPVARIANT();
pStore.GetValue(prop.propertyKey, propVariant);
if (prop.returnType == PropertyReturnType.RAWVALUE)
{
Expand All @@ -83,7 +91,7 @@ public static IList<object> GetFileProperties(ShellItem folderItem, List<(Vanara
else if (prop.returnType == PropertyReturnType.DISPLAYVALUE)
{
using var pDesc = PropertyDescription.Create(prop.propertyKey);
var pValue = pDesc?.FormatForDisplay(propVariant, Vanara.PInvoke.PropSys.PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT);
var pValue = pDesc?.FormatForDisplay(propVariant, PropSys.PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT);
propValueList.Add(pValue);
}
}
Expand All @@ -96,39 +104,28 @@ public static IList<object> GetFileProperties(ShellItem folderItem, List<(Vanara
return propValueList;
}

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int LoadString(IntPtr hInstance, int ID, StringBuilder lpBuffer, int nBufferMax);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeLibrary(IntPtr hModule);

public static string ExtractStringFromDLL(string file, int number)
{
IntPtr lib = LoadLibrary(file);
var lib = Kernel32.LoadLibrary(file);
StringBuilder result = new StringBuilder(2048);
LoadString(lib, number, result, result.Capacity);
FreeLibrary(lib);
User32.LoadString(lib, number, result, result.Capacity);
Kernel32.FreeLibrary(lib);
return result.ToString();
}

[DllImport("shell32.dll", SetLastError = true)]
public static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);

[DllImport("kernel32.dll")]
public static extern IntPtr LocalFree(IntPtr hMem);

public static string[] CommandLineToArgs(string commandLine)
{
if (String.IsNullOrEmpty(commandLine))
if (string.IsNullOrEmpty(commandLine))
{
return Array.Empty<string>();
}

var argv = CommandLineToArgvW(commandLine, out int argc);
var argv = Shell32.CommandLineToArgvW(commandLine, out int argc);
if (argv == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
{
throw new Win32Exception();
}

try
{
var args = new string[argc];
Expand Down