Files
ContextMenuManager/ContextMenuManager/Controls/ShellList.cs
2021-03-10 23:21:52 +08:00

534 lines
23 KiB
C#

using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Controls.Interfaces;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Xml;
namespace ContextMenuManager.Controls
{
sealed class ShellList : MyList
{
public const string MENUPATH_FILE = @"HKEY_CLASSES_ROOT\*";//文件
public const string MENUPATH_FOLDER = @"HKEY_CLASSES_ROOT\Folder";//文件夹
public const string MENUPATH_DIRECTORY = @"HKEY_CLASSES_ROOT\Directory";//目录
public const string MENUPATH_BACKGROUND = @"HKEY_CLASSES_ROOT\Directory\Background";//目录背景
public const string MENUPATH_DESKTOP = @"HKEY_CLASSES_ROOT\DesktopBackground";//桌面背景
public const string MENUPATH_DRIVE = @"HKEY_CLASSES_ROOT\Drive";//磁盘分区
public const string MENUPATH_ALLOBJECTS = @"HKEY_CLASSES_ROOT\AllFilesystemObjects";//所有对象
public const string MENUPATH_COMPUTER = @"HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}";//此电脑
public const string MENUPATH_RECYCLEBIN = @"HKEY_CLASSES_ROOT\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}";//回收站
public const string MENUPATH_LIBRARY = @"HKEY_CLASSES_ROOT\LibraryFolder";//库
public const string MENUPATH_LIBRARY_BACKGROUND = @"HKEY_CLASSES_ROOT\LibraryFolder\Background";//库背景
public const string MENUPATH_LIBRARY_USER = @"HKEY_CLASSES_ROOT\UserLibraryFolder";//用户库
public const string MENUPATH_UWPLNK = @"HKEY_CLASSES_ROOT\Launcher.ImmersiveApplication";//UWP快捷方式
public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown";//未知格式
public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations";//系统扩展名注册表父项路径
public enum Scenes
{
File, Folder, Directory, Background, Desktop, Drive, AllObjects, Computer, RecycleBin, Library,
LnkFile, UwpLnk, ExeFile, UnknownType, CustomExtension, PerceivedType, DirectoryType, CommandStore, DragDrop
}
private static readonly string[] DirectoryTypes = { "Document", "Image", "Video", "Audio" };
private static readonly string[] PerceivedTypes = { "Text", "Document", "Image", "Video", "Audio", "Compressed", "System" };
private static readonly string[] DirectoryTypeNames =
{
AppString.Dialog.DocumentDirectory, AppString.Dialog.ImageDirectory,
AppString.Dialog.VideoDirectory, AppString.Dialog.AudioDirectory
};
private static readonly string[] PerceivedTypeNames =
{
AppString.Dialog.TextFile, AppString.Dialog.DocumentFile, AppString.Dialog.ImageFile, AppString.Dialog.VideoFile,
AppString.Dialog.AudioFile, AppString.Dialog.CompressedFile, AppString.Dialog.SystemFile
};
private static string GetDirectoryTypeName()
{
if(CurrentDirectoryType != null)
{
for(int i = 0; i < DirectoryTypes.Length; i++)
{
if(CurrentDirectoryType.Equals(DirectoryTypes[i], StringComparison.OrdinalIgnoreCase))
{
return DirectoryTypeNames[i];
}
}
}
return null;
}
private static string GetPerceivedTypeName()
{
if(CurrentPerceivedType != null)
{
for(int i = 0; i < PerceivedTypes.Length; i++)
{
if(CurrentPerceivedType.Equals(PerceivedTypes[i], StringComparison.OrdinalIgnoreCase))
{
return PerceivedTypeNames[i];
}
}
}
return null;
}
private static string CurrentExtension = null;
private static string CurrentDirectoryType = null;
private static string CurrentPerceivedType = null;
private static string GetShellPath(string scenePath) => $@"{scenePath}\shell";
private static string GetShellExPath(string scenePath) => $@"{scenePath}\shellEx";
private static string GetSysAssExtPath(string typeName) => typeName != null ? $@"{SYSFILEASSPATH}\{typeName}" : null;
private static string GetOpenModePath(string extension) => extension != null ? $@"HKEY_CLASSES_ROOT\{FileExtension.GetOpenMode(extension)}" : null;
public Scenes Scene { get; set; }
public ShellList()
{
SelectItem.SelectedChanged += (sender, e) => { this.ClearItems(); this.LoadItems(); };
}
public void LoadItems()
{
string scenePath = null;
switch(Scene)
{
case Scenes.File:
scenePath = MENUPATH_FILE; break;
case Scenes.Folder:
scenePath = MENUPATH_FOLDER; break;
case Scenes.Directory:
scenePath = MENUPATH_DIRECTORY; break;
case Scenes.Background:
scenePath = MENUPATH_BACKGROUND; break;
case Scenes.Desktop:
//Vista系统没有这一项
if(WindowsOsVersion.IsEqualVista) return;
scenePath = MENUPATH_DESKTOP; break;
case Scenes.Drive:
scenePath = MENUPATH_DRIVE; break;
case Scenes.AllObjects:
scenePath = MENUPATH_ALLOBJECTS; break;
case Scenes.Computer:
scenePath = MENUPATH_COMPUTER; break;
case Scenes.RecycleBin:
scenePath = MENUPATH_RECYCLEBIN; break;
case Scenes.Library:
//Vista系统没有这一项
if(WindowsOsVersion.IsEqualVista) return;
scenePath = MENUPATH_LIBRARY; break;
case Scenes.LnkFile:
scenePath = GetSysAssExtPath(".lnk"); break;
case Scenes.UwpLnk:
//Win8之前没有Uwp
if(WindowsOsVersion.IsBefore8) return;
scenePath = MENUPATH_UWPLNK; break;
case Scenes.ExeFile:
scenePath = GetSysAssExtPath(".exe"); break;
case Scenes.UnknownType:
scenePath = MENUPATH_UNKNOWN; break;
case Scenes.CustomExtension:
scenePath = GetSysAssExtPath(CurrentExtension); break;
case Scenes.PerceivedType:
scenePath = GetSysAssExtPath(CurrentPerceivedType); break;
case Scenes.DirectoryType:
if(CurrentDirectoryType == null) scenePath = null;
else scenePath = GetSysAssExtPath($"Directory.{CurrentDirectoryType}"); break;
case Scenes.CommandStore:
//Vista系统没有这一项
if(WindowsOsVersion.IsEqualVista) return;
this.AddNewItem(RegistryEx.GetParentPath(ShellItem.CommandStorePath));
this.LoadStoreItems();
return;
case Scenes.DragDrop:
this.AddNewItem(MENUPATH_FOLDER);
this.LoadShellExItems(GetShellExPath(MENUPATH_FOLDER));
this.LoadShellExItems(GetShellExPath(MENUPATH_DIRECTORY));
this.LoadShellExItems(GetShellExPath(MENUPATH_DRIVE));
this.LoadShellExItems(GetShellExPath(MENUPATH_ALLOBJECTS));
return;
}
this.AddNewItem(scenePath);
this.LoadItems(scenePath);
if(WindowsOsVersion.ISAfterOrEqual10) this.AddUwpModeItem();
switch(Scene)
{
case Scenes.Background:
this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.CustomFolder));
break;
case Scenes.Computer:
this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.NetworkDrive));
break;
case Scenes.RecycleBin:
this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.RecycleBinProperties));
break;
case Scenes.Library:
this.LoadItems(MENUPATH_LIBRARY_BACKGROUND);
this.LoadItems(MENUPATH_LIBRARY_USER);
break;
case Scenes.LnkFile:
this.LoadItems(GetOpenModePath(".lnk"));
break;
case Scenes.ExeFile:
this.LoadItems(GetOpenModePath(".exe"));
break;
case Scenes.CustomExtension:
case Scenes.PerceivedType:
case Scenes.DirectoryType:
this.InsertItem(new SelectItem(Scene), 0);
if(Scene == Scenes.CustomExtension) this.LoadItems(GetOpenModePath(CurrentExtension));
break;
}
}
private void LoadItems(string scenePath)
{
if(scenePath == null) return;
RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath);
this.LoadShellItems(GetShellPath(scenePath));
this.LoadShellExItems(GetShellExPath(scenePath));
}
private void LoadShellItems(string shellPath)
{
using(RegistryKey shellKey = RegistryEx.GetRegistryKey(shellPath))
{
if(shellKey == null) return;
RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name);
Array.ForEach(shellKey.GetSubKeyNames(), keyName =>
{
this.AddItem(new ShellItem($@"{shellPath}\{keyName}"));
});
}
}
private void LoadShellExItems(string shellExPath)
{
List<string> names = new List<string>();
using(RegistryKey shellExKey = RegistryEx.GetRegistryKey(shellExPath))
{
if(shellExKey == null) return;
bool isDragDrop = Scene == Scenes.DragDrop;
RegTrustedInstaller.TakeRegTreeOwnerShip(shellExKey.Name);
Dictionary<string, Guid> dic = ShellExItem.GetPathAndGuids(shellExPath, isDragDrop);
GroupPathItem groupItem = null;
if(isDragDrop)
{
groupItem = GetDragDropGroupItem(shellExPath);
this.AddItem(groupItem);
}
foreach(string path in dic.Keys)
{
string keyName = RegistryEx.GetKeyName(path);
if(!names.Contains(keyName))
{
ShellExItem item = new ShellExItem(dic[path], path);
if(groupItem != null) item.FoldGroupItem = groupItem;
this.AddItem(item);
names.Add(keyName);
}
}
if(groupItem != null) groupItem.IsFold = true;
}
}
private GroupPathItem GetDragDropGroupItem(string shellExPath)
{
string text = null;
Image image = null;
string path = shellExPath.Substring(0, shellExPath.LastIndexOf('\\'));
switch(path)
{
case MENUPATH_FOLDER:
text = AppString.SideBar.Folder;
image = AppImage.Folder;
break;
case MENUPATH_DIRECTORY:
text = AppString.SideBar.Directory;
image = AppImage.Directory;
break;
case MENUPATH_DRIVE:
text = AppString.SideBar.Drive;
image = AppImage.Drive;
break;
case MENUPATH_ALLOBJECTS:
text = AppString.SideBar.AllObjects;
image = AppImage.AllObjects;
break;
}
return new GroupPathItem(shellExPath, ObjectPath.PathType.Registry) { Text = text, Image = image };
}
private void AddNewItem(string scenePath)
{
NewItem newItem = new NewItem { Visible = scenePath != null };
this.AddItem(newItem);
newItem.AddNewItem += (sender, e) =>
{
bool isShell;
if(Scene == Scenes.CommandStore) isShell = true;
else if(Scene == Scenes.DragDrop) isShell = false;
else
{
using(SelectDialog dlg = new SelectDialog())
{
dlg.Items = new[] { "Shell", "ShellEx" };
dlg.Title = AppString.Dialog.SelectNewItemType;
dlg.Selected = dlg.Items[0];
if(dlg.ShowDialog() != DialogResult.OK) return;
isShell = dlg.SelectedIndex == 0;
}
}
if(isShell) this.AddNewShellItem(scenePath);
else this.AddNewShellExItem(scenePath);
};
}
private void AddNewShellItem(string scenePath)
{
string shellPath = GetShellPath(scenePath);
using(NewShellDialog dlg = new NewShellDialog())
{
dlg.ScenePath = scenePath;
dlg.ShellPath = shellPath;
if(dlg.ShowDialog() != DialogResult.OK) return;
for(int i = 0; i < this.Controls.Count; i++)
{
if(this.Controls[i] is NewItem)
{
ShellItem item;
if(Scene != Scenes.CommandStore) item = new ShellItem(dlg.NewItemRegPath);
else item = new StoreShellItem(dlg.NewItemRegPath, true, false);
this.InsertItem(item, i + 1);
break;
}
}
}
}
private void AddNewShellExItem(string scenePath)
{
bool isDragDrop = Scene == Scenes.DragDrop;
using(InputDialog dlg1 = new InputDialog { Title = AppString.Dialog.InputGuid })
{
if(GuidEx.TryParse(Clipboard.GetText(), out Guid guid)) dlg1.Text = guid.ToString();
if(dlg1.ShowDialog() != DialogResult.OK) return;
if(GuidEx.TryParse(dlg1.Text, out guid))
{
if(isDragDrop)
{
using(SelectDialog dlg2 = new SelectDialog())
{
dlg2.Title = AppString.Dialog.SelectGroup;
dlg2.Items = new[] { AppString.SideBar.Folder, AppString.SideBar.Directory,
AppString.SideBar.Drive, AppString.SideBar.AllObjects };
dlg2.Selected = dlg2.Items[0];
if(dlg2.ShowDialog() != DialogResult.OK) return;
switch(dlg2.SelectedIndex)
{
case 0:
scenePath = MENUPATH_FOLDER; break;
case 1:
scenePath = MENUPATH_DIRECTORY; break;
case 2:
scenePath = MENUPATH_DRIVE; break;
case 3:
scenePath = MENUPATH_ALLOBJECTS; break;
}
}
}
string shellExPath = GetShellExPath(scenePath);
if(ShellExItem.GetPathAndGuids(shellExPath, isDragDrop).Values.Contains(guid))
{
MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded);
}
else
{
string part = isDragDrop ? ShellExItem.DdhParts[0] : ShellExItem.CmhParts[0];
string regPath = $@"{shellExPath}\{part}\{guid:B}";
Registry.SetValue(regPath, "", guid.ToString("B"));
ShellExItem item = new ShellExItem(guid, regPath);
for(int i = 0; i < this.Controls.Count; i++)
{
if(isDragDrop)
{
if(this.Controls[i] is GroupPathItem groupItem)
{
if(groupItem.TargetPath.Equals(shellExPath, StringComparison.OrdinalIgnoreCase))
{
this.InsertItem(item, i + 1);
item.FoldGroupItem = groupItem;
item.Visible = !groupItem.IsFold;
break;
}
}
}
else
{
if(this.Controls[i] is NewItem)
{
this.InsertItem(item, i + 1);
break;
}
}
}
}
}
else
{
MessageBoxEx.Show(AppString.MessageBox.MalformedGuid);
}
}
}
///<summary>“其他规则”-“公共引用”</summary>
private void LoadStoreItems()
{
using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath))
{
Array.ForEach(Array.FindAll(shellKey.GetSubKeyNames(), itemName =>
!ShellItem.SysStoreItemNames.Contains(itemName, StringComparer.OrdinalIgnoreCase)), itemName =>
this.AddItem(new StoreShellItem($@"{ShellItem.CommandStorePath}\{itemName}", true, false)));
}
}
private void AddUwpModeItem()
{
XmlDocument doc = AppDic.ReadXml(AppConfig.WebUwpModeItemsDic,
AppConfig.UserUwpModeItemsDic, Properties.Resources.UwpModeItemsDic);
List<Guid> guids = new List<Guid>();
foreach(XmlElement sceneXE in doc.DocumentElement.ChildNodes)
{
if(sceneXE.Name == Scene.ToString())
{
foreach(XmlElement itemXE in sceneXE.ChildNodes)
{
if(GuidEx.TryParse(itemXE.GetAttribute("Guid"), out Guid guid))
{
if(guids.Contains(guid)) continue;
string uwpName = GuidInfo.GetUwpName(guid);
if(!string.IsNullOrEmpty(uwpName))
{
this.AddItem(new UwpModeItem(uwpName, guid));
guids.Add(guid);
}
}
}
}
}
}
sealed class SelectItem : MyListItem
{
static string selected;
public static string Selected
{
get => selected;
set
{
selected = value;
SelectedChanged?.Invoke(null, null);
}
}
public static event EventHandler SelectedChanged;
readonly PictureButton BtnSelect = new PictureButton(AppImage.Select);
public SelectItem(Scenes scene)
{
this.SetNoClickEvent();
this.Image = AppImage.Custom;
this.Text = this.GetText(scene);
this.AddCtr(BtnSelect);
BtnSelect.MouseDown += (sender, e) => Select(scene);
}
private string GetText(Scenes scene)
{
switch(scene)
{
case Scenes.CustomExtension:
if(CurrentExtension == null)
{
return AppString.Dialog.SelectExtension;
}
else
{
return AppString.Other.CurrentExtension.Replace("%s", CurrentExtension);
}
case Scenes.PerceivedType:
if(CurrentPerceivedType == null)
{
return AppString.Dialog.SelectPerceivedType;
}
else
{
return AppString.Other.CurrentPerceivedType.Replace("%s", GetPerceivedTypeName());
}
case Scenes.DirectoryType:
if(CurrentDirectoryType == null)
{
return AppString.Dialog.SelectDirectoryType;
}
else
{
return AppString.Other.CurrentDirectoryType.Replace("%s", GetDirectoryTypeName());
}
default:
return null;
}
}
private void Select(Scenes scene)
{
SelectDialog dlg;
switch(scene)
{
case Scenes.CustomExtension:
dlg = new FileExtensionDialog
{
Selected = CurrentExtension?.Substring(1)
};
break;
case Scenes.PerceivedType:
dlg = new SelectDialog
{
Items = PerceivedTypeNames,
Title = AppString.Dialog.SelectPerceivedType,
Selected = GetPerceivedTypeName() ?? PerceivedTypeNames[0]
};
break;
case Scenes.DirectoryType:
dlg = new SelectDialog
{
Items = DirectoryTypeNames,
Title = AppString.Dialog.SelectDirectoryType,
Selected = GetDirectoryTypeName() ?? DirectoryTypeNames[0]
};
break;
default: return;
}
if(dlg.ShowDialog() != DialogResult.OK) return;
switch(scene)
{
case Scenes.CustomExtension:
Selected = CurrentExtension = dlg.Selected;
break;
case Scenes.PerceivedType:
Selected = CurrentPerceivedType = PerceivedTypes[dlg.SelectedIndex];
break;
case Scenes.DirectoryType:
Selected = CurrentDirectoryType = DirectoryTypes[dlg.SelectedIndex];
break;
}
}
}
}
}