Repository: BluePointLilac/ContextMenuManager
Branch: master
Commit: 55507155dd8e
Files: 146
Total size: 916.3 KB
Directory structure:
gitextract_o6208a9c/
├── .gitattributes
├── .gitignore
├── ContextMenuManager/
│ ├── App.config
│ ├── BluePointLilac.Controls/
│ │ ├── DownloadDialog.cs
│ │ ├── IconDialog.cs
│ │ ├── InputDialog.cs
│ │ ├── MyCheckBox.cs
│ │ ├── MyListBox.cs
│ │ ├── MyMainForm.cs
│ │ ├── MySideBar.cs
│ │ ├── MyStatusBar.cs
│ │ ├── MyToolBar.cs
│ │ ├── PictureButton.cs
│ │ ├── ReadOnlyTextBox.cs
│ │ ├── ResizeLimitedForm.cs
│ │ ├── SelectDialog.cs
│ │ └── UAWebClient.cs
│ ├── BluePointLilac.Methods/
│ │ ├── ComboBoxExtension.cs
│ │ ├── ControlExtension.cs
│ │ ├── DirectoryEx.cs
│ │ ├── ElevatedFileDroper.cs
│ │ ├── EncodingType.cs
│ │ ├── ExternalProgram.cs
│ │ ├── FileExtension.cs
│ │ ├── FormExtension.cs
│ │ ├── GuidEx.cs
│ │ ├── HighDpi.cs
│ │ ├── ImageExtension.cs
│ │ ├── IniReader.cs
│ │ ├── IniWriter.cs
│ │ ├── MessageBoxEx.cs
│ │ ├── RegTrustedInstaller.cs
│ │ ├── RegistryEx.cs
│ │ ├── ResourceIcon.cs
│ │ ├── ResourceString.cs
│ │ ├── RichTextBoxExtension.cs
│ │ ├── ShellLink.cs
│ │ ├── SingleInstance.cs
│ │ ├── StringExtension.cs
│ │ ├── TextBoxExtension.cs
│ │ ├── ToolTipBox.cs
│ │ └── WinOsVersion.cs
│ ├── ContextMenuManager.csproj
│ ├── Controls/
│ │ ├── AppSettingBox.cs
│ │ ├── DetailedEditDialog.cs
│ │ ├── DetailedEditList.cs
│ │ ├── DictionariesBox.cs
│ │ ├── DonateBox.cs
│ │ ├── EnhanceMenusDialog.cs
│ │ ├── EnhanceMenusItem.cs
│ │ ├── EnhanceMenusList.cs
│ │ ├── ExplorerRestarter.cs
│ │ ├── FileExtensionDialog.cs
│ │ ├── FoldSubItem.cs
│ │ ├── GuidBlockedItem.cs
│ │ ├── GuidBlockedList.cs
│ │ ├── IEItem.cs
│ │ ├── IEList.cs
│ │ ├── Interfaces/
│ │ │ ├── IBtnDeleteItem.cs
│ │ │ ├── IBtnMoveUpDownItem.cs
│ │ │ ├── IBtnShowMenuItem.cs
│ │ │ ├── IChkVisibleItem.cs
│ │ │ ├── IProtectOpenItem.cs
│ │ │ ├── ITsiAdministratorItem.cs
│ │ │ ├── ITsiCommandItem.cs
│ │ │ ├── ITsiDeleteItem.cs
│ │ │ ├── ITsiFilePathItem.cs
│ │ │ ├── ITsiGuidItem.cs
│ │ │ ├── ITsiIconItem.cs
│ │ │ ├── ITsiRegExportItem.cs
│ │ │ ├── ITsiRegPathItem.cs
│ │ │ ├── ITsiShortcutCommandItem.cs
│ │ │ ├── ITsiTextItem.cs
│ │ │ └── ITsiWebSearchItem.cs
│ │ ├── LanguagesBox.cs
│ │ ├── NewIEDialog.cs
│ │ ├── NewItem.cs
│ │ ├── NewItemForm.cs
│ │ ├── NewLnkFileDialog.cs
│ │ ├── NewOpenWithDialog.cs
│ │ ├── NewShellDialog.cs
│ │ ├── OpenWithItem.cs
│ │ ├── OpenWithList.cs
│ │ ├── RuleItem.cs
│ │ ├── SendToItem.cs
│ │ ├── SendToList.cs
│ │ ├── ShellExItem.cs
│ │ ├── ShellExecuteDialog.cs
│ │ ├── ShellItem.cs
│ │ ├── ShellList.cs
│ │ ├── ShellNewItem.cs
│ │ ├── ShellNewList.cs
│ │ ├── ShellStoreDialog.cs
│ │ ├── ShellSubMenuDialog.cs
│ │ ├── SubItemsForm.cs
│ │ ├── SwitchDicList.cs
│ │ ├── UwpModeItem.cs
│ │ ├── WinXGroupItem.cs
│ │ ├── WinXItem.cs
│ │ └── WinXList.cs
│ ├── MainForm.cs
│ ├── Methods/
│ │ ├── AppConfig.cs
│ │ ├── AppImage.cs
│ │ ├── AppMessageBox.cs
│ │ ├── AppString.cs
│ │ ├── DesktopIni.cs
│ │ ├── GuidInfo.cs
│ │ ├── ObjectPath.cs
│ │ ├── Updater.cs
│ │ ├── UwpHelper.cs
│ │ ├── WinXHasher.cs
│ │ └── XmlDicHelper.cs
│ ├── Program.cs
│ └── Properties/
│ ├── App.manifest
│ ├── AssemblyInfo.cs
│ ├── Resources/
│ │ ├── ShellNew/
│ │ │ ├── 0.c
│ │ │ ├── 0.html
│ │ │ ├── 0.reg
│ │ │ ├── 0.rtf
│ │ │ ├── 0.xlsx
│ │ │ └── 0.xml
│ │ └── Texts/
│ │ ├── AppLanguageDic.ini
│ │ ├── DetailedEditDic.xml
│ │ ├── EnhanceMenusDic.xml
│ │ ├── GuidInfosDic.ini
│ │ └── UwpModeItemsDic.xml
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── ContextMenuManager.sln
├── Donate.md
├── LICENSE
├── Logo/
│ └── Logo.psd
├── README-en.md
├── README.md
├── Update.ini
└── languages/
├── ar-DZ عربية
├── de-DE.ini
├── en-US.ini
├── ja-JP.ini
├── ko-KR.ini
├── pt-BR.ini
├── ru-RU.ini
├── zh-CN.ini
└── zh-TW.ini
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
================================================
FILE: ContextMenuManager/App.config
================================================
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/DownloadDialog.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
sealed class DownloadDialog : CommonDialog
{
public string Text { get; set; }
public string Url { get; set; }
public string FilePath { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(Process process = Process.GetCurrentProcess())
using(DownloadForm frm = new DownloadForm())
{
frm.Url = this.Url;
frm.Text = this.Text;
frm.FilePath = this.FilePath;
return frm.ShowDialog() == DialogResult.OK;
}
}
sealed class DownloadForm : Form
{
public DownloadForm()
{
this.SuspendLayout();
this.Font = SystemFonts.MessageBoxFont;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MinimizeBox = this.MaximizeBox = this.ShowInTaskbar = false;
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
this.Controls.AddRange(new Control[] { pgbDownload, btnCancel });
this.Load += (sender, e) => DownloadFile(Url, FilePath);
this.InitializeComponents();
this.ResumeLayout();
}
readonly ProgressBar pgbDownload = new ProgressBar
{
Width = 200.DpiZoom(),
Maximum = 100
};
readonly Button btnCancel = new Button
{
DialogResult = DialogResult.Cancel,
Text = ResourceString.Cancel,
AutoSize = true
};
public string Url { get; set; }
public string FilePath { get; set; }
private void InitializeComponents()
{
int a = 20.DpiZoom();
pgbDownload.Left = pgbDownload.Top = btnCancel.Top = a;
pgbDownload.Height = btnCancel.Height;
btnCancel.Left = pgbDownload.Right + a;
this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a);
}
private void DownloadFile(string url, string filePath)
{
try
{
using(UAWebClient client = new UAWebClient())
{
client.DownloadProgressChanged += (sender, e) =>
{
int value = e.ProgressPercentage;
this.Text = $"Downloading: {value}%";
pgbDownload.Value = value;
if(this.DialogResult == DialogResult.Cancel)
{
client.CancelAsync();
File.Delete(FilePath);
}
};
client.DownloadFileCompleted += (sender, e) =>
{
this.DialogResult = DialogResult.OK;
};
client.DownloadFileAsync(new Uri(url), filePath);
}
}
catch(Exception e)
{
MessageBox.Show(e.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.Cancel;
}
}
protected override void OnLoad(EventArgs e)
{
if(this.Owner == null && Form.ActiveForm != this) this.Owner = Form.ActiveForm;
if(this.Owner == null) this.StartPosition = FormStartPosition.CenterScreen;
else
{
this.TopMost = this.Owner.TopMost;
this.StartPosition = FormStartPosition.CenterParent;
}
base.OnLoad(e);
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/IconDialog.cs
================================================
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class IconDialog : CommonDialog
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "#62", SetLastError = true)]
private static extern bool PickIconDlg(IntPtr hWnd, StringBuilder pszFileName, int cchFileNameMax, ref int pnIconIndex);
private const int MAXLENGTH = 260;
private int iconIndex;
public int IconIndex { get => iconIndex; set => iconIndex = value; }
public string IconPath { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
StringBuilder sb = new StringBuilder(IconPath, MAXLENGTH);
bool flag = PickIconDlg(hwndOwner, sb, MAXLENGTH, ref iconIndex);
IconPath = flag ? sb.ToString() : null;
return flag;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/InputDialog.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class InputDialog : CommonDialog
{
/// 输入对话框标题
public string Title { get; set; } = Application.ProductName;
/// 输入对话框文本框文本
public string Text { get; set; }
public Size Size { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(InputBox frm = new InputBox())
{
frm.Text = Title;
frm.InputedText = this.Text;
frm.Size = this.Size;
Form owner = (Form)Control.FromHandle(hwndOwner);
if(owner != null) frm.TopMost = owner.TopMost;
bool flag = frm.ShowDialog() == DialogResult.OK;
this.Text = flag ? frm.InputedText : null;
return flag;
}
}
sealed class InputBox : Form
{
public InputBox()
{
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
this.Font = SystemFonts.MessageBoxFont;
this.SizeGripStyle = SizeGripStyle.Hide;
this.StartPosition = FormStartPosition.CenterParent;
this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false;
this.Controls.AddRange(new Control[] { txtInput, btnOK, btnCancel });
txtInput.Font = new Font(txtInput.Font.FontFamily, txtInput.Font.Size + 2F);
txtInput.CanResizeFont();
InitializeComponents();
}
public string InputedText
{
get => txtInput.Text;
set => txtInput.Text = value;
}
readonly TextBox txtInput = new TextBox
{
Font = SystemFonts.MenuFont,
ScrollBars = ScrollBars.Vertical,
Multiline = true
};
readonly Button btnOK = new Button
{
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.OK,
Text = ResourceString.OK,
AutoSize = true
};
readonly Button btnCancel = new Button
{
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.Cancel,
Text = ResourceString.Cancel,
AutoSize = true
};
private void InitializeComponents()
{
this.SuspendLayout();
int a = 20.DpiZoom();
txtInput.Location = new Point(a, a);
txtInput.Size = new Size(340, 24).DpiZoom();
this.ClientSize = new Size(txtInput.Width + a * 2, txtInput.Height + btnOK.Height + a * 3);
btnCancel.Top = btnOK.Top = txtInput.Bottom + a;
btnCancel.Left = txtInput.Right - btnCancel.Width;
btnOK.Left = btnCancel.Left - btnOK.Width - a;
this.ResumeLayout();
this.MinimumSize = this.Size;
this.Resize += (sender, e) =>
{
txtInput.Width = this.ClientSize.Width - 2 * a;
txtInput.Height = btnCancel.Top - 2 * a;
};
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs
================================================
using BluePointLilac.Methods;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
[DefaultProperty("Checked")]
public class MyCheckBox : PictureBox
{
public MyCheckBox()
{
this.Image = TurnOff;
this.Cursor = Cursors.Hand;
this.SizeMode = PictureBoxSizeMode.AutoSize;
}
private bool? _Checked = null;
public bool Checked
{
get => _Checked == true;
set
{
if(_Checked == value) return;
this.Image = SwitchImage(value);
if(_Checked == null)
{
_Checked = value;
return;
}
if(PreCheckChanging != null && !PreCheckChanging.Invoke())
{
this.Image = SwitchImage(!value);
return;
}
else CheckChanging?.Invoke();
if(PreCheckChanged != null && !PreCheckChanged.Invoke())
{
this.Image = SwitchImage(!value);
return;
}
else
{
_Checked = value;
CheckChanged?.Invoke();
}
}
}
public Func PreCheckChanging;
public Func PreCheckChanged;
public Action CheckChanging;
public Action CheckChanged;
public Image TurnOnImage { get; set; } = TurnOn;
public Image TurnOffImage { get; set; } = TurnOff;
private Image SwitchImage(bool value)
{
return value ? TurnOnImage : TurnOffImage;
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if(e.Button == MouseButtons.Left) this.Checked = !this.Checked;
}
private static readonly Image TurnOn = DrawImage(true);
private static readonly Image TurnOff = DrawImage(false);
private static Image DrawImage(bool value)
{
int w = 80.DpiZoom();
int r1 = 16.DpiZoom();
float r2 = 13F.DpiZoom();
int d1 = r1 * 2;
float d2 = r2 * 2;
float a = r1 - r2;
Bitmap bitmap = new Bitmap(w, d1);
using(Graphics g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.CompositingQuality = CompositingQuality.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
using(GraphicsPath path = new GraphicsPath())
{
path.AddArc(new RectangleF(0, 0, d1, d1), 90, 180);
path.AddLine(new PointF(r1, 0), new PointF(w - r1, 0));
path.AddArc(new RectangleF(w - d1, 0, d1, d1), -90, 180);
path.AddLine(new PointF(w - r1, d1), new PointF(r1, d1));
Color color = value ? Color.FromArgb(0, 138, 217) : Color.FromArgb(130, 136, 144);
using(Brush brush = new SolidBrush(color))
{
g.FillPath(brush, path);
}
}
using(GraphicsPath path = new GraphicsPath())
{
path.AddArc(new RectangleF(a, a, d2, d2), 90, 180);
path.AddLine(new PointF(r1, a), new PointF(w - r1, a));
path.AddArc(new RectangleF(w - d2 - a, a, d2, d2), -90, 180);
path.AddLine(new PointF(w - r1, d2 + a), new PointF(r1, d2 + a));
Color color = value ? Color.FromArgb(0, 162, 255) : Color.FromArgb(153, 160, 169);
using(Brush brush = new SolidBrush(color))
{
g.FillPath(brush, path);
}
}
using(GraphicsPath path = new GraphicsPath())
{
path.AddEllipse(new RectangleF(value ? (w - d2 - a) : a, a, d2, d2));
g.FillPath(Brushes.White, path);
}
}
return bitmap;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MyListBox.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public class MyListBox : Panel
{
public MyListBox()
{
this.AutoScroll = true;
this.BackColor = Color.FromArgb(250, 250, 250);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
//使滚动幅度与MyListItem的高度相配合,防止滚动过快导致来不及重绘界面变花
base.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X, e.Y, Math.Sign(e.Delta) * 50.DpiZoom()));
}
}
public class MyList : FlowLayoutPanel
{
public MyListBox Owner
{
get => (MyListBox)this.Parent;
set => this.Parent = value;
}
public MyList(MyListBox owner) : this()
{
this.Owner = owner;
}
public MyList()
{
this.AutoSize = true;
this.WrapContents = true;
this.Dock = DockStyle.Top;
this.DoubleBuffered = true;
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
}
private MyListItem hoveredItem;
public MyListItem HoveredItem
{
get => hoveredItem;
set
{
if(hoveredItem == value) return;
if(hoveredItem != null)
{
hoveredItem.ForeColor = Color.FromArgb(90, 90, 90);
//hoveredItem.BackColor = Color.FromArgb(250, 250, 250);
//hoveredItem.Font = new Font(hoveredItem.Font, FontStyle.Regular);
}
hoveredItem = value;
if(hoveredItem != null)
{
value.ForeColor = Color.FromArgb(0, 138, 217);
//value.BackColor = Color.FromArgb(200, 230, 250);
//value.Font = new Font(hoveredItem.Font, FontStyle.Bold);
value.Focus();
}
HoveredItemChanged?.Invoke(this, null);
}
}
public event EventHandler HoveredItemChanged;
public void AddItem(MyListItem item)
{
this.SuspendLayout();
item.Parent = this;
item.MouseEnter += (sender, e) => HoveredItem = item;
this.MouseWheel += (sender, e) => item.ContextMenuStrip?.Close();
void ResizeItem() => item.Width = Owner.Width - item.Margin.Horizontal;
Owner.Resize += (sender, e) => ResizeItem();
ResizeItem();
this.ResumeLayout();
}
public void AddItems(MyListItem[] items)
{
Array.ForEach(items, item => AddItem(item));
}
public void AddItems(List items)
{
items.ForEach(item => AddItem(item));
}
public void SetItemIndex(MyListItem item, int newIndex)
{
this.Controls.SetChildIndex(item, newIndex);
}
public int GetItemIndex(MyListItem item)
{
return Controls.GetChildIndex(item);
}
public void InsertItem(MyListItem item, int index)
{
if(item == null) return;
this.AddItem(item);
this.SetItemIndex(item, index);
}
public virtual void ClearItems()
{
if(this.Controls.Count == 0) return;
this.SuspendLayout();
for(int i = this.Controls.Count - 1; i >= 0; i--)
{
Control ctr = this.Controls[i];
this.Controls.Remove(ctr);
ctr.Dispose();
}
this.ResumeLayout();
}
public void SortItemByText()
{
List items = new List();
foreach(MyListItem item in this.Controls) items.Add(item);
this.Controls.Clear();
items.Sort(new TextComparer());
items.ForEach(item => this.AddItem(item));
}
public class TextComparer : IComparer
{
public int Compare(MyListItem x, MyListItem y)
{
if(x.Equals(y)) return 0;
string[] strs = { x.Text, y.Text };
Array.Sort(strs);
if(strs[0] == x.Text) return -1;
else return 1;
}
}
}
public class MyListItem : Panel
{
public MyListItem()
{
this.SuspendLayout();
this.HasImage = true;
this.DoubleBuffered = true;
this.Height = 50.DpiZoom();
this.Margin = new Padding(0);
this.Font = SystemFonts.IconTitleFont;
this.ForeColor = Color.FromArgb(80, 80, 80);
this.BackColor = Color.FromArgb(250, 250, 250);
this.Controls.AddRange(new Control[] { lblSeparator, flpControls, lblText, picImage });
this.Resize += (Sender, e) => pnlScrollbar.Height = this.ClientSize.Height;
flpControls.MouseClick += (sender, e) => this.OnMouseClick(e);
flpControls.MouseEnter += (sender, e) => this.OnMouseEnter(e);
flpControls.MouseDown += (sender, e) => this.OnMouseDown(e);
lblSeparator.SetEnabled(false);
lblText.SetEnabled(false);
CenterControl(lblText);
CenterControl(picImage);
AddCtr(pnlScrollbar, 0);
this.ResumeLayout();
}
public Image Image
{
get => picImage.Image;
set => picImage.Image = value;
}
public new string Text
{
get => lblText.Text;
set => lblText.Text = value;
}
public new Font Font
{
get => lblText.Font;
set => lblText.Font = value;
}
public new Color ForeColor
{
get => lblText.ForeColor;
set => lblText.ForeColor = value;
}
private bool hasImage;
public bool HasImage
{
get => hasImage;
set
{
hasImage = value;
picImage.Visible = value;
lblText.Left = (value ? 60 : 20).DpiZoom();
}
}
private readonly Label lblText = new Label
{
AutoSize = true,
Name = "Text"
};
private readonly PictureBox picImage = new PictureBox
{
SizeMode = PictureBoxSizeMode.AutoSize,
Left = 20.DpiZoom(),
Enabled = false,
Name = "Image"
};
private readonly FlowLayoutPanel flpControls = new FlowLayoutPanel
{
AutoSizeMode = AutoSizeMode.GrowAndShrink,
FlowDirection = FlowDirection.RightToLeft,
Anchor = AnchorStyles.Right,
AutoSize = true,
Name = "Controls"
};
private readonly Label lblSeparator = new Label
{
BackColor = Color.FromArgb(220, 220, 220),
Dock = DockStyle.Bottom,
Name = "Separator",
Height = 1
};//分割线
private readonly Panel pnlScrollbar = new Panel
{
Width = SystemInformation.VerticalScrollBarWidth,
Enabled = false
};//预留滚动条宽度
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e); OnMouseEnter(null);
}
private void CenterControl(Control ctr)
{
void reSize()
{
if(ctr.Parent == null) return;
int top = (this.ClientSize.Height - ctr.Height) / 2;
ctr.Top = top;
if(ctr.Parent == flpControls)
{
ctr.Margin = new Padding(0, top, ctr.Margin.Right, top);
}
}
ctr.Parent.Resize += (sender, e) => reSize();
ctr.Resize += (sender, e) => reSize();
reSize();
}
public void AddCtr(Control ctr)
{
AddCtr(ctr, 20.DpiZoom());
}
public void AddCtr(Control ctr, int space)
{
this.SuspendLayout();
ctr.Parent = flpControls;
ctr.Margin = new Padding(0, 0, space, 0);
ctr.MouseEnter += (sender, e) => this.OnMouseEnter(e);
ctr.MouseDown += (sender, e) => this.OnMouseEnter(e);
CenterControl(ctr);
this.ResumeLayout();
}
public void AddCtrs(Control[] ctrs)
{
Array.ForEach(ctrs, ctr => AddCtr(ctr));
}
public void RemoveCtrAt(int index)
{
if(flpControls.Controls.Count > index) flpControls.Controls.RemoveAt(index + 1);
}
public int GetCtrIndex(Control ctr)
{
return flpControls.Controls.GetChildIndex(ctr, true) - 1;
}
public void SetCtrIndex(Control ctr, int newIndex)
{
flpControls.Controls.SetChildIndex(ctr, newIndex + 1);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public class MyMainForm : Form
{
public MyMainForm()
{
this.SuspendLayout();
this.Text = Application.ProductName;
this.ForeColor = Color.FromArgb(80, 80, 80);
this.BackColor = Color.FromArgb(250, 250, 250);
this.StartPosition = FormStartPosition.CenterScreen;
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
this.Controls.AddRange(new Control[] { MainBody, SideBar, StatusBar, ToolBar });
SideBar.Resize += (sender, e) => this.OnResize(null);
this.ClientSize = new Size(850, 610).DpiZoom();
this.MinimumSize = this.Size;
MainBody.Dock = DockStyle.Left;
StatusBar.CanMoveForm();
ToolBar.CanMoveForm();
this.ResumeLayout();
}
public readonly MyToolBar ToolBar = new MyToolBar();
public readonly MySideBar SideBar = new MySideBar();
public readonly MyStatusBar StatusBar = new MyStatusBar();
public readonly MyListBox MainBody = new MyListBox();
/// 窗体移动时是否临时挂起MainBody
public bool SuspendMainBodyWhenMove { get; set; } = false;
/// 窗体调整大小时是否临时挂起MainBody
public bool SuspendMainBodyWhenResize { get; set; } = true;
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
MainBody.Width = this.ClientSize.Width - SideBar.Width;
}
protected override void WndProc(ref Message m)
{
const int WM_NCLBUTTONDBLCLK = 0x00A3;
const int WM_SYSCOMMAND = 0x0112;
const int SC_MAXIMIZE = 0xF030;
const int SC_MINIMIZE = 0xF020;
const int SC_RESTORE = 0xF120;
const int SC_MOVE = 0xF012;
const int SC_SIZE = 0xF000;
const int HT_CAPTION = 0x2;
bool suspend = false;//临时挂起MainBody
switch(m.Msg)
{
case WM_SYSCOMMAND:
switch(m.WParam.ToInt32())
{
//解决控件过多移动窗体时延迟问题
case SC_MOVE:
//解决控件过多调整窗体大小时延迟问题
case SC_SIZE:
suspend = this.SuspendMainBodyWhenMove; break;
//解决控件过多最大化、最小化、还原重绘卡顿问题
case SC_RESTORE:
case SC_MINIMIZE:
case SC_MAXIMIZE:
suspend = this.SuspendMainBodyWhenResize; break;
}
break;
case WM_NCLBUTTONDBLCLK:
switch(m.WParam.ToInt32())
{
//双击标题栏最大化和还原窗口
case HT_CAPTION:
suspend = this.SuspendMainBodyWhenResize; break;
}
break;
}
if(suspend)
{
this.SuspendLayout();
MainBody.SuspendLayout();
this.Controls.Remove(MainBody);
base.WndProc(ref m);
this.Controls.Add(MainBody);
MainBody.BringToFront();
MainBody.ResumeLayout();
this.ResumeLayout();
}
else base.WndProc(ref m);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MySideBar.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class MySideBar : Panel
{
public MySideBar()
{
this.Dock = DockStyle.Left;
this.ItemHeight = 30.DpiZoom();
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
this.ForeColor = Color.FromArgb(80, 80, 80);
this.BackColor = Color.FromArgb(245, 245, 245);
this.BackgroundImageLayout = ImageLayout.None;
this.Controls.AddRange(new Control[] { LblSeparator, PnlSelected, PnlHovered });
PnlHovered.Paint += PaintItem;
PnlSelected.Paint += PaintItem;
this.SelectedIndex = -1;
}
private string[] itemNames;
public string[] ItemNames
{
get => itemNames;
set
{
itemNames = value;
if(value != null && !IsFixedWidth)
{
int maxWidth = 0;
Array.ForEach(value, str => maxWidth = Math.Max(maxWidth, GetItemWidth(str)));
this.Width = maxWidth + 2 * HorizontalSpace;
}
PnlHovered.Width = PnlSelected.Width = this.Width;
PaintItems();
SelectedIndex = -1;
}
}
private int itemHeight;
public int ItemHeight
{
get => itemHeight;
set => PnlHovered.Height = PnlSelected.Height = itemHeight = value;
}//项上下边缘距离
public int TopSpace { get; set; } = 2.DpiZoom();//第一项顶部与上边缘的距离
public int HorizontalSpace { get; set; } = 20.DpiZoom();//项文字与项左右边缘距离
private float VerticalSpace => (itemHeight - TextHeight) * 0.5F;//项文字与项上下边缘距离
private int TextHeight => TextRenderer.MeasureText(" ", Font).Height;//项文字高度
public bool IsFixedWidth { get; set; } = true;//是否固定宽度
public Color SeparatorColor
{
get => LblSeparator.BackColor;
set => LblSeparator.BackColor = value;
}//分隔线颜色
public Color SelectedBackColor
{
get => PnlSelected.BackColor;
set => PnlSelected.BackColor = value;
}
public Color HoveredBackColor
{
get => PnlHovered.BackColor;
set => PnlHovered.BackColor = value;
}
public Color SelectedForeColor
{
get => PnlSelected.ForeColor;
set => PnlSelected.ForeColor = value;
}
public Color HoveredForeColor
{
get => PnlHovered.ForeColor;
set => PnlHovered.ForeColor = value;
}
readonly Panel PnlSelected = new Panel
{
BackColor = Color.FromArgb(40, 140, 210),
ForeColor = Color.White,
Enabled = false
};
readonly Panel PnlHovered = new Panel
{
BackColor = Color.FromArgb(80, 180, 250),
ForeColor = Color.White,
Enabled = false
};
readonly Label LblSeparator = new Label
{
BackColor = Color.FromArgb(220, 220, 220),
Dock = DockStyle.Right,
Width = 1,
};
/// 获取项目宽度
public int GetItemWidth(string str)
{
return TextRenderer.MeasureText(str, Font).Width + 2 * HorizontalSpace;
}
/// 绘制所有项目作为底图
private void PaintItems()
{
this.BackgroundImage = new Bitmap(Width, ItemHeight * ItemNames.Length);
using(Graphics g = Graphics.FromImage(BackgroundImage))
{
g.Clear(BackColor);
if(itemNames == null) return;
for(int i = 0; i < itemNames.Length; i++)
{
if(itemNames[i] != null)
{
g.DrawString(itemNames[i], Font, new SolidBrush(ForeColor),
new PointF(HorizontalSpace, TopSpace + i * ItemHeight + VerticalSpace));
}
else
{
g.DrawLine(new Pen(SeparatorColor),
new PointF(HorizontalSpace, TopSpace + (i + 0.5F) * ItemHeight),
new PointF(Width - HorizontalSpace, TopSpace + (i + 0.5F) * ItemHeight)
);
}
}
}
}
/// 刷新选中的项目
private void RefreshItem(Panel panel, int index)
{
panel.CreateGraphics().Clear(panel.BackColor);
panel.Top = index < 0 ? -ItemHeight : (TopSpace + index * ItemHeight);
panel.Text = index < 0 ? null : ItemNames[index];
panel.Refresh();
}
/// 绘制选中的项目
private void PaintItem(object sender, PaintEventArgs e)
{
Control ctr = (Control)sender;
e.Graphics.DrawString(ctr.Text, Font,
new SolidBrush(ctr.ForeColor),
new PointF(HorizontalSpace, VerticalSpace));
}
/// 显示选中的项目
private void ShowItem(Panel panel, MouseEventArgs e)
{
if(itemNames == null) return;
int index = (e.Y - TopSpace) / ItemHeight;
if(index >= itemNames.Length || index < 0 || string.IsNullOrEmpty(itemNames[index]) || index == SelectedIndex)
{
this.Cursor = Cursors.Default;
HoveredIndex = SelectedIndex;
}
else
{
this.Cursor = Cursors.Hand;
if(panel == PnlSelected) SelectedIndex = index;
else HoveredIndex = index;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
ShowItem(PnlHovered, e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if(e.Button == MouseButtons.Left) ShowItem(PnlSelected, e);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
HoveredIndex = SelectedIndex;
}
public event EventHandler SelectIndexChanged;
public event EventHandler HoverIndexChanged;
private int selectIndex;
public int SelectedIndex
{
get => selectIndex;
set
{
if(selectIndex == value) return;
HoveredIndex = value;
RefreshItem(PnlSelected, value);
selectIndex = value;
SelectIndexChanged?.Invoke(this, null);
}
}
private int hoverIndex;
public int HoveredIndex
{
get => hoverIndex;
set
{
if(hoverIndex == value) return;
RefreshItem(PnlHovered, value);
hoverIndex = value;
HoverIndexChanged?.Invoke(this, null);
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs
================================================
using BluePointLilac.Methods;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class MyStatusBar : Panel
{
public static readonly string DefaultText = $"Ver: {Application.ProductVersion} {Application.CompanyName}";
public MyStatusBar()
{
this.Text = DefaultText;
this.Height = 30.DpiZoom();
this.Dock = DockStyle.Bottom;
this.Font = SystemFonts.StatusFont;
this.BackColor = Color.FromArgb(70, 130, 200);
this.ForeColor = Color.White;
}
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public override string Text { get => base.Text; set => base.Text = value; }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
string txt = this.Text;
int left = this.Height / 3;
for(int i = this.Text.Length - 1; i >= 0; i--)
{
Size size = TextRenderer.MeasureText(txt, this.Font);
if(size.Width < ClientSize.Width - 2 * left)
{
using(Brush brush = new SolidBrush(this.ForeColor))
{
int top = (this.Height - size.Height) / 2;
e.Graphics.Clear(this.BackColor);
e.Graphics.DrawString(txt, this.Font, brush, left, top);
break;
}
}
txt = this.Text.Substring(0, i) + "...";
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e); this.Refresh();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e); this.Refresh();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e); this.Refresh();
}
protected override void OnForeColorChanged(EventArgs e)
{
base.OnForeColorChanged(e); this.Refresh();
}
protected override void OnBackColorChanged(EventArgs e)
{
base.OnBackColorChanged(e); this.Refresh();
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class MyToolBar : FlowLayoutPanel
{
public MyToolBar()
{
this.Height = 80.DpiZoom();
this.Dock = DockStyle.Top;
this.DoubleBuffered = true;
this.BackColor = Color.FromArgb(85, 145, 215);
}
private MyToolBarButton selectedButton;
public MyToolBarButton SelectedButton
{
get => selectedButton;
set
{
if(selectedButton == value) return;
if(selectedButton != null)
{
selectedButton.Opacity = 0;
selectedButton.Cursor = Cursors.Hand;
}
selectedButton = value;
if(selectedButton != null)
{
selectedButton.Opacity = 0.4F;
selectedButton.Cursor = Cursors.Default;
}
SelectedButtonChanged?.Invoke(this, null);
}
}
public event EventHandler SelectedButtonChanged;
public int SelectedIndex
{
get
{
if(SelectedButton == null) return -1;
else return Controls.GetChildIndex(SelectedButton);
}
set
{
if(value < 0 || value >= this.Controls.Count) SelectedButton = null;
else SelectedButton = (MyToolBarButton)Controls[value];
}
}
public void AddButton(MyToolBarButton button)
{
this.SuspendLayout();
button.Parent = this;
button.Margin = new Padding(12, 4, 0, 0).DpiZoom();
button.MouseDown += (sender, e) =>
{
if(e.Button == MouseButtons.Left && button.CanBeSelected) SelectedButton = button;
};
button.MouseEnter += (sender, e) =>
{
if(button != SelectedButton) button.Opacity = 0.2F;
};
button.MouseLeave += (sender, e) =>
{
if(button != SelectedButton) button.Opacity = 0;
};
this.ResumeLayout();
}
public void AddButtons(MyToolBarButton[] buttons)
{
int maxWidth = 72.DpiZoom();
Array.ForEach(buttons, button => maxWidth = Math.Max(maxWidth, TextRenderer.MeasureText(button.Text, button.Font).Width));
Array.ForEach(buttons, button => { button.Width = maxWidth; AddButton(button); });
}
}
public sealed class MyToolBarButton : Panel
{
public MyToolBarButton(Image image, string text)
{
this.SuspendLayout();
this.DoubleBuffered = true;
this.Cursor = Cursors.Hand;
this.Size = new Size(72, 72).DpiZoom();
this.Controls.AddRange(new Control[] { picImage, lblText });
lblText.Resize += (sender, e) => this.OnResize(null);
picImage.Top = 6.DpiZoom();
lblText.Top = 52.DpiZoom();
lblText.SetEnabled(false);
this.Image = image;
this.Text = text;
this.ResumeLayout();
}
readonly PictureBox picImage = new PictureBox
{
SizeMode = PictureBoxSizeMode.StretchImage,
Size = new Size(40, 40).DpiZoom(),
BackColor = Color.Transparent,
Enabled = false
};
readonly Label lblText = new Label
{
BackColor = Color.Transparent,
Font = SystemFonts.MenuFont,
ForeColor = Color.White,
AutoSize = true,
};
public Image Image
{
get => picImage.Image;
set => picImage.Image = value;
}
public new string Text
{
get => lblText.Text;
set => lblText.Text = value;
}
public float Opacity
{
get => BackColor.A / 255;
set => BackColor = Color.FromArgb((int)(value * 255), Color.White);
}
public bool CanBeSelected { get; set; } = true;
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
lblText.Left = (this.Width - lblText.Width) / 2;
picImage.Left = (this.Width - picImage.Width) / 2;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/PictureButton.cs
================================================
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public class PictureButton : PictureBox
{
public PictureButton(Image image)
{
this.BaseImage = image;
this.SizeMode = PictureBoxSizeMode.AutoSize;
this.Cursor = Cursors.Hand;
}
private Image baseImage;
public Image BaseImage
{
get => baseImage;
set
{
baseImage = value;
this.Image = ToolStripRenderer.CreateDisabledImage(value);
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e); this.Image = BaseImage;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.Image = ToolStripRenderer.CreateDisabledImage(BaseImage);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left) base.OnMouseDown(e);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/ReadOnlyTextBox.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public sealed class ReadOnlyTextBox : TextBox
{
public ReadOnlyTextBox()
{
this.ReadOnly = true;
this.Multiline = true;
this.ShortcutsEnabled = false;
this.BackColor = Color.White;
this.ForeColor = Color.FromArgb(80, 80, 80);
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
}
const int WM_SETFOCUS = 0x0007;
const int WM_KILLFOCUS = 0x0008;
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_SETFOCUS:
m.Msg = WM_KILLFOCUS; break;
}
base.WndProc(ref m);
}
private bool firstEnter = true;
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
if(firstEnter) this.Focus();
firstEnter = false;
}
}
public sealed class ReadOnlyRichTextBox : RichTextBox
{
public ReadOnlyRichTextBox()
{
this.ReadOnly = true;
this.Dock = DockStyle.Fill;
this.BackColor = Color.White;
this.BorderStyle = BorderStyle.None;
this.ForeColor = Color.FromArgb(80, 80, 80);
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
}
const int WM_SETFOCUS = 0x0007;
const int WM_KILLFOCUS = 0x0008;
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_SETFOCUS:
m.Msg = WM_KILLFOCUS; break;
}
base.WndProc(ref m);
}
private bool firstEnter = true;
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
if(firstEnter) this.Focus();
firstEnter = false;
}
protected override void OnLinkClicked(LinkClickedEventArgs e)
{
base.OnLinkClicked(e);
ExternalProgram.OpenWebUrl(e.LinkText);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/ResizeLimitedForm.cs
================================================
using System;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
/// 限制水平、竖直方向调整大小的窗体
public class ResizeLimitedForm : Form
{
/// 水平方向可调整大小
public bool HorizontalResizable { get; set; } = true;
/// 竖直方向可调整大小
public bool VerticalResizable { get; set; } = true;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if(m.Msg == WM_NCHITTEST && this.WindowState == FormWindowState.Normal)
{
IntPtr hNowhere = new IntPtr((int)HitTest.Nowhere);
switch((HitTest)m.Result)
{
case HitTest.Top:
case HitTest.Bottom:
if(!VerticalResizable) m.Result = hNowhere;
break;
case HitTest.Left:
case HitTest.Right:
if(!HorizontalResizable) m.Result = hNowhere;
break;
case HitTest.TopLeft:
case HitTest.TopRight:
case HitTest.BottomLeft:
case HitTest.BottomRight:
if(!VerticalResizable || !HorizontalResizable) m.Result = hNowhere;
break;
}
}
}
const int WM_NCHITTEST = 0x84;//光标移动或鼠标按下、释放时的消息
/// 鼠标击中位置
enum HitTest : int
{
Error = -2,
Transparent = -1,
Nowhere = 0,
Client = 1,
TitleBar = 2,
SysMenu = 3,
Size = 4,
GrowBox = 5,
Hscroll = 6,
Vscroll = 7,
MinButton = 8,
MaxButton = 9,
Left = 10,
Right = 11,
Top = 12,
TopLeft = 13,
TopRight = 14,
Bottom = 15,
BottomLeft = 16,
BottomRight = 17,
Border = 18,
Close = 20,
Help = 21
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/SelectDialog.cs
================================================
using BluePointLilac.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Controls
{
public class SelectDialog : CommonDialog
{
public string Title { get; set; }
public string Selected { get; set; }
public int SelectedIndex { get; set; }
public string[] Items { get; set; }
public bool CanEdit { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(SelectForm frm = new SelectForm())
{
frm.Text = this.Title;
frm.Items = this.Items;
if(this.Selected != null) frm.Selected = this.Selected;
else frm.SelectedIndex = this.SelectedIndex;
frm.CanEdit = this.CanEdit;
Form owner = (Form)Control.FromHandle(hwndOwner);
if(owner != null) frm.TopMost = owner.TopMost;
bool flag = frm.ShowDialog() == DialogResult.OK;
if(flag)
{
this.Selected = frm.Selected;
this.SelectedIndex = frm.SelectedIndex;
}
return flag;
}
}
sealed class SelectForm : Form
{
public SelectForm()
{
this.SuspendLayout();
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
this.Font = SystemFonts.MenuFont;
this.ShowIcon = this.ShowInTaskbar = false;
this.MaximizeBox = this.MinimizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.StartPosition = FormStartPosition.CenterParent;
this.InitializeComponents();
this.ResumeLayout();
}
public string Selected
{
get => cmbItems.Text;
set => cmbItems.Text = value;
}
public string[] Items
{
get
{
string[] value = new string[cmbItems.Items.Count];
cmbItems.Items.CopyTo(value, 0);
return value;
}
set
{
cmbItems.Items.Clear();
cmbItems.Items.AddRange(value);
}
}
public bool CanEdit
{
get => cmbItems.DropDownStyle == ComboBoxStyle.DropDown;
set => cmbItems.DropDownStyle = value ? ComboBoxStyle.DropDown : ComboBoxStyle.DropDownList;
}
public int SelectedIndex
{
get => cmbItems.SelectedIndex;
set => cmbItems.SelectedIndex = value;
}
readonly Button btnOK = new Button
{
DialogResult = DialogResult.OK,
Text = ResourceString.OK,
AutoSize = true
};
readonly Button btnCancel = new Button
{
DialogResult = DialogResult.Cancel,
Text = ResourceString.Cancel,
AutoSize = true
};
readonly ComboBox cmbItems = new ComboBox
{
AutoCompleteMode = AutoCompleteMode.SuggestAppend,
AutoCompleteSource = AutoCompleteSource.ListItems,
DropDownHeight = 294.DpiZoom(),
ImeMode = ImeMode.Disable
};
private void InitializeComponents()
{
this.Controls.AddRange(new Control[] { cmbItems, btnOK, btnCancel });
int a = 20.DpiZoom();
cmbItems.Left = a;
cmbItems.Width = 85.DpiZoom();
cmbItems.Top = btnOK.Top = btnCancel.Top = a;
btnOK.Left = cmbItems.Right + a;
btnCancel.Left = btnOK.Right + a;
this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a);
cmbItems.AutosizeDropDownWidth();
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Controls/UAWebClient.cs
================================================
using System;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
namespace BluePointLilac.Controls
{
public sealed class UAWebClient : WebClient
{
public UAWebClient()
{
//此类主要为了解决访问Github的一些问题
//请求被中止: 未能创建 SSL/TLS 安全通道; 基础连接已经关闭: 发送时发生错误,一般添加TLS12即可
//TLS12------0xc00,TLS11------0x300,TLS------0xc0,SSL------0x30;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc00 | 0x300 | 0xc0 | 0x30);
//网络传输默认文本编码 UTF-8
this.Encoding = Encoding.UTF8;
//远程服务器返回错误: (403) 已禁止
//浏览器 F12 console 输入 console.log(navigator.userAgent); 获取 User Agent
this.Headers.Add("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.66");
}
/// 获取网页文本
public string GetWebString(string url)
{
try
{
string str = this.DownloadString(url);
str = str?.Replace("\n", Environment.NewLine);//换行符转换
return str;
}
catch { return null; }
}
/// 将网络文本写入本地文件
/// 本地文件路径
/// 网络文件Raw路径
public bool WebStringToFile(string filePath, string fileUrl)
{
string contents = GetWebString(fileUrl);
bool flag = contents != null;
if(flag) File.WriteAllText(filePath, contents, Encoding.Unicode);
return flag;
}
/// 获取网页Json文本并加工为Xml
public XmlDocument GetWebJsonToXml(string url)
{
try
{
byte[] bytes = this.DownloadData(url);
using(XmlReader xReader = JsonReaderWriterFactory.CreateJsonReader(bytes, XmlDictionaryReaderQuotas.Max))
{
XmlDocument doc = new XmlDocument();
doc.Load(xReader);
return doc;
}
}
catch { return null; }
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ComboBoxExtension.cs
================================================
using System;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class ComboBoxExtension
{
public static void AutosizeDropDownWidth(this ComboBox box)
{
box.DropDown += (sender, e) =>
{
int maxWidth = 0;
foreach(var item in box.Items)
{
maxWidth = Math.Max(maxWidth, TextRenderer.MeasureText(item.ToString(), box.Font).Width);
}
maxWidth = Math.Max(maxWidth, box.Width);
box.DropDownWidth = maxWidth;
};
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs
================================================
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class ControlExtension
{
[DllImport("user32.dll")]
private static extern int PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool ReleaseCapture();
/// 使控件能够移动所属窗体
/// 目标控件
public static void CanMoveForm(this Control ctr)
{
const int WM_NCLBUTTONDOWN = 0xA1;
const int HT_CAPTION = 0x2;
ctr.MouseMove += (sender, e) =>
{
if(e.Button != MouseButtons.Left) return;
ReleaseCapture();
PostMessage(ctr.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
};
}
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int wndproc);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
/// 通过Win32API禁用/启用目标控件
/// 控件被禁用时仍可更改字体颜色,不需要同时设置ctr.Enabled=false
/// 目标控件
/// 启用为true,禁用为false
public static void SetEnabled(this Control ctr, bool enabled)
{
const int GWL_STYLE = -16;
const int WS_DISABLED = 0x8000000;
int value = GetWindowLong(ctr.Handle, GWL_STYLE);
if(enabled) value &= ~WS_DISABLED;
else value |= WS_DISABLED;
SetWindowLong(ctr.Handle, GWL_STYLE, value);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs
================================================
using System.IO;
namespace BluePointLilac.Methods
{
public static class DirectoryEx
{
public static void CopyTo(string srcDirPath, string dstDirPath)
{
DirectoryInfo srcDi = new DirectoryInfo(srcDirPath);
DirectoryInfo dstDi = new DirectoryInfo(dstDirPath);
dstDi.Create();
foreach(FileInfo srcFi in srcDi.GetFiles())
{
string dstFilePath = $@"{dstDirPath}\{srcFi.Name}";
srcFi.CopyTo(dstFilePath, true);
}
foreach(DirectoryInfo srcSubDi in srcDi.GetDirectories())
{
DirectoryInfo dstSubDi = dstDi.CreateSubdirectory(srcSubDi.Name);
CopyTo(srcSubDi.FullName, dstSubDi.FullName);
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ElevatedFileDroper.cs
================================================
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
/// 代码用途:管理员UAC进程窗口拖放文件
/// 代码来源1:https://zhuanlan.zhihu.com/p/48735364
/// 代码来源2:https://github.com/volschin/sdimager/blob/master/ElevatedDragDropManager.cs
/// 代码作者:雨少主(知乎)、volschin(Github)、蓝点lilac(转载、修改)
/// 调用方法:var droper = new ElevatedFileDroper(control);
/// droper.DragDrop += (sender, e) => MessageBox.Show(droper.DropFilePaths[0]);
/// 备注:此类只能生效一个实例,不能将control.AllowDrop设为true,droper.DragDrop与control.DragDrop不共存
public sealed class ElevatedFileDroper : IMessageFilter
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint message, ChangeFilterAction action, in ChangeFilterStruct pChangeFilterStruct);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags);
[DllImport("shell32.dll", SetLastError = false)]
private static extern void DragAcceptFiles(IntPtr hWnd, bool fAccept);
[DllImport("shell32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
private static extern uint DragQueryFile(IntPtr hWnd, uint iFile, StringBuilder lpszFile, int cch);
[DllImport("shell32.dll", SetLastError = false)]
private static extern bool DragQueryPoint(IntPtr hDrop, out Point lppt);
[DllImport("shell32.dll", SetLastError = false)]
private static extern void DragFinish(IntPtr hDrop);
[StructLayout(LayoutKind.Sequential)]
struct ChangeFilterStruct
{
public uint CbSize;
public ChangeFilterStatu ExtStatus;
}
enum ChangeWindowMessageFilterFlags : uint
{
MSGFLT_ADD = 1,
MSGFLT_REMOVE = 2
}
enum ChangeFilterAction : uint
{
MSGFLT_RESET,
MSGFLT_ALLOW,
MSGFLT_DISALLOW
}
enum ChangeFilterStatu : uint
{
MSGFLTINFO_NONE,
MSGFLTINFO_ALREADYALLOWED_FORWND,
MSGFLTINFO_ALREADYDISALLOWED_FORWND,
MSGFLTINFO_ALLOWED_HIGHER
}
const uint WM_COPYGLOBALDATA = 0x0049;
const uint WM_COPYDATA = 0x004A;
const uint WM_DROPFILES = 0x0233;
public event EventHandler DragDrop;
public string[] DropFilePaths { get; private set; }
public Point DropPoint { get; private set; }
public ElevatedFileDroper(Control ctr)
{
ctr.AllowDrop = false;
DragAcceptFiles(ctr.Handle, true);
Application.AddMessageFilter(this);
ctr.Disposed += (sender, e) => Application.RemoveMessageFilter(this);
if(ctr is Form frm)
{
double opacity = frm.Opacity;
frm.Paint += (sender, e) =>
{
if(frm.Opacity != opacity)
{
//窗体透明度变化时需要重新注册接受文件拖拽标识符
DragAcceptFiles(ctr.Handle, true);
opacity = frm.Opacity;
}
};
}
Version ver = Environment.OSVersion.Version;
bool isVistaOrHigher = ver >= new Version(6, 0);
bool isWin7OrHigher = ver >= new Version(6, 1);
var status = new ChangeFilterStruct { CbSize = 8 };
if(isVistaOrHigher)
{
foreach(uint msg in new[] { WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA })
{
bool error = false;
if(isWin7OrHigher)
{
error = !ChangeWindowMessageFilterEx(ctr.Handle, msg, ChangeFilterAction.MSGFLT_ALLOW, in status);
}
else
{
error = !ChangeWindowMessageFilter(msg, ChangeWindowMessageFilterFlags.MSGFLT_ADD);
}
if(error) throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
public bool PreFilterMessage(ref Message m)
{
if(m.Msg != WM_DROPFILES) return false;
IntPtr handle = m.WParam;
uint fileCount = DragQueryFile(handle, uint.MaxValue, null, 0);
string[] filePaths = new string[fileCount];
for(uint i = 0; i < fileCount; i++)
{
StringBuilder sb = new StringBuilder(260);
uint result = DragQueryFile(handle, i, sb, sb.Capacity);
if(result > 0) filePaths[i] = sb.ToString();
}
DragQueryPoint(handle, out Point point);
DragFinish(handle);
DropPoint = point;
DropFilePaths = filePaths;
DragDrop?.Invoke(this, null);
return true;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/EncodingType.cs
================================================
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace BluePointLilac.Methods
{
/* 获取文本文件编码类型
* 代码参考:https://www.cnblogs.com/guyun/p/4262587.html (Napoléon)*/
public static class EncodingType
{
/// 各种带BOM的编码BOM值
private static readonly Dictionary EncodingBomBytes = new Dictionary
{
{ new byte[] { 0xEF, 0xBB, 0xBF }, Encoding.UTF8 }, //UTF-8 EF BB BF
{ new byte[] { 0xFF, 0xFE, 0x00, 0x00 }, Encoding.UTF32 }, //UTF-32LE FF FE 00 00
{ new byte[] { 0xFF, 0xFE }, Encoding.Unicode }, //UTF-16LE FF FE
{ new byte[] { 0xFE, 0xFF }, Encoding.BigEndianUnicode }, //UTF-16BE FE FF
{ new byte[] { 0x2B, 0x2F, 0x76 }, Encoding.UTF7 }, //UTF-7 2B 2F 76
{ new byte[] { 0x00, 0x00, 0xFE, 0xFF }, new UTF32Encoding(true, true) }, //UTF-32BE 00 00 FE FF
};
/// 获取给定的文件的编码类型
/// 文件路径
/// 文件的编码类型
public static Encoding GetType(string filePath)
{
byte[] fs = File.ReadAllBytes(filePath);
foreach(var kv in EncodingBomBytes)
{
if(fs.Length < kv.Key.Length) continue;
int i = -1;
bool flag = kv.Key.All(s => { i++; return s == fs[i]; });
if(flag) return kv.Value;
}
if(IsUTF8Bytes(fs)) return Encoding.UTF8; //不带BOM的UTF-8
return Encoding.Default;
}
/// 判断是否是不带 BOM 的 UTF8 格式
///
private static bool IsUTF8Bytes(byte[] bytes)
{
int count = 1; //计算当前正分析的字符应还有的字节数
for(int i = 0; i < bytes.Length; i++)
{
byte curByte = bytes[i];//当前分析的字节.
if(count == 1)
{
if(curByte >= 0x80)
{
//判断当前
while(((curByte <<= 1) & 0x80) != 0) count++;
//标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X
if(count == 1 || count > 6) return false;
}
}
else
{
//若是UTF-8 此时第一位必须为1
if((curByte & 0xC0) != 0x80) return false;
else count--;
}
}
//if(count > 1) throw new Exception("非预期的byte格式");
return true;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs
================================================
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace BluePointLilac.Methods
{
/// 外部程序
public static class ExternalProgram
{
/// 在Regedit中跳转指定路径并定位指定键名
/// 注册表项路径
/// 注册表键名
/// 窗口是否多开
public static void JumpRegEdit(string regPath, string valueName = null, bool moreOpen = false)
{
//还有一种方法,修改HKCU\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit
//中的LastKey键值(记录上次关闭注册表编辑器时的注册表路径)为要跳转的注册表项路径regPath,
//再使用Process.Start("regedit.exe", "-m")打开注册表编辑器
//优点:代码少、不会有Bug。缺点:不能定位具体键,没有逐步展开效果
if(regPath == null) return;
Process process;
IntPtr hMain = FindWindow("RegEdit_RegEdit", null);
if(hMain != IntPtr.Zero && !moreOpen)
{
GetWindowThreadProcessId(hMain, out int id);
process = Process.GetProcessById(id);
}
else
{
//注册表编辑器窗口多开
process = Process.Start("regedit.exe", "-m");
process.WaitForInputIdle();
hMain = process.MainWindowHandle;
}
ShowWindowAsync(hMain, SW_SHOWNORMAL);
SetForegroundWindow(hMain);
IntPtr hTree = FindWindowEx(hMain, IntPtr.Zero, "SysTreeView32", null);
IntPtr hList = FindWindowEx(hMain, IntPtr.Zero, "SysListView32", null);
SetForegroundWindow(hTree);
SetFocus(hTree);
process.WaitForInputIdle();
SendMessage(hTree, WM_KEYDOWN, VK_HOME, null);
Thread.Sleep(100);
process.WaitForInputIdle();
SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null);
foreach(char chr in Encoding.Default.GetBytes(regPath))
{
process.WaitForInputIdle();
if(chr == '\\')
{
Thread.Sleep(100);
SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null);
}
else
{
SendMessage(hTree, WM_CHAR, Convert.ToInt16(chr), null);
}
}
if(string.IsNullOrEmpty(valueName)) return;
using(RegistryKey key = RegistryEx.GetRegistryKey(regPath))
{
if(key?.GetValue(valueName) == null) return;
}
Thread.Sleep(100);
SetForegroundWindow(hList);
SetFocus(hList);
process.WaitForInputIdle();
SendMessage(hList, WM_KEYDOWN, VK_HOME, null);
foreach(char chr in Encoding.Default.GetBytes(valueName))
{
process.WaitForInputIdle();
SendMessage(hList, WM_CHAR, Convert.ToInt16(chr), null);
}
process.Dispose();
}
/// 在Explorer中选中指定文件或文件夹
/// 文件或文件夹路径
/// 窗口是否多开
public static void JumpExplorer(string filePath, bool moreOpen = false)
{
if(filePath == null) return;
if(!moreOpen)
{
IntPtr pidlList = ILCreateFromPathW(filePath);
if(pidlList == IntPtr.Zero) return;
SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0);
ILFree(pidlList);
}
else
{
using(Process process = new Process())
{
process.StartInfo.FileName = "explorer.exe";
process.StartInfo.Arguments = $"/select, {filePath}";
process.Start();
}
}
}
/// 在Explorer中打开指定目录
/// 目录路径
public static void OpenDirectory(string dirPath)
{
if(!Directory.Exists(dirPath)) return;
using(Process process = new Process())
{
process.StartInfo.FileName = dirPath;
process.Start();
}
}
/// 打开文件或文件夹的属性对话框
/// 文件或文件夹路径
public static bool ShowPropertiesDialog(string filePath)
{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO
{
lpVerb = "Properties",
//显示详细信息选项卡, 此处有语言差异
//lpParameters = ResourceString.GetDirectString("@shell32.dll,-31433"),//"详细信息",
lpFile = filePath,
nShow = SW_SHOW,
fMask = SEE_MASK_INVOKEIDLIST,
cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO))
};
return ShellExecuteEx(ref info);
}
/// 打开指定未关联打开方式的扩展名的打开方式对话框
/// 文件扩展名
public static void ShowOpenWithDialog(string extension)
{
//Win10 调用 SHOpenWithDialog API 或调用 OpenWith.exe -override "%1"
//或调用 rundll32.exe shell32.dll,OpenAs_RunDLL %1 能显示打开方式对话框,但都不能设置默认应用
//以下方法只针对未关联打开方式的扩展名显示系统打开方式对话框,对于已关联打开方式的扩展名会报错
string tempPath = $"{Path.GetTempPath()}{Guid.NewGuid()}{extension}";
File.WriteAllText(tempPath, "");
using(Process process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = tempPath,
Verb = "openas"
};
process.Start();
}
File.Delete(tempPath);
}
/// 重启Explorer
public static void RestartExplorer()
{
using(Process process = new Process())
{
//有些系统有tskill.exe可以直接调用tskill explorer命令
process.StartInfo = new ProcessStartInfo
{
FileName = "taskkill.exe",
Arguments = "-f -im explorer.exe",
WindowStyle = ProcessWindowStyle.Hidden
};
process.Start();
process.WaitForExit();
process.StartInfo = new ProcessStartInfo("explorer.exe");
process.Start();
}
}
/// 调用默认浏览器打开指定网址
/// 网址
public static void OpenWebUrl(string url)
{
if(url == null) return;
//替换网址转义符
url = url.Replace("%", "%25").Replace("#", "%23").Replace("&", "%26").Replace("+", "%2B");
using(Process process = new Process())
{
//通过explorer来调用默认浏览器打开链接,避免管理员权限影响
process.StartInfo = new ProcessStartInfo($"\"{url}\"");
process.Start();
}
}
/// 导出指定注册表项的.reg文件
/// 注册表项路径
/// .reg文件保存路径
public static void ExportRegistry(string regPath, string filePath)
{
using(Process process = new Process())
{
process.StartInfo.FileName = "regedit.exe";
process.StartInfo.Arguments = $"/e \"{filePath}\" \"{regPath}\"";
process.Start();
process.WaitForExit();
}
}
/// 打开记事本显示指定文本
/// 要显示的文本
public static void OpenNotepadWithText(string text)
{
using(Process process = Process.Start("notepad.exe"))
{
process.WaitForInputIdle();
IntPtr handle = FindWindowEx(process.MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessage(handle, WM_SETTEXT, 0, text);
}
}
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOW = 5;
private const uint SEE_MASK_INVOKEIDLIST = 12;
private const int WM_SETTEXT = 0xC;
private const int WM_KEYDOWN = 0x100;
private const int WM_CHAR = 0x102;
private const int VK_HOME = 0x24;
private const int VK_RIGHT = 0x27;
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChild, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("shell32.dll", ExactSpelling = true)]
private static extern void ILFree(IntPtr pidlList);
[DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern IntPtr ILCreateFromPathW(string pszPath);
[DllImport("shell32.dll", ExactSpelling = true)]
private static extern IntPtr SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SHELLEXECUTEINFO
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpFile;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIcon;
public IntPtr hProcess;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/FileExtension.cs
================================================
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace BluePointLilac.Methods
{
public static class FileExtension
{
[Flags]
enum AssocF
{
Init_NoRemapCLSID = 0x1,
Init_ByExeName = 0x2,
Open_ByExeName = 0x2,
Init_DefaultToStar = 0x4,
Init_DefaultToFolder = 0x8,
NoUserSettings = 0x10,
NoTruncate = 0x20,
Verify = 0x40,
RemapRunDll = 0x80,
NoFixUps = 0x100,
IgnoreBaseClass = 0x200
}
public enum AssocStr
{
Command = 1,
Executable,
FriendlyDocName,
FriendlyAppName,
NoOpen,
ShellNewValue,
DDECommand,
DDEIfExec,
DDEApplication,
DDETopic
}
[DllImport("shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, ref uint pcchOut);
public const string FILEEXTSPATH = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts";
private const string HKCRCLASSES = @"HKEY_CURRENT_USER\SOFTWARE\Classes";
private const string HKLMCLASSES = @"HKEY_LOCAL_MACHINE\SOFTWARE\Classes";
public static string GetExtentionInfo(AssocStr assocStr, string extension)
{
uint pcchOut = 0;
AssocQueryString(AssocF.Verify, assocStr, extension, null, null, ref pcchOut);
StringBuilder pszOut = new StringBuilder((int)pcchOut);
AssocQueryString(AssocF.Verify, assocStr, extension, null, pszOut, ref pcchOut);
return pszOut.ToString();
}
public static string GetOpenMode(string extension)
{
if(string.IsNullOrEmpty(extension)) return null;
string mode;
bool CheckMode()
{
if(mode.IsNullOrWhiteSpace()) return false;
if(mode.Length > 255) return false;
if(mode.ToLower().StartsWith(@"applications\")) return false;
using(RegistryKey root = Registry.ClassesRoot)
using(RegistryKey key = root.OpenSubKey(mode))
{
return key != null;
}
}
mode = Registry.GetValue($@"{FILEEXTSPATH}\{extension}\UserChoice", "ProgId", null)?.ToString();
if(CheckMode()) return mode;
mode = Registry.GetValue($@"{HKLMCLASSES}\{extension}", "", null)?.ToString();
if(CheckMode()) return mode;
mode = Registry.GetValue($@"{HKCRCLASSES}\{extension}", "", null)?.ToString();
if(CheckMode()) return mode;
return null;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/FormExtension.cs
================================================
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class FormExtension
{
/// 移动窗体时同时移动另一个窗体
/// 主动移动的窗体
/// 同时被移动的窗体
public static void MoveAsMove(this Form frm1, Form frm2)
{
if(frm2 == null) return;
Point pLast = Point.Empty;
frm1.Load += (sender, e) => pLast = frm1.Location;
frm1.LocationChanged += (sender, e) =>
{
if(pLast == Point.Empty) return;
frm2.Left += frm1.Left - pLast.X;
frm2.Top += frm1.Top - pLast.Y;
pLast = frm1.Location;
};
}
/// 给窗体添加ESC键关闭功能
/// 指定窗口
/// 关闭窗口时的对话框返回值
/// 也可以重写Form的ProcessDialogKey事件,
/// 这个方法更简单,但遍历窗体控件时切记多了一个不可见的关闭按钮
public static void AddEscapeButton(this Form frm, DialogResult dr = DialogResult.Cancel)
{
Button btn = new Button
{
Parent = frm,
Size = Size.Empty,
DialogResult = dr
};
frm.CancelButton = btn;
frm.Disposed += (sender, e) => btn.Dispose();
frm.FormClosing += (sender, e) => btn.PerformClick();
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/GuidEx.cs
================================================
using System;
using System.Text.RegularExpressions;
namespace BluePointLilac.Methods
{
//为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的Guid.TryParse)写了这个扩展方法
public static class GuidEx
{
public static bool TryParse(string str, out Guid guid)
{
if(IsGuid(str))
{
guid = new Guid(str);
return true;
}
else
{
guid = Guid.Empty;
return false;
}
}
private static readonly Regex GuidRegex = new Regex(@"[A-F0-9]{8}(\-[A-F0-9]{4}){3}\-[A-F0-9]{12}", RegexOptions.IgnoreCase);
public static bool IsGuid(string str)
{
if(string.IsNullOrEmpty(str)) return false;
if(str.Length == 38 && str.StartsWith("{") && str.EndsWith("}") && GuidRegex.IsMatch(str)) return true;
if(str.Length == 36 && GuidRegex.IsMatch(str)) return true;
return false;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/HighDpi.cs
================================================
using System.Drawing;
using System.Windows;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
/// 处理不同DPI缩放比下的像素绘制和字体显示问题
/// 使用此类需要添加引用 PresentationFramework
/// 还应在配置清单App.manifest中启用DPI感知自动缩放
/// Font为矢量类型,Point、Size、Rectangle、Padding等为像素类型。
/// 在不同DPI缩放下,矢量类型等比缩放,像素类型保持不变,故会出现排版显示问题。
/// 解决方案一:项目中所有用到的像素类型实例值都取与缩放比之积,矢量类型不变。
/// 解决方案二:项目中所有用到的矢量类型实例都取与缩放比之商,像素类型不变
public static class HighDpi
{
/// DPI缩放比
public static readonly double DpiScale = Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;
public static Point DpiZoom(this Point point) => new Point(DpiZoom(point.X), DpiZoom(point.Y));
public static PointF DpiZoom(this PointF point) => new PointF(DpiZoom(point.X), DpiZoom(point.Y));
public static Size DpiZoom(this Size size) => new Size(DpiZoom(size.Width), DpiZoom(size.Height));
public static SizeF DpiZoom(this SizeF size) => new SizeF(DpiZoom(size.Width), DpiZoom(size.Height));
public static Rectangle DpiZoom(this Rectangle r) => new Rectangle(DpiZoom(r.Location), DpiZoom(r.Size));
public static RectangleF DpiZoom(this RectangleF r) => new RectangleF(DpiZoom(r.Location), DpiZoom(r.Size));
public static Padding DpiZoom(this Padding p) => new Padding(DpiZoom(p.Left), DpiZoom(p.Top), DpiZoom(p.Right), DpiZoom(p.Bottom));
public static Font DpiZoom(this Font font) => new Font(font.FontFamily, font.Size / DpiZoom(1F), font.Style);
public static int DpiZoom(this int num) => (int)(num * DpiScale);
public static float DpiZoom(this float num) => (float)(num * DpiScale);
public static double DpiZoom(this double num) => num * DpiScale;
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs
================================================
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace BluePointLilac.Methods
{
public static class ImageExtension
{
public static Image ToTransparent(this Image image, float opacity = 0.5F)
{
Bitmap bitmap = new Bitmap(image.Width, image.Height);
using(Graphics g = Graphics.FromImage(bitmap))
using(ImageAttributes attributes = new ImageAttributes())
{
ColorMatrix matrix = new ColorMatrix { Matrix33 = opacity };
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
return bitmap;
}
public static Image ResizeImage(this Image image, int width, int height)
{
//return image.GetThumbnailImage(width, height, null, System.IntPtr.Zero);//质量稍微低一点
if(image.Width == width && image.Height == height) return image;
Bitmap destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using(Graphics g = Graphics.FromImage(destImage))
{
g.CompositingMode = CompositingMode.SourceCopy;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.CompositingQuality = CompositingQuality.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
using(ImageAttributes attributes = new ImageAttributes())
{
attributes.SetWrapMode(WrapMode.TileFlipXY);
g.DrawImage(image, new Rectangle(0, 0, width, height),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
}
return destImage;
}
public static Image ResizeImage(this Image image, double scale)
{
if(scale == 1) return image;
int width = (int)(image.Width * scale);
int height = (int)(image.Height * scale);
return image.ResizeImage(width, height);
}
public static Image ResizeImage(this Image image, Size newSize)
{
if(newSize == image.Size) return image;
return image.ResizeImage(newSize.Width, newSize.Height);
}
public static Image RotateImage(this Image image, RotateFlipType rotateType)
{
Bitmap bitmap = new Bitmap(image);
bitmap.RotateFlip(rotateType);
return bitmap;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/IniReader.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace BluePointLilac.Methods
{
public sealed class IniReader
{
public IniReader() { }
public IniReader(StringBuilder sb) => LoadStringBuilder(sb);
public IniReader(string filePath) => LoadFile(filePath);
private readonly Dictionary> RootDic
= new Dictionary>(StringComparer.OrdinalIgnoreCase);
public string[] Sections => RootDic.Keys.ToArray();
public void LoadStringBuilder(StringBuilder sb)
{
RootDic.Clear();
if(sb.ToString().IsNullOrWhiteSpace()) return;
List lines = sb.ToString().Split(new[] { "\r\n", "\n" },
StringSplitOptions.RemoveEmptyEntries).ToList();//拆分为行
lines.ForEach(line => line.Trim());
ReadLines(lines);
}
public void LoadFile(string filePath)
{
RootDic.Clear();
if(!File.Exists(filePath)) return;
List lines = new List();
using(StreamReader reader = new StreamReader(filePath, EncodingType.GetType(filePath)))
{
while(!reader.EndOfStream)
{
string line = reader.ReadLine().Trim();
if(line != string.Empty) lines.Add(line);
}
}
ReadLines(lines);
}
private void ReadLines(List lines)
{
lines.RemoveAll(
line => line.StartsWith(";") || line.StartsWith("#")//移除注释
|| (!line.StartsWith("[") && !line.Contains("=")));//移除非section行且非key行
if(lines.Count == 0) return;
List indexs = new List { 0 };
for(int i = 1; i < lines.Count; i++)
{
if(lines[i].StartsWith("[")) indexs.Add(i);//获取section行号
}
indexs.Add(lines.Count);
for(int i = 0; i < indexs.Count - 1; i++)
{
string section = lines[indexs[i]];
int m = section.IndexOf(']') - 1;
if(m < 0) continue;
section = section.Substring(1, m);
if(RootDic.ContainsKey(section)) continue;
var keyValues = new Dictionary(StringComparer.OrdinalIgnoreCase);
RootDic.Add(section, keyValues);
for(int j = indexs[i] + 1; j < indexs[i + 1]; j++)
{
int k = lines[j].IndexOf('=');
string key = lines[j].Substring(0, k).TrimEnd();
string value = lines[j].Substring(k + 1).TrimStart();
if(keyValues.ContainsKey(key)) continue;
keyValues.Add(key, value);
}
}
}
public string GetValue(string section, string key)
{
if(RootDic.TryGetValue(section, out Dictionary sectionDic))
if(sectionDic.TryGetValue(key, out string value))
return value;
return string.Empty;
}
public bool TryGetValue(string section, string key, out string value)
{
value = GetValue(section, key);
return value != string.Empty;
}
public string[] GetSectionKeys(string section)
{
if(!RootDic.ContainsKey(section)) return null;
else return RootDic[section].Keys.ToArray();
}
public bool RemoveSection(string section)
{
return RootDic.Remove(section);
}
public bool RemoveKey(string section, string key)
{
if(RootDic.ContainsKey(section))
{
return RootDic[section].Remove(key);
}
return false;
}
public void AddValue(string section, string key, string value)
{
if(RootDic.ContainsKey(section))
{
if(RootDic[section].ContainsKey(key))
{
RootDic[section][key] = value;
}
else
{
RootDic[section].Add(key, value);
}
}
else
{
var dic = new Dictionary(StringComparer.OrdinalIgnoreCase);
RootDic.Add(section, dic);
dic.Add(key, value);
}
}
public void SaveFile(string filePath)
{
List lines = new List();
foreach(var item in RootDic)
{
lines.Add("[" + item.Key + "]");
foreach(var key in item.Value)
{
lines.Add(key.Key + " = " + key.Value);
}
lines.Add("");
}
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
FileAttributes attributes = FileAttributes.Normal;
Encoding encoding = Encoding.Unicode;
if(File.Exists(filePath))
{
encoding = EncodingType.GetType(filePath);
attributes = File.GetAttributes(filePath);
File.SetAttributes(filePath, FileAttributes.Normal);
}
File.WriteAllLines(filePath, lines.ToArray(), encoding);
File.SetAttributes(filePath, attributes);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/IniWriter.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace BluePointLilac.Methods
{
public sealed class IniWriter
{
public IniWriter() { }
public IniWriter(string filePath)
{
this.FilePath = filePath;
}
public string FilePath { get; set; }
public bool DeleteFileWhenEmpty { get; set; }
private List GetLines()
{
List lines = new List();
if(!File.Exists(FilePath)) return lines;
using(StreamReader reader = new StreamReader(FilePath, EncodingType.GetType(FilePath)))
{
while(!reader.EndOfStream)
{
lines.Add(reader.ReadLine().Trim());
}
}
return lines;
}
/// 是否是获取value值
private void SetValue(string section, string key, ref string value, bool isGetValue)
{
if(section == null) return;
List lines = GetLines();
string sectionLine = $"[{section}]";
string keyLine = $"{key}={value}";
int sectionRow = -1, keyRow = -1;//先假设不存在目标section和目标key
int nextSectionRow = -1;//下一个section的行数
for(int i = 0; i < lines.Count; i++)
{
if(lines[i].StartsWith(sectionLine, StringComparison.OrdinalIgnoreCase))
{
sectionRow = i; break;//得到目标section所在行
}
}
if(sectionRow >= 0)//如果目标section存在
{
for(int i = sectionRow + 1; i < lines.Count; i++)
{
if(lines[i].StartsWith(";") || lines[i].StartsWith("#"))
{
continue;//跳过注释
}
if(lines[i].StartsWith("["))
{
nextSectionRow = i; break;//读取到下一个section
}
if(key != null && keyRow == -1)
{
int index = lines[i].IndexOf('=');
if(index < 0) continue;
string str = lines[i].Substring(0, index).TrimEnd();
if(str.Equals(key, StringComparison.OrdinalIgnoreCase))
{
if(isGetValue)//如果是获取Value值,直接返回
{
value = lines[i].Substring(index + 1).Trim();
return;
}
keyRow = i; continue;//得到目标key行
}
}
}
}
if(isGetValue) return;
if(sectionRow == -1)
{
if(key != null && value != null)
{
lines.Add(string.Empty);//添加空行
//目标section不存在则添加到最后
lines.Add(sectionLine);
lines.Add(keyLine);
}
}
else
{
if(keyRow == -1)
{
if(key != null)
{
//存在下一个section时插入到其上方
if(nextSectionRow != -1)
{
//目标section存在但目标key不存在
keyRow = nextSectionRow;
lines.Insert(keyRow, keyLine);
}
else
{
//不存在下一个section则添加到最后
lines.Add(keyLine);
}
}
else
{
//key为null则删除整个section
int count;
if(nextSectionRow == -1) count = lines.Count - sectionRow;
else count = nextSectionRow - sectionRow;
lines.RemoveRange(sectionRow, count);
}
}
else
{
if(value != null)
{
//目标section和目标key都存在
lines[keyRow] = keyLine;
}
else
{
//赋值为null则删除key
lines.RemoveAt(keyRow);
}
}
}
Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
FileAttributes attributes = FileAttributes.Normal;
Encoding encoding = Encoding.Unicode;
if(File.Exists(FilePath))
{
encoding = EncodingType.GetType(FilePath);
attributes = File.GetAttributes(FilePath);
File.SetAttributes(FilePath, FileAttributes.Normal);
}
File.WriteAllLines(FilePath, lines.ToArray(), encoding);
File.SetAttributes(FilePath, attributes);
if(DeleteFileWhenEmpty && lines.TrueForAll(line => line.IsNullOrWhiteSpace()))
{
File.Delete(FilePath);
}
}
public void SetValue(string section, string key, object value)
{
SetValue(section, key, value.ToString());
}
public void SetValue(string section, string key, string value)
{
SetValue(section, key, ref value, false);
}
public void DeleteKey(string section, string key)
{
SetValue(section, key, null);
}
public void DeleteSection(string section)
{
SetValue(section, null, null);
}
/// 一次读取只获取一个值,用此方法比IniReader.GetValue要快
public string GetValue(string section, string key)
{
string value = string.Empty;
SetValue(section, key, ref value, true);
return value;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs
================================================
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
/// 在窗体居中显示的MessageBox
public static class MessageBoxEx
{
/// 弹出一个消息框
/// 要在消息框中显示的文本
/// 要在消息框的标题栏中显示的文本
/// 指定在消息框中显示哪些按钮
/// 指定在消息框中显示哪个图标
/// 指定消息框的拥有者
/// 指定默认结果,使对应按钮预先获取焦点
/// 能否移动父窗体
/// System.Windows.Forms.DialogResult 值之一
public static DialogResult Show(string text, string caption = null,
MessageBoxButtons buttons = MessageBoxButtons.OK, MessageBoxIcon boxIcon = MessageBoxIcon.None,
IWin32Window owner = null, DialogResult defaultResult = DialogResult.None, bool canMoveParent = true)
{
using(MessageBoxForm frm = new MessageBoxForm(text, caption, buttons, boxIcon, defaultResult, canMoveParent))
{
return frm.ShowDialog(owner);
}
}
/// 弹出一个消息框
/// 要在消息框中显示的文本
/// 要在消息框的标题栏中显示的文本
/// 从右至左添加数组长度的个数的按钮,按钮文本为数组对应成员
/// 指定在消息框中显示的图标图像
/// 指定消息框的拥有者
/// 指定默认结果,使对应按钮预先获取焦点
/// 能否移动父窗体
/// 消息框关闭按钮是否可用
/// 返回用户点击的按钮所显示的文本
public static string Show(string text, string caption,
string[] buttonTexts, Image boxImaage,
IWin32Window owner = null, string defaultResult = null,
bool canMoveParent = true, bool closeBox = true)
{
using(MessageBoxForm frm = new MessageBoxForm(text, caption, buttonTexts, boxImaage, defaultResult, canMoveParent, closeBox))
{
frm.ShowDialog(owner);
return frm.Tag?.ToString();
}
}
sealed class MessageBoxForm : Form
{
private MessageBoxForm(string text, string caption, bool canMoveParent)
{
lblText.Text = text;
this.Text = caption;
this.CanMoveParent = canMoveParent;
this.Font = SystemFonts.MessageBoxFont;
this.ShowIcon = this.ShowInTaskbar = false;
this.MaximizeBox = this.MinimizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.StartPosition = FormStartPosition.CenterParent;
}
public MessageBoxForm(string text, string caption,
string[] buttonTexts, Image boxImage,
string defaultResult, bool canMoveParent, bool closeBox) : this(text, caption, canMoveParent)
{
this.CloseBox = closeBox;
this.InitializeComponents(buttonTexts, boxImage);
foreach(Button button in flpButtons.Controls)
{
button.Click += (sender, e) =>
{
this.Tag = button.Text;
this.Close();
};
this.Shown += (sender, e) =>
{
if(button.Text == defaultResult) button.Focus();
};
}
}
public MessageBoxForm(string text, string caption,
MessageBoxButtons buttons, MessageBoxIcon boxIcon,
DialogResult defaultResult, bool canMoveParent) : this(text, caption, canMoveParent)
{
string[] buttonTexts = null;
Image boxImage = null;
switch(buttons)
{
case MessageBoxButtons.OK:
buttonTexts = new[] { "OK" }; break;
case MessageBoxButtons.OKCancel:
buttonTexts = new[] { "Cancel", "OK" }; break;
case MessageBoxButtons.AbortRetryIgnore:
buttonTexts = new[] { "&Ignore", "&Retry", "&Abort" }; break;
case MessageBoxButtons.YesNoCancel:
buttonTexts = new[] { "Cancel", "&No", "&Yes" }; break;
case MessageBoxButtons.YesNo:
buttonTexts = new[] { "&No", "&Yes" }; break;
case MessageBoxButtons.RetryCancel:
buttonTexts = new[] { "Cancel", "&Retry" }; break;
}
switch(boxIcon)
{
case MessageBoxIcon.Question:
boxImage = MessageBoxImage.Question; break;
case MessageBoxIcon.Error:
boxImage = MessageBoxImage.Error; break;
case MessageBoxIcon.Warning:
boxImage = MessageBoxImage.Warning; break;
case MessageBoxIcon.Information:
boxImage = MessageBoxImage.Information; break;
}
this.InitializeComponents(buttonTexts, boxImage);
foreach(Button button in flpButtons.Controls)
{
switch(button.Text)
{
case "OK":
if(buttons == MessageBoxButtons.OK)
{
this.CancelButton = button;
this.FormClosing += (sender, e) => button.PerformClick();
}
button.DialogResult = DialogResult.OK; break;
case "Cancel":
this.CancelButton = button;
button.DialogResult = DialogResult.Cancel; break;
case "&Yes":
button.DialogResult = DialogResult.Yes; break;
case "&No":
button.DialogResult = DialogResult.No; break;
case "&Abort":
button.DialogResult = DialogResult.Abort; break;
case "&Retry":
button.DialogResult = DialogResult.Retry; break;
case "&Ignore":
button.DialogResult = DialogResult.Ignore; break;
}
this.Shown += (sender, e) =>
{
if(button.DialogResult == defaultResult) button.Focus();
};
}
this.CloseBox = this.CancelButton != null;
}
private void InitializeComponents(string[] buttonTexts, Image boxImage)
{
this.SuspendLayout();
int w1 = 36.DpiZoom();
Size buttonSize = new Size(75, 27).DpiZoom();
for(int i = 0; i < buttonTexts.Length; i++)
{
Button button = new Button
{
Margin = new Padding(12, 0, 0, 0).DpiZoom(),
Text = buttonTexts[i],
Parent = flpButtons,
AutoSize = true,
};
button.Width = Math.Max(buttonSize.Width, button.Width);
button.Height = Math.Max(buttonSize.Height, button.Height);
buttonSize = button.Size;
w1 += button.Width + button.Margin.Horizontal;
}
picIcon.Image = boxImage;
if(boxImage == null)
{
picIcon.Visible = false;
lblText.Left = picIcon.Left;
}
pnlInfo.Controls.AddRange(new Control[] { picIcon, lblText });
this.Controls.AddRange(new Control[] { pnlInfo, flpButtons });
pnlInfo.Height = lblText.Height + lblText.Top * 2;
if(picIcon.Height > lblText.Height / 2)
{
picIcon.Top = (pnlInfo.Height - picIcon.Height) / 2;
}
int w2 = lblText.Right + picIcon.Left;
int w = Math.Max(w1, w2);
int h = pnlInfo.Height + flpButtons.Height;
this.ClientSize = new Size(w, h);
this.ResumeLayout();
}
readonly FlowLayoutPanel flpButtons = new FlowLayoutPanel
{
FlowDirection = FlowDirection.RightToLeft,
Padding = new Padding(12.DpiZoom()),
Dock = DockStyle.Bottom,
Height = 50.DpiZoom(),
WrapContents = false,
};
readonly Panel pnlInfo = new Panel
{
BackColor = Color.White,
Dock = DockStyle.Top,
};
readonly PictureBox picIcon = new PictureBox
{
SizeMode = PictureBoxSizeMode.AutoSize,
Location = new Point(32, 32).DpiZoom(),
};
readonly Label lblText = new Label
{
Location = new Point(68, 32).DpiZoom(),
AutoSize = true,
};
readonly bool CloseBox = true;//关闭按钮可用性
readonly bool CanMoveParent = true;//可移动父窗体
protected override CreateParams CreateParams
{
get
{
const int CP_NOCLOSE_BUTTON = 0x200;
CreateParams cp = base.CreateParams;
if(!CloseBox) cp.ClassStyle |= CP_NOCLOSE_BUTTON; //禁用关闭按钮
return cp;
}
}
protected override void OnLoad(EventArgs e)
{
if(this.Owner == null && Form.ActiveForm != this) this.Owner = Form.ActiveForm;
if(this.Owner == null) this.StartPosition = FormStartPosition.CenterScreen;
else
{
this.TopMost = this.Owner.TopMost;
this.StartPosition = FormStartPosition.CenterParent;
if(this.CanMoveParent) this.MoveAsMove(this.Owner);
}
base.OnLoad(e);
}
}
}
public static class MessageBoxImage
{
// SystemIcons 里面的图标不是扁平的,❌、⚠️、❔、ℹ️
public static readonly Image Error = GetImage(-98);
public static readonly Image Warning = GetImage(-84);
public static readonly Image Question = GetImage(-99);
public static readonly Image Information = GetImage(-81);
private static Image GetImage(int index)
{
using(Icon icon = ResourceIcon.GetIcon("imageres.dll", index))
{
return icon?.ToBitmap();
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs
================================================
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
namespace BluePointLilac.Methods
{
/// 获取TrustedInstaller权限注册表项的所有权
/// 代码作者:JPBlanc(原作者)、蓝点lilac(转载、修改)
/// 代码原文:https://gist.github.com/JPBlanc/ca0e4f1830e4ca18a526#file-write_a_registry_own_by_trustedinstaller-cs
public class RegTrustedInstaller
{
static class NativeMethod
{
public const string TakeOwnership = "SeTakeOwnershipPrivilege";
public const string Restore = "SeRestorePrivilege";
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public int lowPart;
public int highPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
[Flags]
public enum PrivilegeAttributes
{
/// 特权被禁用.
Disabled = 0,
/// 默认特权.
EnabledByDefault = 1,
/// 特权被激活.
Enabled = 2,
/// 特权被废除.
Removed = 4,
/// 用于访问对象或服务的特权.
UsedForAccess = -2147483648
}
[Flags]
public enum TokenAccessRights
{
/// 向进程附加主令牌的权限.
AssignPrimary = 0,
/// 复制访问令牌的权利.
Duplicate = 1,
/// 向进程附加模拟访问令牌的权限.
Impersonate = 4,
/// 查询访问令牌的权利.
Query = 8,
/// 有权查询访问令牌的来源.
QuerySource = 16,
/// 启用或禁用访问令牌中的特权的权限.
AdjustPrivileges = 32,
/// 调整访问令牌中的组属性的权限.
AdjustGroups = 64,
/// 更改访问令牌的默认所有者、主组或DACL的权限.
AdjustDefault = 128,
/// 正确调整访问令牌的会话ID.
AdjustSessionId = 256,
/// 为令牌组合所有可能的访问权限.
AllAccess = AccessTypeMasks.StandardRightsRequired | AssignPrimary | Duplicate | Impersonate
| Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionId,
/// 结合需要阅读的标准权利
Read = AccessTypeMasks.StandardRightsRead | Query,
/// 组合了写入所需的标准权限
Write = AccessTypeMasks.StandardRightsWrite | AdjustPrivileges | AdjustGroups | AdjustDefault,
/// 合并执行所需的标准权限
Execute = AccessTypeMasks.StandardRightsExecute | Impersonate
}
[Flags]
private enum AccessTypeMasks
{
Delete = 65536,
ReadControl = 131072,
WriteDAC = 262144,
WriteOwner = 524288,
Synchronize = 1048576,
StandardRightsRequired = 983040,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 2031616,
SpecificRightsAll = 65535
}
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AdjustTokenPrivileges([In] IntPtr accessTokenHandle, [In] bool disableAllPrivileges,
[In] ref TOKEN_PRIVILEGES newState, [In] int bufferLength, [In, Out] ref TOKEN_PRIVILEGES previousState, [In, Out] ref int returnLength);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool LookupPrivilegeValue([In] string systemName, [In] string name, [In, Out] ref LUID luid);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenProcessToken([In] IntPtr processHandle, [In] TokenAccessRights desiredAccess, [In, Out] ref IntPtr tokenHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetCurrentProcess();
public static bool TrySetPrivilege(string sPrivilege, bool enablePrivilege)
{
bool blRc;
TOKEN_PRIVILEGES newTP = new TOKEN_PRIVILEGES();
TOKEN_PRIVILEGES oldTP = new TOKEN_PRIVILEGES();
LUID luid = new LUID();
int retrunLength = 0;
IntPtr processToken = IntPtr.Zero;
//本地进程令牌恢复
blRc = OpenProcessToken(GetCurrentProcess(), TokenAccessRights.AllAccess, ref processToken);
if(blRc == false) return false;
//恢复特权的唯一标识符空间
blRc = LookupPrivilegeValue(null, sPrivilege, ref luid);
if(blRc == false) return false;
//建立或取消特权
newTP.PrivilegeCount = 1;
newTP.Privileges = new LUID_AND_ATTRIBUTES[64];
newTP.Privileges[0].Luid = luid;
if(enablePrivilege) newTP.Privileges[0].Attributes = (int)PrivilegeAttributes.Enabled;
else newTP.Privileges[0].Attributes = (int)PrivilegeAttributes.Disabled;
oldTP.PrivilegeCount = 64;
oldTP.Privileges = new LUID_AND_ATTRIBUTES[64];
blRc = AdjustTokenPrivileges(processToken, false, ref newTP, 16, ref oldTP, ref retrunLength);
if(blRc == false) { GetLastError(); return false; }
return true;
}
}
/// 获取注册表项权限
/// 将注册表项所有者改为当前管理员用户
/// 要获取权限的注册表完整路径
public static void TakeRegKeyOwnerShip(string regPath)
{
if(regPath.IsNullOrWhiteSpace()) return;
RegistryKey key = null;
WindowsIdentity id = null;
//利用试错判断是否有写入权限
try { key = RegistryEx.GetRegistryKey(regPath, true); }
catch
{
try
{
//获取当前用户的ID
id = WindowsIdentity.GetCurrent();
//添加TakeOwnership特权
bool flag = NativeMethod.TrySetPrivilege(NativeMethod.TakeOwnership, true);
if(!flag) throw new PrivilegeNotHeldException(NativeMethod.TakeOwnership);
//添加恢复特权(必须这样做才能更改所有者)
flag = NativeMethod.TrySetPrivilege(NativeMethod.Restore, true);
if(!flag) throw new PrivilegeNotHeldException(NativeMethod.Restore);
//打开没有权限的注册表路径
key = RegistryEx.GetRegistryKey(regPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
RegistrySecurity security = key.GetAccessControl(AccessControlSections.All);
//得到真正所有者
//IdentityReference oldId = security.GetOwner(typeof(SecurityIdentifier));
//SecurityIdentifier siTrustedInstaller = new SecurityIdentifier(oldId.ToString());
//使进程用户成为所有者
security.SetOwner(id.User);
key.SetAccessControl(security);
//添加完全控制
RegistryAccessRule fullAccess = new RegistryAccessRule(id.User, RegistryRights.FullControl,
InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
security.AddAccessRule(fullAccess);
key.SetAccessControl(security);
//注册表操作(写入、删除)
//key.SetValue("temp", "");//示例
//恢复原有所有者
//security.SetOwner(siTrustedInstaller);
//key.SetAccessControl(security);
//收回原有权利
//security.RemoveAccessRule(fullAccess);
//key.SetAccessControl(security);
///得到真正所有者、注册表操作、恢复原有所有者、收回原有权利,这四部分在原文中没有被注释掉
///但是如果保留这四部分,会在恢复原有所有者这一步抛出异常,提示没有权限,
///不过我发现经过上面的操作,虽然无法还原所有者权限,但是已经获取了注册表权限
///即已经将TrustedInstaller权限更改为当前管理员用户权限,我要的目的已经达到了
}
catch { }
}
finally { key?.Close(); id?.Dispose(); }
}
/// 获取注册表项及其子项、递归子级子项权限
/// 将注册表项所有者改为当前管理员用户
/// 要获取权限的注册表完整路径
public static void TakeRegTreeOwnerShip(string regPath)
{
if(regPath.IsNullOrWhiteSpace()) return;
TakeRegKeyOwnerShip(regPath);
try
{
using(RegistryKey key = RegistryEx.GetRegistryKey(regPath))
{
if(key == null) return;
foreach(string subKeyName in key.GetSubKeyNames())
{
TakeRegTreeOwnerShip($@"{key.Name}\{subKeyName}");
}
}
}
catch { }
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs
================================================
using Microsoft.Win32;
using System;
using System.Security.AccessControl;
namespace BluePointLilac.Methods
{
public static class RegistryEx
{
public const string CLASSES_ROOT = "HKEY_CLASSES_ROOT";
public const string CURRENT_USER = "HKEY_CURRENT_USER";
public const string LOCAL_MACHINE = "HKEY_LOCAL_MACHINE";
public const string CURRENT_CONFIG = "HKEY_CURRENT_CONFIG";
public const string USERS = "HKEY_USERS";
public const string HKCR = "HKCR";
public const string HKCU = "HKCU";
public const string HKLM = "HKLM";
public const string HKCC = "HKCC";
public const string HKU = "HKU";
public static void CopyTo(this RegistryKey srcKey, RegistryKey dstKey)
{
foreach(string name in srcKey.GetValueNames())
{
dstKey.SetValue(name, srcKey.GetValue(name), srcKey.GetValueKind(name));
}
foreach(string name in srcKey.GetSubKeyNames())
{
using(RegistryKey srcSubKey = srcKey.OpenSubKey(name))
using(RegistryKey dstSubKey = dstKey.CreateSubKey(name, true))
srcSubKey.CopyTo(dstSubKey);
}
}
public static void CopyTo(string srcPath, string dstPath)
{
using(RegistryKey srcKey = GetRegistryKey(srcPath))
using(RegistryKey dstKey = GetRegistryKey(dstPath, true, true))
{
CopyTo(srcKey, dstKey);
}
}
public static void MoveTo(this RegistryKey srcKey, RegistryKey dstKey)
{
CopyTo(srcKey, dstKey);
DeleteKeyTree(srcKey.Name, true);
}
public static void MoveTo(string srcPath, string dstPath)
{
CopyTo(srcPath, dstPath);
DeleteKeyTree(srcPath, true);
}
public static RegistryKey CreateSubKey(this RegistryKey key, string subKeyName, bool writable)
{
using(key.CreateSubKey(subKeyName))
return key.OpenSubKey(subKeyName, writable);
}
/// 获取指定路径注册表项的上一级路径
public static string GetParentPath(string regPath) => regPath.Substring(0, regPath.LastIndexOf('\\'));
/// 获取指定路径注册表项的项名
public static string GetKeyName(string regPath) => regPath.Substring(regPath.LastIndexOf('\\') + 1);
/// 获取指定路径注册表项的根项项名
public static string GetRootName(string regPath) => regPath.Substring(0, regPath.IndexOf('\\'));
/// 获取不包含根项部分的注册表路径
public static string GetPathWithoutRoot(string regPath) => regPath.Substring(regPath.IndexOf('\\') + 1);
/// 删除指定路径的注册表项的指定名称的键值
/// 注册表项路径
/// 要删除的键值名称
/// 找不到键值时是否抛出异常
public static void DeleteValue(string regPath, string valueName, bool throwOnMissingValue = false)
{
GetRegistryKey(regPath, true)?.DeleteValue(valueName, throwOnMissingValue);
}
/// 递归删除指定注册表项及所有子项
/// 注册表路径
/// 找不到注册表项或者没有操作权限时是否抛出异常
public static void DeleteKeyTree(string regPath, bool throwOnMissingKey = false)
{
string dirPath = GetParentPath(regPath);
string keyName = GetKeyName(regPath);
try
{
GetRegistryKey(dirPath, true)?.DeleteSubKeyTree(keyName);
}
catch(Exception)
{
if(throwOnMissingKey) throw;
}
}
/// 获取指定注册表路径的根项RegistryKey和不包含根项部分的注册表路径
/// 注册表路径
/// 成功解析返回一个RegistryKey,否则抛出异常
/// 不包含根项的注册表路径
public static void GetRootAndSubRegPath(string regPath, out RegistryKey root, out string subRegPath)
{
string rootPath;
int index = regPath.IndexOf('\\');
if(index > 0)
{
rootPath = regPath.Substring(0, index).ToUpper();
subRegPath = regPath.Substring(index + 1);
}
else
{
rootPath = regPath;
subRegPath = string.Empty;
}
switch(rootPath)
{
case HKCR:
case CLASSES_ROOT:
root = Registry.ClassesRoot;
break;
case HKCU:
case CURRENT_USER:
root = Registry.CurrentUser;
break;
case HKLM:
case LOCAL_MACHINE:
root = Registry.LocalMachine;
break;
case HKU:
case USERS:
root = Registry.Users;
break;
case HKCC:
case CURRENT_CONFIG:
root = Registry.CurrentConfig;
break;
default:
throw new ArgumentNullException(regPath);
}
}
/// 获取指定注册表项路径的RegistryKey
/// 注册表项路径
/// 写入访问权限
/// 是否创建新项
public static RegistryKey GetRegistryKey(string regPath, bool writable = false, bool create = false)
{
GetRootAndSubRegPath(regPath, out RegistryKey root, out string keyPath);
using(root)
{
if(create) return root.CreateSubKey(keyPath, writable);
else
{
RegTrustedInstaller.TakeRegTreeOwnerShip(keyPath);
return root.OpenSubKey(keyPath, writable);
}
}
}
public static RegistryKey GetRegistryKey(string regPath, RegistryKeyPermissionCheck check, RegistryRights rights)
{
GetRootAndSubRegPath(regPath, out RegistryKey root, out string keyPath);
using(root) return root.OpenSubKey(keyPath, check, rights);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs
================================================
using Microsoft.Win32;
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class ResourceIcon
{
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int ExtractIconEx(string lpFileName, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, uint nIcons);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hLibModule);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr LoadImage(IntPtr hInst, string lpFileName, uint uType, int cx, int cy, uint fuLoad);
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, FileInfoFlags uFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
[Flags]
public enum FileInfoFlags : uint
{
SHGFI_ICON = 0x000000100, // get icon
SHGFI_DISPLAYNAME = 0x000000200, // get display name
SHGFI_TYPENAME = 0x000000400, // get type name
SHGFI_ATTRIBUTES = 0x000000800, // get attributes
SHGFI_ICONLOCATION = 0x000001000, // get icon location
SHGFI_EXETYPE = 0x000002000, // return exe type
SHGFI_SYSICONINDEX = 0x000004000, // get system icon index
SHGFI_LINKOVERLAY = 0x000008000, // put a link overlay on icon
SHGFI_SELECTED = 0x000010000, // show icon in selected state
SHGFI_ATTR_SPECIFIED = 0x000020000, // get only specified attributes
SHGFI_LARGEICON = 0x000000000, // get large icon
SHGFI_SMALLICON = 0x000000001, // get small icon
SHGFI_OPENICON = 0x000000002, // get open icon
SHGFI_SHELLICONSIZE = 0x000000004, // get shell size icon
SHGFI_PIDL = 0x000000008, // pszPath is a pidl
SHGFI_USEFILEATTRIBUTES = 0x000000010, // use passed dwFileAttribute
SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays
SHGFI_OVERLAYINDEX = 0x000000040 // get the index of the overlay in the upper 8 bits of the iIcon
}
/// 获取文件类型的关联图标
/// 文件类型的扩展名,如.txt
/// 获取到的图标
public static Icon GetExtensionIcon(string extension)
{
FileInfoFlags flags = FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_LARGEICON | FileInfoFlags.SHGFI_USEFILEATTRIBUTES;
return GetIcon(extension, flags);
}
/// 获取文件夹、磁盘驱动器的图标
/// 文件夹或磁盘驱动器路径
/// 获取到的图标
public static Icon GetFolderIcon(string folderPath)
{
FileInfoFlags flags = FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_LARGEICON;
return GetIcon(folderPath, flags);
}
/// 根据文件信息标志提取指定文件路径的图标
/// 文件路径
/// 文件信息标志
/// 获取到的图标
public static Icon GetIcon(string filePath, FileInfoFlags flags)
{
SHFILEINFO info = new SHFILEINFO();
IntPtr hInfo = SHGetFileInfo(filePath, 0, ref info, (uint)Marshal.SizeOf(info), flags);
if(hInfo.Equals(IntPtr.Zero)) return null;
Icon icon = (Icon)Icon.FromHandle(info.hIcon).Clone();
DestroyIcon(info.hIcon); //释放资源
return icon;
}
/// 获取指定位置的图标
/// 图标位置
/// 获取到的图标
public static Icon GetIcon(string iconLocation)
{
return GetIcon(iconLocation, out _, out _);
}
/// 获取指定位置的图标
/// 图标位置
/// 返回图标文件路径
/// 返回图标索引
/// 获取到的图标
public static Icon GetIcon(string iconLocation, out string iconPath, out int iconIndex)
{
iconIndex = 0; iconPath = null;
if(iconLocation.IsNullOrWhiteSpace()) return null;
iconLocation = Environment.ExpandEnvironmentVariables(iconLocation).Replace("\"", "");
int index = iconLocation.LastIndexOf(',');
if(index == -1) iconPath = iconLocation;
else
{
if(File.Exists(iconLocation)) iconPath = iconLocation;
else
{
bool flag = int.TryParse(iconLocation.Substring(index + 1), out iconIndex);
iconPath = flag ? iconLocation.Substring(0, index) : null;
}
}
return GetIcon(iconPath, iconIndex);
}
/// 获取指定文件中指定索引的图标
/// 图标文件路径
/// 图标索引
/// 获取到的图标
public static Icon GetIcon(string iconPath, int iconIndex)
{
Icon icon = null;
if(iconPath.IsNullOrWhiteSpace()) return icon;
iconPath = Environment.ExpandEnvironmentVariables(iconPath).Replace("\"", "");
if(Path.GetFileName(iconPath).ToLower() == "shell32.dll")
{
iconPath = "shell32.dll";//系统强制文件重定向
icon = GetReplacedShellIcon(iconIndex);//注册表图标重定向
if(icon != null) return icon;
}
IntPtr hInst = IntPtr.Zero;
IntPtr[] hIcons = new[] { IntPtr.Zero };
//iconIndex为负数就是指定资源标识符, 为正数就是该图标在资源文件中的顺序序号, 为-1时不能使用ExtractIconEx提取图标
if(iconIndex == -1)
{
hInst = LoadLibrary(iconPath);
hIcons[0] = LoadImage(hInst, "#1", 1, SystemInformation.IconSize.Width, SystemInformation.IconSize.Height, 0);
}
else ExtractIconEx(iconPath, iconIndex, hIcons, null, 1);
try { icon = (Icon)Icon.FromHandle(hIcons[0]).Clone(); }
catch { icon = null; }
finally { DestroyIcon(hIcons[0]); FreeLibrary(hInst); }//释放资源
return icon;
}
private const string ShellIconPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons";
/// 获取shell32.dll中的图标被替换后的图标
/// 图标索引
/// 获取到的图标
public static Icon GetReplacedShellIcon(int iconIndex)
{
string iconLocation = Registry.GetValue(ShellIconPath, iconIndex.ToString(), null)?.ToString();
if(iconLocation != null) return GetIcon(iconLocation) ?? GetIcon("imageres.dll", 2);
else return null;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ResourceString.cs
================================================
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace BluePointLilac.Methods
{
public static class ResourceString
{
//MSDN文档: https://docs.microsoft.com/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring
//提取.pri文件资源: https://docs.microsoft.com/windows/uwp/app-resources/makepri-exe-command-options
//.pri转储.xml资源列表: MakePri.exe dump /if [priPath] /of [xmlPath]
[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode,
ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
private static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved);
/// 获取格式为"@[filename],-[strID]"或"@{[packageName]?ms-resource://[resPath]}"的直接字符串
/// 要转换的字符串
/// resStr为Null时返回值为string.Empty; resStr首字符为@但解析失败时返回string.Empty
/// [fileName]:文件路径; [strID]:字符串资源索引; [packageName]:UWP带版本号包名; [resPath]:pri资源路径
public static string GetDirectString(string resStr)
{
StringBuilder outBuff = new StringBuilder(1024);
SHLoadIndirectString(resStr, outBuff, 1024, IntPtr.Zero);
return outBuff.ToString();
}
public static readonly string OK = GetDirectString("@shell32.dll,-9752");
public static readonly string Cancel = GetDirectString("@shell32.dll,-9751");
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs
================================================
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Xml.Linq;
namespace BluePointLilac.Methods
{
public static class RichTextBoxExtension
{
/// RichTextBox中ini语法高亮
/// 要显示的ini文本
public static void LoadIni(this RichTextBox box, string iniStr)
{
string[] lines = iniStr.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
for(int i = 0; i < lines.Length; i++)
{
string str = lines[i].Trim();
if(str.StartsWith(";") || str.StartsWith("#"))
{
box.AppendText(str, Color.SkyBlue);//注释
}
else if(str.StartsWith("["))
{
if(str.Contains("]"))
{
int index = str.IndexOf(']');
box.AppendText(str.Substring(0, index + 1), Color.DarkCyan, null, true);//section
box.AppendText(str.Substring(index + 1), Color.SkyBlue);//section标签之后的内容视作注释
}
else box.AppendText(str, Color.SkyBlue);//section标签未关闭视作注释
}
else if(str.Contains("="))
{
int index = str.IndexOf('=');
box.AppendText(str.Substring(0, index), Color.DodgerBlue);//key
box.AppendText(str.Substring(index), Color.DimGray);//value
}
else box.AppendText(str, Color.SkyBlue);//非section行和非key行视作注释
if(i != lines.Length - 1) box.AppendText("\r\n");
}
}
/// 代码原文:https://archive.codeplex.com/?p=xmlrichtextbox
/// 本人(蓝点lilac)仅作简单修改,将原继承类改写为扩展方法
/// RichTextBox中xml语法高亮
/// 要显示的xml文本
/// 可直接用WebBrowser的Url加载本地xml文件,但无法自定义颜色
public static void LoadXml(this RichTextBox box, string xmlStr)
{
XmlStateMachine machine = new XmlStateMachine();
if(xmlStr.StartsWith(""))
{
string declaration = machine.GetXmlDeclaration(xmlStr);
try
{
xmlStr = XDocument.Parse(xmlStr, LoadOptions.PreserveWhitespace).ToString().Trim();
if(string.IsNullOrEmpty(xmlStr) && declaration == string.Empty) return;
}
catch { throw; }
xmlStr = declaration + "\r\n" + xmlStr;
}
int location = 0;
int failCount = 0;
int tokenTryCount = 0;
while(location < xmlStr.Length)
{
string token = machine.GetNextToken(xmlStr.Substring(location), out XmlTokenType ttype);
Color color = machine.GetTokenColor(ttype);
bool isBold = ttype == XmlTokenType.DocTypeName || ttype == XmlTokenType.NodeName;
box.AppendText(token, color, null, isBold);
location += token.Length;
tokenTryCount++;
// Check for ongoing failure
if(token.Length == 0) failCount++;
if(failCount > 10 || tokenTryCount > xmlStr.Length)
{
string theRestOfIt = xmlStr.Substring(location, xmlStr.Length - location);
//box.AppendText(Environment.NewLine + Environment.NewLine + theRestOfIt); // DEBUG
box.AppendText(theRestOfIt);
break;
}
}
}
public static void AppendText(this RichTextBox box, string text, Color color = default, Font font = null, bool isBold = false)
{
FontStyle fontStyle = isBold ? FontStyle.Bold : FontStyle.Regular;
box.SelectionFont = new Font(font ?? box.Font, fontStyle);
box.SelectionColor = color != default ? color : box.ForeColor;
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
}
sealed class XmlStateMachine
{
public XmlTokenType CurrentState = XmlTokenType.Unknown;
private string subString = string.Empty;
private string token = string.Empty;
public string GetNextToken(string s, out XmlTokenType ttype)
{
ttype = XmlTokenType.Unknown;
// skip past any whitespace (token added to it at the end of method)
string whitespace = GetWhitespace(s);
subString = s.TrimStart();
token = string.Empty;
if(CurrentState == XmlTokenType.CDataStart)
{
// check for empty CDATA
if(subString.StartsWith("]]>"))
{
CurrentState = XmlTokenType.CDataEnd;
token = "]]>";
}
else
{
CurrentState = XmlTokenType.CDataValue;
int n = subString.IndexOf("]]>");
token = subString.Substring(0, n);
}
}
else if(CurrentState == XmlTokenType.DocTypeStart)
{
CurrentState = XmlTokenType.DocTypeName;
token = "DOCTYPE";
}
else if(CurrentState == XmlTokenType.DocTypeName)
{
CurrentState = XmlTokenType.DocTypeDeclaration;
int n = subString.IndexOf("[");
token = subString.Substring(0, n);
}
else if(CurrentState == XmlTokenType.DocTypeDeclaration)
{
CurrentState = XmlTokenType.DocTypeDefStart;
token = "[";
}
else if(CurrentState == XmlTokenType.DocTypeDefStart)
{
if(subString.StartsWith("]>"))
{
CurrentState = XmlTokenType.DocTypeDefEnd;
token = "]>";
}
else
{
CurrentState = XmlTokenType.DocTypeDefValue;
int n = subString.IndexOf("]>");
token = subString.Substring(0, n);
}
}
else if(CurrentState == XmlTokenType.DocTypeDefValue)
{
CurrentState = XmlTokenType.DocTypeDefEnd;
token = "]>";
}
else if(CurrentState == XmlTokenType.DoubleQuotationMarkStart)
{
// check for empty attribute value
if(subString[0] == '\"')
{
CurrentState = XmlTokenType.DoubleQuotationMarkEnd;
token = "\"";
}
else
{
CurrentState = XmlTokenType.AttributeValue;
int n = subString.IndexOf("\"");
token = subString.Substring(0, n);
}
}
else if(CurrentState == XmlTokenType.SingleQuotationMarkStart)
{
// check for empty attribute value
if(subString[0] == '\'')
{
CurrentState = XmlTokenType.SingleQuotationMarkEnd;
token = "\'";
}
else
{
CurrentState = XmlTokenType.AttributeValue;
int n = subString.IndexOf("'");
token = subString.Substring(0, n);
}
}
else if(CurrentState == XmlTokenType.CommentStart)
{
// check for empty comment
if(subString.StartsWith("-->"))
{
CurrentState = XmlTokenType.CommentEnd;
token = "-->";
}
else
{
CurrentState = XmlTokenType.CommentValue;
token = ReadCommentValue(subString);
}
}
else if(CurrentState == XmlTokenType.NodeStart)
{
CurrentState = XmlTokenType.NodeName;
token = ReadNodeName(subString);
}
else if(CurrentState == XmlTokenType.XmlDeclarationStart)
{
CurrentState = XmlTokenType.NodeName;
token = ReadNodeName(subString);
}
else if(CurrentState == XmlTokenType.NodeName)
{
if(subString[0] != '/' &&
subString[0] != '>')
{
CurrentState = XmlTokenType.AttributeName;
token = ReadAttributeName(subString);
}
else
{
HandleReservedXmlToken();
}
}
else if(CurrentState == XmlTokenType.NodeEndValueStart)
{
if(subString[0] == '<')
{
HandleReservedXmlToken();
}
else
{
CurrentState = XmlTokenType.NodeValue;
token = ReadNodeValue(subString);
}
}
else if(CurrentState == XmlTokenType.DoubleQuotationMarkEnd)
{
HandleAttributeEnd();
}
else if(CurrentState == XmlTokenType.SingleQuotationMarkEnd)
{
HandleAttributeEnd();
}
else
{
HandleReservedXmlToken();
}
if(token != string.Empty)
{
ttype = CurrentState;
return whitespace + token;
}
return string.Empty;
}
public Color GetTokenColor(XmlTokenType ttype)
{
switch(ttype)
{
case XmlTokenType.NodeValue:
case XmlTokenType.EqualSignStart:
case XmlTokenType.EqualSignEnd:
case XmlTokenType.DoubleQuotationMarkStart:
case XmlTokenType.DoubleQuotationMarkEnd:
case XmlTokenType.SingleQuotationMarkStart:
case XmlTokenType.SingleQuotationMarkEnd:
return Color.DimGray;
case XmlTokenType.XmlDeclarationStart:
case XmlTokenType.XmlDeclarationEnd:
case XmlTokenType.NodeStart:
case XmlTokenType.NodeEnd:
case XmlTokenType.NodeEndValueStart:
case XmlTokenType.CDataStart:
case XmlTokenType.CDataEnd:
case XmlTokenType.CommentStart:
case XmlTokenType.CommentEnd:
case XmlTokenType.AttributeValue:
case XmlTokenType.DocTypeStart:
case XmlTokenType.DocTypeEnd:
case XmlTokenType.DocTypeDefStart:
case XmlTokenType.DocTypeDefEnd:
return Color.DimGray;
case XmlTokenType.CDataValue:
case XmlTokenType.DocTypeDefValue:
return Color.SkyBlue;
case XmlTokenType.CommentValue:
return Color.SkyBlue;
case XmlTokenType.DocTypeName:
case XmlTokenType.NodeName:
return Color.DarkCyan;
case XmlTokenType.AttributeName:
case XmlTokenType.DocTypeDeclaration:
return Color.DodgerBlue;
default:
return Color.DimGray;
}
}
public string GetXmlDeclaration(string s)
{
int start = s.IndexOf("");
int end = s.IndexOf("?>");
if(start > -1 && end > start)
{
return s.Substring(start, end - start + 2);
}
return string.Empty;
}
private void HandleAttributeEnd()
{
if(subString.StartsWith(">"))
{
HandleReservedXmlToken();
}
else if(subString.StartsWith("/>"))
{
HandleReservedXmlToken();
}
else if(subString.StartsWith("?>"))
{
HandleReservedXmlToken();
}
else
{
CurrentState = XmlTokenType.AttributeName;
token = ReadAttributeName(subString);
}
}
private void HandleReservedXmlToken()
{
// check if state changer
// <, >, =, , />,
if(subString.StartsWith(""))
{
CurrentState = XmlTokenType.XmlDeclarationEnd;
token = "?>";
}
else if(subString.StartsWith(">"))
{
CurrentState = XmlTokenType.NodeEndValueStart;
token = ">";
}
else if(subString.StartsWith("-->"))
{
CurrentState = XmlTokenType.CommentEnd;
token = "-->";
}
else if(subString.StartsWith("]>"))
{
CurrentState = XmlTokenType.DocTypeEnd;
token = "]>";
}
else if(subString.StartsWith("]]>"))
{
CurrentState = XmlTokenType.CDataEnd;
token = "]]>";
}
else if(subString.StartsWith("/>"))
{
CurrentState = XmlTokenType.NodeEnd;
token = "/>";
}
else if(subString.StartsWith("\""))
{
if(CurrentState == XmlTokenType.AttributeValue)
{
CurrentState = XmlTokenType.DoubleQuotationMarkEnd;
}
else
{
CurrentState = XmlTokenType.DoubleQuotationMarkStart;
}
token = "\"";
}
else if(subString.StartsWith("'"))
{
if(CurrentState == XmlTokenType.AttributeValue)
{
CurrentState = XmlTokenType.SingleQuotationMarkEnd;
}
else
{
CurrentState = XmlTokenType.SingleQuotationMarkStart;
}
token = "'";
}
}
private string ReadNodeName(string s)
{
string nodeName = "";
for(int i = 0; i < s.Length; i++)
{
if(s[i] == '/' || s[i] == ' ' || s[i] == '>') return nodeName;
else nodeName += s[i].ToString();
}
return nodeName;
}
private string ReadAttributeName(string s)
{
string attName = "";
int n = s.IndexOf('=');
if(n != -1) attName = s.Substring(0, n);
return attName;
}
private string ReadNodeValue(string s)
{
string nodeValue = "";
int n = s.IndexOf('<');
if(n != -1) nodeValue = s.Substring(0, n);
return nodeValue;
}
private string ReadCommentValue(string s)
{
string commentValue = "";
int n = s.IndexOf("-->");
if(n != -1) commentValue = s.Substring(0, n);
return commentValue;
}
private string GetWhitespace(string s)
{
string whitespace = "";
for(int i = 0; i < s.Length; i++)
{
char c = s[i];
if(char.IsWhiteSpace(c)) whitespace += c;
else break;
}
return whitespace;
}
}
enum XmlTokenType
{
Whitespace, XmlDeclarationStart, XmlDeclarationEnd, NodeStart, NodeEnd, NodeEndValueStart, NodeName,
NodeValue, AttributeName, AttributeValue, EqualSignStart, EqualSignEnd, CommentStart, CommentValue,
CommentEnd, CDataStart, CDataValue, CDataEnd, DoubleQuotationMarkStart, DoubleQuotationMarkEnd,
SingleQuotationMarkStart, SingleQuotationMarkEnd, DocTypeStart, DocTypeName, DocTypeDeclaration,
DocTypeDefStart, DocTypeDefValue, DocTypeDefEnd, DocTypeEnd, DocumentEnd, Unknown
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ShellLink.cs
================================================
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using ComTypes = System.Runtime.InteropServices.ComTypes;
namespace BluePointLilac.Methods
{
public class ShellLink : IDisposable
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
interface IShellLinkW
{
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags);
void GetIDList(out IntPtr ppidl);
void SetIDList(IntPtr pidl);
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
void GetHotKey(out ushort pwHotkey);
void SetHotKey(ushort wHotKey);
void GetShowCmd(out int piShowCmd);
void SetShowCmd(int iShowCmd);
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
void Resolve(IntPtr hwnd, uint fFlags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[ComImport]
[ClassInterface(ClassInterfaceType.None)]
[Guid("00021401-0000-0000-C000-000000000046")]
private class CShellLink { }
[ComImport]
[Guid("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IShellLinkDataList
{
void AddDataBlock(IntPtr pDataBlock);
void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
void RemoveDataBlock(uint dwSig);
void GetFlags(out ShellLinkDataFlags pdwFlags);
void SetFlags(ShellLinkDataFlags dwFlags);
}
[Flags]
enum ShellLinkDataFlags : uint
{
Default = 0x00000000,
HasIdList = 0x00000001,
HasLinkInfo = 0x00000002,
HasName = 0x00000004,
HasRelpath = 0x00000008,
HasWorkingdir = 0x00000010,
HasArgs = 0x00000020,
HasIconLocation = 0x00000040,
Unicode = 0x00000080,
ForceNoLinkInfo = 0x00000100,
HasExpSz = 0x00000200,
RunInSeparate = 0x00000400,
HasLogo3Id = 0x00000800,
HasDarwinId = 0x00001000,
RunasUser = 0x00002000,
HasExpIconSz = 0x00004000,
NoPidlAlias = 0x00008000,
ForceUncname = 0x00010000,
RunWithShimlayer = 0x00020000,
ForceNoLinktrack = 0x00040000,
EnableTargetMetadata = 0x00080000,
DisableLinkPathTracking = 0x00100000,
DisableKnownfolderRelativeTracking = 0x00200000,
NoKFAlias = 0x00400000,
AllowLinkToLink = 0x00800000,
UnaliasOnSave = 0x01000000,
PreferEnvironmentPath = 0x02000000,
KeepLocalIdListForUncTarget = 0x04000000,
Valid = 0x07fff7ff,
Reserved = 0x80000000
}
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public ComTypes.FILETIME ftCreationTime;
public ComTypes.FILETIME ftLastAccessTime;
public ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public struct ICONLOCATION
{
public string IconPath;
public int IconIndex;
}
private const int MAX_PATH = 260;
private const int INFOTIPSIZE = 1024;
private const uint SLGP_UNCPRIORITY = 2;
private const int STGM_READWRITE = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMINNOACTIVE = 7;
private const int SW_SHOWNORMAL = 1;
private IShellLinkW shellLinkW = null;
private IPersistFile PersistFile => (IPersistFile)shellLinkW;
private IShellLinkDataList LinkDataList => (IShellLinkDataList)shellLinkW;
public string ShortcutPath { get; private set; }
public string TargetPath
{
get
{
StringBuilder targetPath = new StringBuilder(MAX_PATH);
WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, SLGP_UNCPRIORITY);
return targetPath.ToString();
}
set
{
shellLinkW.SetPath(value);
}
}
public string Arguments
{
get
{
StringBuilder arguments = new StringBuilder(INFOTIPSIZE);
shellLinkW.GetArguments(arguments, arguments.Capacity);
return arguments.ToString();
}
set
{
shellLinkW.SetArguments(value);
}
}
public string WorkingDirectory
{
get
{
StringBuilder dirPath = new StringBuilder(MAX_PATH);
shellLinkW.GetWorkingDirectory(dirPath, dirPath.Capacity);
return dirPath.ToString();
}
set
{
shellLinkW.SetWorkingDirectory(value);
}
}
public ICONLOCATION IconLocation
{
get
{
StringBuilder iconPath = new StringBuilder(MAX_PATH);
shellLinkW.GetIconLocation(iconPath, iconPath.Capacity, out int iconIndex);
return new ICONLOCATION { IconPath = iconPath.ToString(), IconIndex = iconIndex };
}
set
{
shellLinkW.SetIconLocation(value.IconPath, value.IconIndex);
}
}
public string IconPath => IconLocation.IconPath;
public int IconIndex => IconLocation.IconIndex;
public string Description
{
get
{
StringBuilder description = new StringBuilder(INFOTIPSIZE);
shellLinkW.GetDescription(description, description.Capacity);
return description.ToString();
}
set
{
shellLinkW.SetDescription(value);
}
}
public Keys HotKey
{
get
{
shellLinkW.GetHotKey(out ushort key);
int hotKey = ((key & 0xFF00) << 8) | (key & 0xFF);
return (Keys)hotKey;
}
set
{
if((value & Keys.Modifiers) == 0) throw new ArgumentException("Hotkey must include a modifier key.");
ushort key = unchecked((ushort)(((int)(value & Keys.Modifiers) >> 8) | (int)(value & Keys.KeyCode)));
shellLinkW.SetHotKey(key);
}
}
public FormWindowState WindowStyle
{
get
{
shellLinkW.GetShowCmd(out int style);
switch(style)
{
case SW_SHOWMINIMIZED:
case SW_SHOWMINNOACTIVE:
return FormWindowState.Minimized;
case SW_SHOWMAXIMIZED:
return FormWindowState.Maximized;
case SW_SHOWNORMAL:
default:
return FormWindowState.Normal;
}
}
set
{
int style;
switch(value)
{
case FormWindowState.Minimized:
style = SW_SHOWMINIMIZED; break;
case FormWindowState.Maximized:
style = SW_SHOWMAXIMIZED; break;
case FormWindowState.Normal:
default:
style = SW_SHOWNORMAL; break;
}
shellLinkW.SetShowCmd(style);
}
}
public bool RunAsAdministrator
{
get
{
LinkDataList.GetFlags(out ShellLinkDataFlags flags);
return (flags & ShellLinkDataFlags.RunasUser) == ShellLinkDataFlags.RunasUser;
}
set
{
LinkDataList.GetFlags(out ShellLinkDataFlags flags);
if(value) flags |= ShellLinkDataFlags.RunasUser;
else flags &= ~ShellLinkDataFlags.RunasUser;
LinkDataList.SetFlags(flags);
}
}
public ShellLink(string lnkPath = null)
{
try { shellLinkW = (IShellLinkW)new CShellLink(); }
catch { throw new COMException("Failed to create ShellLink object."); }
Load(lnkPath);
}
~ShellLink() { Dispose(false); }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(shellLinkW == null) return;
Marshal.FinalReleaseComObject(shellLinkW);
shellLinkW = null;
}
public void Save() { Save(this.ShortcutPath); }
public void Save(string lnkPath)
{
PersistFile.Save(lnkPath, true);
}
public void Load() { Load(this.ShortcutPath); }
public void Load(string lnkPath)
{
this.ShortcutPath = lnkPath;
if(File.Exists(lnkPath)) PersistFile.Load(lnkPath, STGM_READWRITE);
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class SingleInstance
{
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
/// 判断单实例程序是否正在运行
/// 若正在运行激活窗口
public static bool IsRunning()
{
using(Process current = Process.GetCurrentProcess())
{
foreach(Process process in Process.GetProcessesByName(current.ProcessName))
{
using(process)
{
if(process.Id == current.Id) continue;
if(process.MainModule.FileName == current.MainModule.FileName)
{
const int SW_RESTORE = 9;
ShowWindowAsync(process.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(process.MainWindowHandle);
return true;
}
}
}
return false;
}
}
/// 重启单实例程序
/// 重启程序时传入参数
/// 用于更新程序的新版本文件路径,为null则为普通重启
public static void Restart(string[] args = null, string updatePath = null)
{
string appPath = Application.ExecutablePath;
string command = appPath;
if(args != null && args.Length > 0) command += "," + string.Join(" ", args);
List contents = new List();
//vbs命令逐行执行不等待,故加些代码确定上一条命令执行是否完成
contents.AddRange(new[]
{
"On Error Resume Next",
"WScript.Sleep 1000",//等待程序结束
"Dim wsh, fso",
"Set wsh = CreateObject(\"WScript.Shell\")",
"Set fso = CreateObject(\"Scripting.FileSystemObject\")",
});
if(File.Exists(updatePath))
{
contents.AddRange(new[]
{
$"fso.DeleteFile \"{appPath}\"",
$"Do While fso.FileExists(\"{appPath}\")",
"WScript.Sleep 100",
"Loop",//确定文件删除完成
$"fso.MoveFile \"{updatePath}\",\"{appPath}\"",//更新文件
$"Do While fso.FileExists(\"{updatePath}\")",//确定文件已被移动
"WScript.Sleep 100",
$"Loop",
});
}
contents.AddRange(new[]
{
$"wsh.Run \"{command}\"",
"fso.DeleteFile(WScript.ScriptFullName)",//vbs自删命令
"Set wsh = Nothing",
"Set fso = Nothing",
});
string vbsPath = Path.GetTempPath() + Guid.NewGuid() + ".vbs";
File.WriteAllLines(vbsPath, contents.ToArray(), Encoding.Unicode);
Application.Exit();
using(Process process = new Process())
{
process.StartInfo.FileName = "wscript.exe";
process.StartInfo.Arguments = vbsPath;
process.Start();
}
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/StringExtension.cs
================================================
namespace BluePointLilac.Methods
{
//为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的string.IsNullOrWhiteSpace)写了这个扩展方法
public static class StringExtension
{
public static bool IsNullOrWhiteSpace(this string str)
{
if(string.IsNullOrEmpty(str)) return true;
return str.Trim().Length == 0;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs
================================================
using System.Drawing;
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class TextBoxExtension
{
/// TextBox仿RichTextBox按住Ctrl加鼠标滚轮放缩字体
public static void CanResizeFont(this TextBox box)
{
box.MouseWheel += (sender, e) =>
{
if(Control.ModifierKeys != Keys.Control) return;
float size = box.Font.Size;
if(size < 8F && e.Delta < 0) return;
if(size > 40F && e.Delta > 0) return;
box.Font = new Font(box.Font.FontFamily, size + (e.Delta > 0 ? 1F : -1F));
};
}
/// TextBox在文字未超出边界时隐藏滚动条,超出时显示
public static void SetAutoShowScroll(this TextBox box, ScrollBars scrollBars)
{
void SetScrollVisible()
{
Size szBox = box.ClientSize;
Size szText = TextRenderer.MeasureText(box.Text, box.Font);
if((scrollBars | ScrollBars.Vertical) == ScrollBars.Vertical)
{
if(szText.Height > szBox.Height)
{
box.ScrollBars = scrollBars | ScrollBars.Vertical;
}
else
{
box.ScrollBars = scrollBars & ~ScrollBars.Vertical;
}
}
if((scrollBars | ScrollBars.Horizontal) == ScrollBars.Horizontal)
{
if(szText.Width > szBox.Width)
{
box.ScrollBars = scrollBars | ScrollBars.Horizontal;
}
else
{
box.ScrollBars = scrollBars & ~ScrollBars.Horizontal;
}
}
};
box.TextChanged += (sender, e) => SetScrollVisible();
box.FontChanged += (sender, e) => SetScrollVisible();
box.ClientSizeChanged += (sender, e) => SetScrollVisible();
}
/// TextBox只读时可以使用Ctrl+A全选快捷键
public static void CanSelectAllWhenReadOnly(this TextBox box)
{
box.KeyDown += (sender, e) =>
{
if(box.ReadOnly && e.Control && e.KeyCode == Keys.A) box.SelectAll();
};
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/ToolTipBox.cs
================================================
using System.Windows.Forms;
namespace BluePointLilac.Methods
{
public static class ToolTipBox
{
public static void SetToolTip(Control ctr, string tip)
{
if(tip.IsNullOrWhiteSpace()) return;
ToolTip toolTip = new ToolTip { InitialDelay = 1 };
toolTip.SetToolTip(ctr, tip);
ctr.Disposed += (sender, e) => toolTip.Dispose();
}
public static void SetToolTip(ToolStripItem item, string tip)
{
//必须先设置item的Owner
item.Owner.ShowItemToolTips = true;
item.ToolTipText = tip;
}
}
}
================================================
FILE: ContextMenuManager/BluePointLilac.Methods/WinOsVersion.cs
================================================
using System;
namespace BluePointLilac.Methods
{
// 判断Windows系统版本
// https://docs.microsoft.com/windows/release-health/release-information
public static class WinOsVersion
{
public static readonly Version Current = Environment.OSVersion.Version;
public static readonly Version Win10 = new Version(10, 0);
public static readonly Version Win8_1 = new Version(6, 3);
public static readonly Version Win8 = new Version(6, 2);
public static readonly Version Win7 = new Version(6, 1);
public static readonly Version Vista = new Version(6, 0);
public static readonly Version XP = new Version(5, 1);
public static readonly Version Win10_1507 = new Version(10, 0, 10240);
public static readonly Version Win10_1511 = new Version(10, 0, 10586);
public static readonly Version Win10_1607 = new Version(10, 0, 14393);
public static readonly Version Win10_1703 = new Version(10, 0, 15063);
public static readonly Version Win10_1709 = new Version(10, 0, 16299);
public static readonly Version Win10_1803 = new Version(10, 0, 17134);
public static readonly Version Win10_1809 = new Version(10, 0, 17763);
public static readonly Version Win10_1903 = new Version(10, 0, 18362);
public static readonly Version Win10_1909 = new Version(10, 0, 18363);
public static readonly Version Win10_2004 = new Version(10, 0, 19041);
public static readonly Version Win10_20H2 = new Version(10, 0, 19042);
}
}
================================================
FILE: ContextMenuManager/ContextMenuManager.csproj
================================================
Debug
AnyCPU
{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}
WinExe
ContextMenuManager
ContextMenuManager
v4.0
512
true
true
false
C:\Users\Lilac\Desktop\
true
Disk
false
Foreground
7
Days
false
false
true
0
1.0.0.%2a
false
true
true
AnyCPU
true
full
false
bin\Debug\
DEBUG;TRACE
send
4
false
false
Auto
4096
true
false
AnyCPU
none
true
bin\Release\
TRACE
prompt
4
false
4096
false
Auto
Properties\AppIcon.ico
Custom
false
false
ContextMenuManager.Program
BEFE3AE42DB8F7604D715C01388001CABFF0FABF
ContextMenuManager_TemporaryKey.pfx
Properties\App.manifest
Component
Component
Component
Component
Component
Component
Form
Component
Component
Form
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Form
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Component
Form
Component
Component
Component
Component
Form
Component
Component
Component
Component
Component
Component
True
True
Resources.resx
SettingsSingleFileGenerator
Settings.Designer.cs
True
Settings.settings
True
False
Microsoft .NET Framework 4.6 %28x86 和 x64%29
true
False
.NET Framework 3.5 SP1
false
ResXFileCodeGenerator
Resources.Designer.cs
mkdir $(TargetDir)\Config\Languages
mkdir $(TargetDir)\Config\Dictionaries\Web
xcopy $(ProjectDir)\Properties\Resources\Texts\AppLanguageDic.ini $(SolutionDir)\Languages\zh-CN.ini /y
xcopy $(SolutionDir)\Languages $(TargetDir)\config\Languages /y
xcopy $(ProjectDir)\Properties\Resources\Texts $(TargetDir)\Config\Dictionaries\Web /y
del $(TargetDir)\Config\Dictionaries\Web\AppLanguageDic.ini
================================================
FILE: ContextMenuManager/Controls/AppSettingBox.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class AppSettingBox : MyList
{
public AppSettingBox()
{
this.SuspendLayout();
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
mliConfigDir.AddCtrs(new Control[] { cmbConfigDir, btnConfigDir });
mliBackup.AddCtrs(new Control[] { chkBackup, btnBackupDir });
mliUpdate.AddCtrs(new Control[] { cmbUpdate, btnUpdate });
mliRepo.AddCtr(cmbRepo);
mliTopMost.AddCtr(chkTopMost);
mliProtect.AddCtr(chkProtect);
mliEngine.AddCtr(cmbEngine);
mliWinXSortable.AddCtr(chkWinXSortable);
mliShowFilePath.AddCtr(chkShowFilePath);
mliOpenMoreRegedit.AddCtr(chkOpenMoreRegedit);
mliOpenMoreExplorer.AddCtr(chkOpenMoreExplorer);
mliHideDisabledItems.AddCtr(chkHideDisabledItems);
mliHideSysStoreItems.AddCtr(chkHideSysStoreItems);
ToolTipBox.SetToolTip(btnUpdate, AppString.Tip.ImmediatelyCheck);
ToolTipBox.SetToolTip(cmbConfigDir, AppString.Tip.ConfigPath);
ToolTipBox.SetToolTip(btnConfigDir, AppString.Menu.FileLocation);
ToolTipBox.SetToolTip(btnBackupDir, AppString.Menu.FileLocation);
cmbRepo.Items.AddRange(new[] { "Github", "Gitee" });
cmbConfigDir.Items.AddRange(new[] { AppString.Other.AppDataDir, AppString.Other.AppDir });
cmbEngine.Items.AddRange(AppConfig.EngineUrlsDic.Keys.ToArray());
cmbEngine.Items.Add(AppString.Other.CustomEngine);
cmbUpdate.Items.AddRange(new[] { AppString.Other.OnceAWeek, AppString.Other.OnceAMonth,
AppString.Other.OnceASeason, AppString.Other.NeverCheck });
cmbConfigDir.Width = cmbEngine.Width = cmbUpdate.Width = cmbRepo.Width = 120.DpiZoom();
cmbConfigDir.DropDownStyle = cmbEngine.DropDownStyle = cmbUpdate.DropDownStyle
= cmbRepo.DropDownStyle = ComboBoxStyle.DropDownList;
cmbConfigDir.AutosizeDropDownWidth();
cmbEngine.AutosizeDropDownWidth();
cmbUpdate.AutosizeDropDownWidth();
cmbRepo.AutosizeDropDownWidth();
btnUpdate.MouseDown += (sender, e) =>
{
this.Cursor = Cursors.WaitCursor;
Updater.Update(true);
this.Cursor = Cursors.Default;
};
btnConfigDir.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(AppConfig.ConfigDir);
btnBackupDir.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(AppConfig.BackupDir);
chkBackup.CheckChanged += () => AppConfig.AutoBackup = chkBackup.Checked;
chkProtect.CheckChanged += () => AppConfig.ProtectOpenItem = chkProtect.Checked;
chkWinXSortable.CheckChanged += () => AppConfig.WinXSortable = chkWinXSortable.Checked;
chkOpenMoreRegedit.CheckChanged += () => AppConfig.OpenMoreRegedit = chkOpenMoreRegedit.Checked;
chkTopMost.CheckChanged += () => AppConfig.TopMost = this.FindForm().TopMost = chkTopMost.Checked;
chkOpenMoreExplorer.CheckChanged += () => AppConfig.OpenMoreExplorer = chkOpenMoreExplorer.Checked;
chkHideDisabledItems.CheckChanged += () => AppConfig.HideDisabledItems = chkHideDisabledItems.Checked;
chkHideSysStoreItems.CheckChanged += () => AppConfig.HideSysStoreItems = chkHideSysStoreItems.Checked;
cmbRepo.SelectionChangeCommitted += (sender, e) => AppConfig.RequestUseGithub = cmbRepo.SelectedIndex == 0;
chkShowFilePath.CheckChanged += () => AppConfig.ShowFilePath = chkShowFilePath.Checked;
cmbUpdate.SelectionChangeCommitted += (sender, e) => ChangeUpdateFrequency();
cmbConfigDir.SelectionChangeCommitted += (sender, e) => ChangeConfigDir();
cmbEngine.SelectionChangeCommitted += (sender, e) => ChangeEngineUrl();
this.ResumeLayout();
}
readonly MyListItem mliConfigDir = new MyListItem
{
Text = AppString.Other.ConfigPath
};
readonly ComboBox cmbConfigDir = new ComboBox();
readonly PictureButton btnConfigDir = new PictureButton(AppImage.Open);
readonly MyListItem mliRepo = new MyListItem
{
Text = AppString.Other.SetRequestRepo
};
readonly ComboBox cmbRepo = new ComboBox();
readonly MyListItem mliBackup = new MyListItem
{
Text = AppString.Other.AutoBackup
};
readonly MyCheckBox chkBackup = new MyCheckBox();
readonly PictureButton btnBackupDir = new PictureButton(AppImage.Open);
readonly MyListItem mliUpdate = new MyListItem
{
Text = AppString.Other.SetUpdateFrequency
};
readonly ComboBox cmbUpdate = new ComboBox();
readonly PictureButton btnUpdate = new PictureButton(AppImage.CheckUpdate);
readonly MyListItem mliTopMost = new MyListItem
{
Text = AppString.Other.TopMost
};
readonly MyCheckBox chkTopMost = new MyCheckBox();
readonly MyListItem mliProtect = new MyListItem
{
Text = AppString.Other.ProtectOpenItem
};
readonly MyCheckBox chkProtect = new MyCheckBox();
readonly MyListItem mliEngine = new MyListItem
{
Text = AppString.Other.WebSearchEngine
};
readonly ComboBox cmbEngine = new ComboBox();
readonly MyListItem mliShowFilePath = new MyListItem
{
Text = AppString.Other.ShowFilePath
};
readonly MyCheckBox chkShowFilePath = new MyCheckBox();
readonly MyListItem mliOpenMoreRegedit = new MyListItem
{
Text = AppString.Other.OpenMoreRegedit
};
readonly MyCheckBox chkOpenMoreRegedit = new MyCheckBox();
readonly MyListItem mliOpenMoreExplorer = new MyListItem
{
Text = AppString.Other.OpenMoreExplorer
};
readonly MyCheckBox chkOpenMoreExplorer = new MyCheckBox();
readonly MyListItem mliHideDisabledItems = new MyListItem
{
Text = AppString.Other.HideDisabledItems
};
readonly MyCheckBox chkHideDisabledItems = new MyCheckBox();
readonly MyListItem mliWinXSortable = new MyListItem
{
Text = AppString.Other.WinXSortable,
Visible = WinOsVersion.Current >= WinOsVersion.Win8
};
readonly MyCheckBox chkWinXSortable = new MyCheckBox();
readonly MyListItem mliHideSysStoreItems = new MyListItem
{
Text = AppString.Other.HideSysStoreItems,
Visible = WinOsVersion.Current >= WinOsVersion.Win7
};
readonly MyCheckBox chkHideSysStoreItems = new MyCheckBox();
public override void ClearItems()
{
this.Controls.Clear();
}
public void LoadItems()
{
this.AddItems(new[] { mliConfigDir, mliUpdate, mliRepo, mliEngine, mliBackup, mliTopMost, mliProtect, mliShowFilePath,
mliHideDisabledItems, mliHideSysStoreItems, mliOpenMoreRegedit, mliOpenMoreExplorer, mliWinXSortable });
foreach(MyListItem item in this.Controls) item.HasImage = false;
cmbConfigDir.SelectedIndex = AppConfig.SaveToAppDir ? 1 : 0;
cmbRepo.SelectedIndex = AppConfig.RequestUseGithub ? 0 : 1;
cmbUpdate.SelectedIndex = GetUpdateSelectIndex();
cmbEngine.SelectedIndex = GetEngineSelectIndex();
chkBackup.Checked = AppConfig.AutoBackup;
chkTopMost.Checked = this.FindForm().TopMost;
chkProtect.Checked = AppConfig.ProtectOpenItem;
chkWinXSortable.Checked = AppConfig.WinXSortable;
chkShowFilePath.Checked = AppConfig.ShowFilePath;
chkOpenMoreRegedit.Checked = AppConfig.OpenMoreRegedit;
chkOpenMoreExplorer.Checked = AppConfig.OpenMoreExplorer;
chkHideDisabledItems.Checked = AppConfig.HideDisabledItems;
chkHideSysStoreItems.Checked = AppConfig.HideSysStoreItems;
}
private void ChangeConfigDir()
{
string newPath = (cmbConfigDir.SelectedIndex == 0) ? AppConfig.AppDataConfigDir : AppConfig.AppConfigDir;
if(newPath == AppConfig.ConfigDir) return;
if(AppMessageBox.Show(AppString.Message.RestartApp, MessageBoxButtons.OKCancel) != DialogResult.OK)
{
cmbConfigDir.SelectedIndex = AppConfig.SaveToAppDir ? 1 : 0;
}
else
{
DirectoryEx.CopyTo(AppConfig.ConfigDir, newPath);
Directory.Delete(AppConfig.ConfigDir, true);
SingleInstance.Restart();
}
}
private void ChangeEngineUrl()
{
if(cmbEngine.SelectedIndex < cmbEngine.Items.Count - 1)
{
AppConfig.EngineUrl = AppConfig.EngineUrlsDic[cmbEngine.Text];
}
else
{
using(InputDialog dlg = new InputDialog())
{
dlg.Text = AppConfig.EngineUrl;
dlg.Title = AppString.Other.SetCustomEngine;
if(dlg.ShowDialog() == DialogResult.OK) AppConfig.EngineUrl = dlg.Text;
cmbEngine.SelectedIndex = GetEngineSelectIndex();
}
}
}
private void ChangeUpdateFrequency()
{
int day = 30;
switch(cmbUpdate.SelectedIndex)
{
case 0:
day = 7; break;
case 2:
day = 90; break;
case 3:
day = -1; break;
}
AppConfig.UpdateFrequency = day;
}
private int GetUpdateSelectIndex()
{
int index = 1;
switch(AppConfig.UpdateFrequency)
{
case 7:
index = 0; break;
case 90:
index = 2; break;
case -1:
index = 3; break;
}
return index;
}
private int GetEngineSelectIndex()
{
string[] urls = AppConfig.EngineUrlsDic.Values.ToArray();
for(int i = 0; i < urls.Length; i++)
{
if(AppConfig.EngineUrl.Equals(urls[i])) return i;
}
return cmbEngine.Items.Count - 1;
}
}
}
================================================
FILE: ContextMenuManager/Controls/DetailedEditDialog.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class DetailedEditDialog : CommonDialog
{
public Guid GroupGuid { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(SubItemsForm frm = new SubItemsForm())
using(DetailedEditList list = new DetailedEditList())
{
var location = GuidInfo.GetIconLocation(this.GroupGuid);
frm.Icon = ResourceIcon.GetIcon(location.IconPath, location.IconIndex);
frm.Text = AppString.Dialog.DetailedEdit.Replace("%s", GuidInfo.GetText(this.GroupGuid));
frm.TopMost = AppConfig.TopMost;
frm.AddList(list);
list.GroupGuid = this.GroupGuid;
list.UseUserDic = XmlDicHelper.DetailedEditGuidDic[this.GroupGuid];
list.LoadItems();
frm.ShowDialog();
}
return false;
}
}
}
================================================
FILE: ContextMenuManager/Controls/DetailedEditList.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Xml;
namespace ContextMenuManager.Controls
{
sealed class DetailedEditList : SwitchDicList
{
public Guid GroupGuid { get; set; }
public override void LoadItems()
{
base.LoadItems();
int index = this.UseUserDic ? 1 : 0;
XmlDocument doc = XmlDicHelper.DetailedEditDic[index];
if(doc?.DocumentElement == null) return;
foreach(XmlNode groupXN in doc.DocumentElement.ChildNodes)
{
try
{
List guids = new List();
XmlNodeList guidList = groupXN.SelectNodes("Guid");
foreach(XmlNode guidXN in guidList)
{
if(!GuidEx.TryParse(guidXN.InnerText, out Guid guid)) continue;
if(!File.Exists(GuidInfo.GetFilePath(guid))) continue;
if(this.GroupGuid != Guid.Empty && this.GroupGuid != guid) continue;
guids.Add(guid);
}
if(guidList.Count > 0 && guids.Count == 0) continue;
FoldGroupItem groupItem;
bool isIniGroup = groupXN.SelectSingleNode("IsIniGroup") != null;
string attribute = isIniGroup ? "FilePath" : "RegPath";
ObjectPath.PathType pathType = isIniGroup ? ObjectPath.PathType.File : ObjectPath.PathType.Registry;
groupItem = new FoldGroupItem(groupXN.SelectSingleNode(attribute)?.InnerText, pathType);
foreach(XmlElement textXE in groupXN.SelectNodes("Text"))
{
if(XmlDicHelper.JudgeCulture(textXE)) groupItem.Text = ResourceString.GetDirectString(textXE.GetAttribute("Value"));
}
if(guids.Count > 0)
{
groupItem.Tag = guids;
if(groupItem.Text.IsNullOrWhiteSpace()) groupItem.Text = GuidInfo.GetText(guids[0]);
groupItem.Image = GuidInfo.GetImage(guids[0]);
string filePath = GuidInfo.GetFilePath(guids[0]);
string clsidPath = GuidInfo.GetClsidPath(guids[0]);
if(filePath != null || clsidPath != null) groupItem.ContextMenuStrip.Items.Add(new ToolStripSeparator());
if(filePath != null)
{
ToolStripMenuItem tsi = new ToolStripMenuItem(AppString.Menu.FileLocation);
tsi.Click += (sender, e) => ExternalProgram.JumpExplorer(filePath, AppConfig.OpenMoreExplorer);
groupItem.ContextMenuStrip.Items.Add(tsi);
}
if(clsidPath != null)
{
ToolStripMenuItem tsi = new ToolStripMenuItem(AppString.Menu.ClsidLocation);
tsi.Click += (sender, e) => ExternalProgram.JumpRegEdit(clsidPath, null, AppConfig.OpenMoreRegedit);
groupItem.ContextMenuStrip.Items.Add(tsi);
}
}
XmlNode iconXN = groupXN.SelectSingleNode("Icon");
using(Icon icon = ResourceIcon.GetIcon(iconXN?.InnerText))
{
if(icon != null) groupItem.Image = icon.ToBitmap();
}
this.AddItem(groupItem);
string GetRuleFullRegPath(string regPath)
{
if(string.IsNullOrEmpty(regPath)) regPath = groupItem.GroupPath;
else if(regPath.StartsWith("\\")) regPath = groupItem.GroupPath + regPath;
return regPath;
};
foreach(XmlElement itemXE in groupXN.SelectNodes("Item"))
{
try
{
if(!XmlDicHelper.JudgeOSVersion(itemXE)) continue;
RuleItem ruleItem;
ItemInfo info = new ItemInfo();
foreach(XmlElement textXE in itemXE.SelectNodes("Text"))
{
if(XmlDicHelper.JudgeCulture(textXE)) info.Text = ResourceString.GetDirectString(textXE.GetAttribute("Value"));
}
foreach(XmlElement tipXE in itemXE.SelectNodes("Tip"))
{
if(XmlDicHelper.JudgeCulture(tipXE)) info.Tip = ResourceString.GetDirectString(tipXE.GetAttribute("Value"));
}
info.RestartExplorer = itemXE.SelectSingleNode("RestartExplorer") != null;
int defaultValue = 0, maxValue = 0, minValue = 0;
if(itemXE.SelectSingleNode("IsNumberItem") != null)
{
XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
defaultValue = ruleXE.HasAttribute("Default") ? Convert.ToInt32(ruleXE.GetAttribute("Default")) : 0;
maxValue = ruleXE.HasAttribute("Max") ? Convert.ToInt32(ruleXE.GetAttribute("Max")) : int.MaxValue;
minValue = ruleXE.HasAttribute("Min") ? Convert.ToInt32(ruleXE.GetAttribute("Min")) : int.MinValue;
}
if(isIniGroup)
{
XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
string iniPath = ruleXE.GetAttribute("FilePath");
if(iniPath.IsNullOrWhiteSpace()) iniPath = groupItem.GroupPath;
string section = ruleXE.GetAttribute("Section");
string keyName = ruleXE.GetAttribute("KeyName");
if(itemXE.SelectSingleNode("IsNumberItem") != null)
{
var rule = new NumberIniRuleItem.IniRule
{
IniPath = iniPath,
Section = section,
KeyName = keyName,
DefaultValue = defaultValue,
MaxValue = maxValue,
MinValue = maxValue
};
ruleItem = new NumberIniRuleItem(rule, info);
}
else if(itemXE.SelectSingleNode("IsStringItem") != null)
{
var rule = new StringIniRuleItem.IniRule
{
IniPath = iniPath,
Secation = section,
KeyName = keyName
};
ruleItem = new StringIniRuleItem(rule, info);
}
else
{
var rule = new VisbleIniRuleItem.IniRule
{
IniPath = iniPath,
Section = section,
KeyName = keyName,
TurnOnValue = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null,
TurnOffValue = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null,
};
ruleItem = new VisbleIniRuleItem(rule, info);
}
}
else
{
if(itemXE.SelectSingleNode("IsNumberItem") != null)
{
XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
var rule = new NumberRegRuleItem.RegRule
{
RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
ValueName = ruleXE.GetAttribute("ValueName"),
ValueKind = XmlDicHelper.GetValueKind(ruleXE.GetAttribute("ValueKind"), RegistryValueKind.DWord),
DefaultValue = defaultValue,
MaxValue = maxValue,
MinValue = minValue
};
ruleItem = new NumberRegRuleItem(rule, info);
}
else if(itemXE.SelectSingleNode("IsStringItem") != null)
{
XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
var rule = new StringRegRuleItem.RegRule
{
RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
ValueName = ruleXE.GetAttribute("ValueName"),
};
ruleItem = new StringRegRuleItem(rule, info);
}
else
{
XmlNodeList ruleXNList = itemXE.SelectNodes("Rule");
var rules = new VisibleRegRuleItem.RegRule[ruleXNList.Count];
for(int i = 0; i < ruleXNList.Count; i++)
{
XmlElement ruleXE = (XmlElement)ruleXNList[i];
rules[i] = new VisibleRegRuleItem.RegRule
{
RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
ValueName = ruleXE.GetAttribute("ValueName"),
ValueKind = XmlDicHelper.GetValueKind(ruleXE.GetAttribute("ValueKind"), RegistryValueKind.DWord)
};
string turnOn = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null;
string turnOff = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null;
switch(rules[i].ValueKind)
{
case RegistryValueKind.Binary:
rules[i].TurnOnValue = turnOn != null ? XmlDicHelper.ConvertToBinary(turnOn) : null;
rules[i].TurnOffValue = turnOff != null ? XmlDicHelper.ConvertToBinary(turnOff) : null;
break;
case RegistryValueKind.DWord:
if(turnOn == null) rules[i].TurnOnValue = null;
else rules[i].TurnOnValue = Convert.ToInt32(turnOn);
if(turnOff == null) rules[i].TurnOffValue = null;
else rules[i].TurnOffValue = Convert.ToInt32(turnOff);
break;
default:
rules[i].TurnOnValue = turnOn;
rules[i].TurnOffValue = turnOff;
break;
}
}
ruleItem = new VisibleRegRuleItem(rules, info);
}
}
this.AddItem(ruleItem);
ruleItem.FoldGroupItem = groupItem;
ruleItem.HasImage = ruleItem.Image != null;
ruleItem.Indent();
}
catch { continue; }
}
groupItem.SetVisibleWithSubItemCount();
}
catch { continue; }
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/DictionariesBox.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class DictionariesBox : TabControl
{
public DictionariesBox()
{
this.SuspendLayout();
this.Dock = DockStyle.Fill;
this.Controls.AddRange(pages);
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
cms.Items.AddRange(items);
for(int i = 0; i < 6; i++)
{
boxs[i] = new ReadOnlyRichTextBox { Parent = pages[i] };
if(i > 0) boxs[i].ContextMenuStrip = cms;
}
items[0].Click += (sender, e) => ExternalProgram.OpenNotepadWithText(GetInitialText());
items[2].Click += (sender, e) => SaveFile();
boxs[0].Controls.Add(btnOpenDir);
btnOpenDir.Top = boxs[0].Height - btnOpenDir.Height;
ToolTipBox.SetToolTip(btnOpenDir, AppString.Menu.FileLocation);
btnOpenDir.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(AppConfig.DicsDir);
this.SelectedIndexChanged += (sender, e) => LoadText();
this.VisibleChanged += (sender, e) => this.SetEnabled(this.Visible);
this.ResumeLayout();
}
readonly TabPage[] pages =
{
new TabPage(AppString.Other.DictionaryDescription),
new TabPage(AppString.SideBar.AppLanguage),
new TabPage(AppString.Other.GuidInfosDictionary),
new TabPage(AppString.SideBar.DetailedEdit),
new TabPage(AppString.SideBar.EnhanceMenu),
new TabPage(AppString.Other.UwpMode)
};
readonly ReadOnlyRichTextBox[] boxs = new ReadOnlyRichTextBox[6];
readonly PictureButton btnOpenDir = new PictureButton(AppImage.Open)
{
Anchor = AnchorStyles.Left | AnchorStyles.Bottom,
Left = 0
};
readonly ContextMenuStrip cms = new ContextMenuStrip();
readonly ToolStripItem[] items =
{
new ToolStripMenuItem(AppString.Menu.Edit),
new ToolStripSeparator(),
new ToolStripMenuItem(AppString.Menu.Save)
};
private void SaveFile()
{
using(SaveFileDialog dlg = new SaveFileDialog())
{
string dirPath = AppConfig.UserDicsDir;
switch(SelectedIndex)
{
case 1:
dirPath = AppConfig.LangsDir;
dlg.FileName = AppConfig.ZH_CNINI;
break;
case 2:
dlg.FileName = AppConfig.GUIDINFOSDICINI;
break;
case 3:
dlg.FileName = AppConfig.DETAILEDEDITDICXML;
break;
case 4:
dlg.FileName = AppConfig.ENHANCEMENUSICXML;
break;
case 5:
dlg.FileName = AppConfig.UWPMODEITEMSDICXML;
break;
}
dlg.Filter = $"{dlg.FileName}|*{Path.GetExtension(dlg.FileName)}";
Directory.CreateDirectory(dirPath);
dlg.InitialDirectory = dirPath;
if(dlg.ShowDialog() == DialogResult.OK)
{
File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.Unicode);
}
}
}
private string GetInitialText()
{
switch(this.SelectedIndex)
{
case 0:
return AppString.Other.Dictionaries;
case 1:
return Properties.Resources.AppLanguageDic;
case 2:
return Properties.Resources.GuidInfosDic;
case 3:
return Properties.Resources.DetailedEditDic;
case 4:
return Properties.Resources.EnhanceMenusDic;
case 5:
return Properties.Resources.UwpModeItemsDic;
default:
return string.Empty;
}
}
public void LoadText()
{
int index = this.SelectedIndex;
if(boxs[index].Text.Length > 0) return;
Action action = null;
switch(index)
{
case 0:
case 1:
case 2:
action = boxs[index].LoadIni; break;
case 3:
case 4:
case 5:
action = boxs[index].LoadXml; break;
}
this.BeginInvoke(action, new[] { GetInitialText() });
}
}
}
================================================
FILE: ContextMenuManager/Controls/DonateBox.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class DonateBox : Panel
{
public DonateBox()
{
this.SuspendLayout();
this.AutoScroll = true;
this.Dock = DockStyle.Fill;
this.BackColor = Color.White;
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
this.Controls.AddRange(new Control[] { lblInfo, picQR, lblList });
this.VisibleChanged += (sender, e) => this.SetEnabled(this.Visible);
lblList.Click += (sender, e) => this.ShowDonateDialog();
picQR.Resize += (sender, e) => this.OnResize(null);
picQR.MouseDown += SwitchQR;
this.ResumeLayout();
}
readonly Label lblInfo = new Label
{
Text = AppString.Other.Donate,
AutoSize = true
};
readonly Label lblList = new Label
{
ForeColor = Color.FromArgb(85, 145, 215),
Text = AppString.Other.DonationList,
Cursor = Cursors.Hand,
AutoSize = true
};
readonly PictureBox picQR = new PictureBox
{
SizeMode = PictureBoxSizeMode.AutoSize,
Cursor = Cursors.Hand,
Image = AllQR,
};
static readonly Image AllQR = Properties.Resources.Donate;
static readonly Image WechatQR = GetSingleQR(0);
static readonly Image AlipayQR = GetSingleQR(1);
static readonly Image QQQR = GetSingleQR(2);
private static Image GetSingleQR(int index)
{
Bitmap bitmap = new Bitmap(200, 200);
using(Graphics g = Graphics.FromImage(bitmap))
{
Rectangle destRect = new Rectangle(0, 0, 200, 200);
Rectangle srcRect = new Rectangle(index * 200, 0, 200, 200);
g.DrawImage(AllQR, destRect, srcRect, GraphicsUnit.Pixel);
}
return bitmap;
}
protected override void OnResize(EventArgs e)
{
int a = 60.DpiZoom();
base.OnResize(e);
picQR.Left = (this.Width - picQR.Width) / 2;
lblInfo.Left = (this.Width - lblInfo.Width) / 2;
lblList.Left = (this.Width - lblList.Width) / 2;
lblInfo.Top = a;
picQR.Top = lblInfo.Bottom + a;
lblList.Top = picQR.Bottom + a;
}
private void SwitchQR(object sender, MouseEventArgs e)
{
if(picQR.Image == AllQR)
{
if(e.X < 200) picQR.Image = WechatQR;
else if(e.X < 400) picQR.Image = AlipayQR;
else picQR.Image = QQQR;
}
else
{
picQR.Image = AllQR;
}
}
private void ShowDonateDialog()
{
this.Cursor = Cursors.WaitCursor;
using(UAWebClient client = new UAWebClient())
{
string url = AppConfig.RequestUseGithub ? AppConfig.GithubDonateRaw : AppConfig.GiteeDonateRaw;
string contents = client.GetWebString(url);
//contents = System.IO.File.ReadAllText(@"..\..\..\Donate.md");//用于求和更新Donate.md文件
if(contents == null)
{
if(AppMessageBox.Show(AppString.Message.WebDataReadFailed + "\r\n"
+ AppString.Message.OpenWebUrl, MessageBoxButtons.OKCancel) == DialogResult.OK)
{
url = AppConfig.RequestUseGithub ? AppConfig.GithubDonate : AppConfig.GiteeDonate;
ExternalProgram.OpenWebUrl(url);
}
}
else
{
using(DonateListDialog dlg = new DonateListDialog())
{
dlg.DanateData = contents;
dlg.ShowDialog();
}
}
}
this.Cursor = Cursors.Default;
}
sealed class DonateListDialog : CommonDialog
{
public string DanateData { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(DonateListForm frm = new DonateListForm())
{
frm.ShowDonateList(DanateData);
MainForm mainForm = (MainForm)FromHandle(hwndOwner);
frm.Left = mainForm.Left + (mainForm.Width + mainForm.SideBar.Width - frm.Width) / 2;
frm.Top = mainForm.Top + 150.DpiZoom();
frm.TopMost = AppConfig.TopMost;
frm.ShowDialog();
}
return true;
}
sealed class DonateListForm : Form
{
public DonateListForm()
{
this.Font = SystemFonts.DialogFont;
this.Text = AppString.Other.DonationList;
this.SizeGripStyle = SizeGripStyle.Hide;
this.StartPosition = FormStartPosition.Manual;
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
this.MinimizeBox = this.MaximizeBox = this.ShowInTaskbar = false;
this.ClientSize = new Size(520, 350).DpiZoom();
this.MinimumSize = this.Size;
dgvDonate.ColumnHeadersDefaultCellStyle.Alignment
= dgvDonate.RowsDefaultCellStyle.Alignment
= DataGridViewContentAlignment.BottomCenter;
this.Controls.AddRange(new Control[] { lblThank, lblDonate, dgvDonate });
lblThank.MouseEnter += (sender, e) => lblThank.ForeColor = Color.FromArgb(0, 162, 255);
lblThank.MouseLeave += (sender, e) => lblThank.ForeColor = Color.DimGray;
lblDonate.Resize += (sender, e) => this.OnResize(null);
this.AddEscapeButton();
}
readonly DataGridView dgvDonate = new DataGridView
{
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
BackgroundColor = SystemColors.Control,
BorderStyle = BorderStyle.None,
AllowUserToResizeRows = false,
AllowUserToAddRows = false,
RowHeadersVisible = false,
MultiSelect = false,
ReadOnly = true
};
readonly Label lblDonate = new Label { AutoSize = true };
readonly Label lblThank = new Label
{
Font = new Font("Lucida Handwriting", 15F),
ForeColor = Color.DimGray,
Text = "Thank you!",
AutoSize = true,
};
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
int a = 20.DpiZoom();
lblDonate.Location = new Point(a, a);
dgvDonate.Location = new Point(a, lblDonate.Bottom + a);
dgvDonate.Width = this.ClientSize.Width - 2 * a;
dgvDonate.Height = this.ClientSize.Height - 3 * a - lblDonate.Height;
lblThank.Location = new Point(dgvDonate.Right - lblThank.Width, lblDonate.Bottom - lblThank.Height);
}
public void ShowDonateList(string contents)
{
string[] lines = contents.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
int index = Array.FindIndex(lines, line => line == "|:--:|:--:|:--:|:--:|:--:");
if(index == -1) return;
string[] heads = lines[index - 1].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
dgvDonate.ColumnCount = heads.Length;
dgvDonate.Columns[4].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
for(int m = 0; m < heads.Length; m++)
{
dgvDonate.Columns[m].HeaderText = heads[m];
}
for(int n = index + 1; n < lines.Length; n++)
{
string[] strs = lines[n].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
object[] values = new object[strs.Length];
for(int k = 0; k < strs.Length; k++)
{
switch(k)
{
case 3:
values[k] = Convert.ToSingle(strs[k]);
break;
default:
values[k] = strs[k];
break;
}
}
dgvDonate.Rows.Add(values);
}
dgvDonate.Sort(dgvDonate.Columns[0], ListSortDirection.Descending);
DateTime date = Convert.ToDateTime(dgvDonate.Rows[0].Cells[0].Value);
float money = dgvDonate.Rows.Cast().Sum(row => (float)row.Cells[3].Value);
lblDonate.Text = AppString.Dialog.DonateInfo.Replace("%date", date.ToLongDateString())
.Replace("%money", money.ToString()).Replace("%count", dgvDonate.RowCount.ToString());
}
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/EnhanceMenusDialog.cs
================================================
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class EnhanceMenusDialog : CommonDialog
{
public string ScenePath { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(SubItemsForm frm = new SubItemsForm())
using(EnhanceMenusList list = new EnhanceMenusList())
{
frm.Text = AppString.SideBar.EnhanceMenu;
frm.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
frm.TopMost = AppConfig.TopMost;
frm.AddList(list);
list.ScenePath = this.ScenePath;
list.UseUserDic = XmlDicHelper.EnhanceMenuPathDic[this.ScenePath];
list.LoadItems();
frm.ShowDialog();
}
return false;
}
}
}
================================================
FILE: ContextMenuManager/Controls/EnhanceMenusItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Controls.Interfaces;
using ContextMenuManager.Methods;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
namespace ContextMenuManager.Controls
{
sealed class EnhanceShellItem : FoldSubItem, IChkVisibleItem
{
public string RegPath { get; set; }
public XmlElement ItemXE { get; set; }
public VisibleCheckBox ChkVisible { get; set; }
public bool ItemVisible
{
get
{
using(RegistryKey key = RegistryEx.GetRegistryKey(RegPath))
return key != null;
}
set
{
if(value) WriteSubKeysValue(ItemXE, RegPath);
else RegistryEx.DeleteKeyTree(RegPath);
}
}
public EnhanceShellItem()
{
ChkVisible = new VisibleCheckBox(this);
this.Indent();
}
private static void WriteAttributesValue(XmlNode valueXN, string regPath)
{
if(valueXN == null) return;
if(!XmlDicHelper.FileExists(valueXN)) return;
if(!XmlDicHelper.JudgeCulture(valueXN)) return;
if(!XmlDicHelper.JudgeOSVersion(valueXN)) return;
using(RegistryKey key = RegistryEx.GetRegistryKey(regPath, true, true))
{
foreach(XmlNode xn in valueXN.ChildNodes)
{
if(xn is XmlComment) continue;
if(!XmlDicHelper.FileExists(xn)) continue;
if(!XmlDicHelper.JudgeCulture(xn)) continue;
if(!XmlDicHelper.JudgeOSVersion(xn)) continue;
foreach(XmlAttribute xa in xn.Attributes)
{
switch(xn.Name)
{
case "REG_SZ":
key.SetValue(xa.Name, Environment.ExpandEnvironmentVariables(xa.Value), RegistryValueKind.String);
break;
case "REG_EXPAND_SZ":
key.SetValue(xa.Name, xa.Value, RegistryValueKind.ExpandString);
break;
case "REG_BINARY":
key.SetValue(xa.Name, XmlDicHelper.ConvertToBinary(xa.Value), RegistryValueKind.Binary);
break;
case "REG_DWORD":
int num = xa.Value.ToLower().StartsWith("0x") ? 16 : 10;
key.SetValue(xa.Name, Convert.ToInt32(xa.Value, num), RegistryValueKind.DWord);
break;
}
}
}
}
}
private static void WriteSubKeysValue(XmlNode keyXN, string regPath)
{
if(keyXN == null) return;
if(!XmlDicHelper.FileExists(keyXN)) return;
if(!XmlDicHelper.JudgeCulture(keyXN)) return;
if(!XmlDicHelper.JudgeOSVersion(keyXN)) return;
string defaultValue = ((XmlElement)keyXN).GetAttribute("Default");
if(!defaultValue.IsNullOrWhiteSpace())
{
defaultValue = Environment.ExpandEnvironmentVariables(defaultValue);
Registry.SetValue(regPath, "", defaultValue);
}
else if(keyXN.Name == "Command")
{
//按照规则Command节点无默认值则创建文件
WriteCommandValue(keyXN, regPath);
}
WriteAttributesValue(keyXN.SelectSingleNode("Value"), regPath);
XmlNode subKeyXN = keyXN.SelectSingleNode("SubKey");
if(subKeyXN != null)
{
foreach(XmlNode xn in subKeyXN.ChildNodes)
{
if(xn is XmlComment) continue;
WriteSubKeysValue(xn, $@"{regPath}\{xn.Name}");
}
}
}
private static void WriteCommandValue(XmlNode cmdXE, string regPath)
{
XmlElement fnXE = (XmlElement)cmdXE.SelectSingleNode("FileName");
XmlElement argXE = (XmlElement)cmdXE.SelectSingleNode("Arguments");
XmlElement seXE = (XmlElement)cmdXE.SelectSingleNode("ShellExecute");
string command;
string fileName = fnXE?.InnerText.Trim();
string arguments = argXE?.InnerText.Trim();
if(string.IsNullOrEmpty(fileName)) fileName = CreateCommandFile(fnXE);
if(string.IsNullOrEmpty(arguments)) arguments = CreateCommandFile(argXE);
fileName = Environment.ExpandEnvironmentVariables(fileName);
arguments = Environment.ExpandEnvironmentVariables(arguments);
string prefix = argXE?.GetAttribute("Prefix");//参数前缀
string suffix = argXE?.GetAttribute("Suffix");//参数后缀
arguments = prefix + arguments + suffix;
if(seXE != null)
{
string verb = seXE.HasAttribute("Verb") ? seXE.GetAttribute("Verb") : "open";
int windowStyle = seXE.HasAttribute("WindowStyle") ? Convert.ToInt32(seXE.GetAttribute("WindowStyle")) : 1;
string directory = Environment.ExpandEnvironmentVariables(seXE.GetAttribute("Directory"));
command = ShellExecuteDialog.GetCommand(fileName, arguments, verb, windowStyle, directory);
}
else
{
command = fileName;
if(arguments != string.Empty) command += $" {arguments}";
}
Registry.SetValue(regPath, "", command);
}
private static string CreateCommandFile(XmlNode xe)
{
string path = string.Empty;
if(xe == null) return path;
foreach(XmlElement cfXE in xe.SelectNodes("CreateFile"))
{
if(!XmlDicHelper.FileExists(cfXE)) continue;
if(!XmlDicHelper.JudgeCulture(cfXE)) continue;
if(!XmlDicHelper.JudgeOSVersion(cfXE)) continue;
string fileName = cfXE.GetAttribute("FileName");
string content = cfXE.GetAttribute("Content");
string extension = Path.GetExtension(fileName).ToLower();
string filePath = $@"{AppConfig.ProgramsDir}\{fileName}";
if(path == string.Empty) path = filePath;
Encoding encoding;
switch(extension)
{
case ".bat":
case ".cmd":
encoding = Encoding.Default;
break;
default:
encoding = Encoding.Unicode;
break;
}
File.Delete(filePath);
File.WriteAllText(filePath, content, encoding);
}
return path;
}
}
sealed class EnhanceShellExItem : FoldSubItem, IChkVisibleItem
{
public string RegPath => $@"{ShellExPath}\ContextMenuHandlers\{DefaultKeyName}";
public string ShellExPath { get; set; }
public string DefaultKeyName { get; set; }
public Guid Guid { get; set; }
public VisibleCheckBox ChkVisible { get; set; }
public bool ItemVisible
{
get => ShellExItem.GetPathAndGuids(ShellExPath).Values.Contains(Guid);
set
{
if(value)
{
string regPath = ObjectPath.GetNewPathWithIndex(this.RegPath, ObjectPath.PathType.Registry);
Registry.SetValue(regPath, "", this.Guid.ToString("B"));
}
else
{
Dictionary dic = ShellExItem.GetPathAndGuids(ShellExPath);
foreach(string regPath in dic.Keys)
{
if(dic[regPath].Equals(this.Guid))
RegistryEx.DeleteKeyTree(regPath);
}
}
}
}
public EnhanceShellExItem()
{
ChkVisible = new VisibleCheckBox(this);
this.Indent();
}
}
}
================================================
FILE: ContextMenuManager/Controls/EnhanceMenusList.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.Xml;
namespace ContextMenuManager.Controls
{
sealed class EnhanceMenusList : SwitchDicList
{
public string ScenePath { get; set; }
public override void LoadItems()
{
base.LoadItems();
int index = this.UseUserDic ? 1 : 0;
XmlDocument doc = XmlDicHelper.EnhanceMenusDic[index];
if(doc?.DocumentElement == null) return;
foreach(XmlNode xn in doc.DocumentElement.ChildNodes)
{
try
{
Image image = null;
string text = null;
string path = xn.SelectSingleNode("RegPath")?.InnerText;
foreach(XmlElement textXE in xn.SelectNodes("Text"))
{
if(XmlDicHelper.JudgeCulture(textXE))
{
text = ResourceString.GetDirectString(textXE.GetAttribute("Value"));
}
}
if(string.IsNullOrEmpty(path) || string.IsNullOrEmpty(text)) continue;
if(!string.IsNullOrEmpty(this.ScenePath) && !path.Equals(this.ScenePath, StringComparison.OrdinalIgnoreCase)) continue;
string iconLocation = xn.SelectSingleNode("Icon")?.InnerText;
using(Icon icon = ResourceIcon.GetIcon(iconLocation))
{
image = icon?.ToBitmap();
image = image ?? AppImage.NotFound;
}
FoldGroupItem groupItem = new FoldGroupItem(path, ObjectPath.PathType.Registry)
{
Image = image,
Text = text
};
this.AddItem(groupItem);
XmlNode shellXN = xn.SelectSingleNode("Shell");
XmlNode shellExXN = xn.SelectSingleNode("ShellEx");
if(shellXN != null) LoadShellItems(shellXN, groupItem);
if(shellExXN != null) LoadShellExItems(shellExXN, groupItem);
groupItem.SetVisibleWithSubItemCount();
}
catch { continue; }
}
}
private void LoadShellItems(XmlNode shellXN, FoldGroupItem groupItem)
{
foreach(XmlElement itemXE in shellXN.SelectNodes("Item"))
{
if(!XmlDicHelper.FileExists(itemXE)) continue;
if(!XmlDicHelper.JudgeCulture(itemXE)) continue;
if(!XmlDicHelper.JudgeOSVersion(itemXE)) continue;
string keyName = itemXE.GetAttribute("KeyName");
if(keyName.IsNullOrWhiteSpace()) continue;
EnhanceShellItem item = new EnhanceShellItem()
{
RegPath = $@"{groupItem.GroupPath}\shell\{keyName}",
FoldGroupItem = groupItem,
ItemXE = itemXE
};
foreach(XmlElement szXE in itemXE.SelectNodes("Value/REG_SZ"))
{
if(!XmlDicHelper.JudgeCulture(szXE)) continue;
if(szXE.HasAttribute("MUIVerb")) item.Text = ResourceString.GetDirectString(szXE.GetAttribute("MUIVerb"));
if(szXE.HasAttribute("Icon")) item.Image = ResourceIcon.GetIcon(szXE.GetAttribute("Icon"))?.ToBitmap();
else if(szXE.HasAttribute("HasLUAShield")) item.Image = AppImage.Shield;
}
if(item.Image == null)
{
XmlElement cmdXE = (XmlElement)itemXE.SelectSingleNode("SubKey/Command");
if(cmdXE != null)
{
Icon icon = null;
if(cmdXE.HasAttribute("Default"))
{
string filePath = ObjectPath.ExtractFilePath(cmdXE.GetAttribute("Default"));
icon = ResourceIcon.GetIcon(filePath);
}
else
{
XmlNode fileXE = cmdXE.SelectSingleNode("FileName");
if(fileXE != null)
{
string filePath = ObjectPath.ExtractFilePath(fileXE.InnerText);
icon = ResourceIcon.GetIcon(filePath);
}
}
item.Image = icon?.ToBitmap();
icon?.Dispose();
}
}
if(item.Image == null) item.Image = AppImage.NotFound;
if(item.Text.IsNullOrWhiteSpace()) item.Text = keyName;
string tip = "";
foreach(XmlElement tipXE in itemXE.SelectNodes("Tip"))
{
if(XmlDicHelper.JudgeCulture(tipXE)) tip = tipXE.GetAttribute("Value");
}
if(itemXE.GetElementsByTagName("CreateFile").Count > 0)
{
if(!tip.IsNullOrWhiteSpace()) tip += "\n";
tip += AppString.Tip.CommandFiles;
}
ToolTipBox.SetToolTip(item.ChkVisible, tip);
this.AddItem(item);
}
}
private void LoadShellExItems(XmlNode shellExXN, FoldGroupItem groupItem)
{
foreach(XmlNode itemXN in shellExXN.SelectNodes("Item"))
{
if(!XmlDicHelper.FileExists(itemXN)) continue;
if(!XmlDicHelper.JudgeCulture(itemXN)) continue;
if(!XmlDicHelper.JudgeOSVersion(itemXN)) continue;
if(!GuidEx.TryParse(itemXN.SelectSingleNode("Guid")?.InnerText, out Guid guid)) continue;
EnhanceShellExItem item = new EnhanceShellExItem
{
FoldGroupItem = groupItem,
ShellExPath = $@"{groupItem.GroupPath}\ShellEx",
Image = ResourceIcon.GetIcon(itemXN.SelectSingleNode("Icon")?.InnerText)?.ToBitmap() ?? AppImage.SystemFile,
DefaultKeyName = itemXN.SelectSingleNode("KeyName")?.InnerText,
Guid = guid
};
foreach(XmlNode textXE in itemXN.SelectNodes("Text"))
{
if(XmlDicHelper.JudgeCulture(textXE))
{
item.Text = ResourceString.GetDirectString(textXE.InnerText);
}
}
if(item.Text.IsNullOrWhiteSpace()) item.Text = GuidInfo.GetText(guid);
if(item.DefaultKeyName.IsNullOrWhiteSpace()) item.DefaultKeyName = guid.ToString("B");
string tip = "";
foreach(XmlElement tipXE in itemXN.SelectNodes("Tip"))
{
if(XmlDicHelper.JudgeCulture(tipXE)) tip = tipXE.GetAttribute("Text");
}
ToolTipBox.SetToolTip(item.ChkVisible, tip);
this.AddItem(item);
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/ExplorerRestarter.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class ExplorerRestarter : MyListItem
{
public ExplorerRestarter()
{
this.Visible = false;
this.DoubleBuffered = false;
this.Dock = DockStyle.Bottom;
this.Image = AppImage.Explorer;
this.Text = AppString.Other.RestartExplorer;
ToolTipBox.SetToolTip(BtnRestart, AppString.Tip.RestartExplorer);
this.AddCtr(BtnRestart);
this.CanMoveForm();
ShowHandler += () => this.Visible = true;
HideHandler += () => this.Visible = false;
BtnRestart.MouseDown += (sender, e) =>
{
ExternalProgram.RestartExplorer();
this.Visible = false;
};
}
public new bool Visible
{
get => base.Visible;
set
{
bool flag = base.Visible != value && this.Parent != null;
base.Visible = value;
if(flag) this.Parent.Height += value ? Height : -Height;
}
}
private readonly PictureButton BtnRestart = new PictureButton(AppImage.RestartExplorer);
private static Action ShowHandler;
private static Action HideHandler;
public static new void Show() => ShowHandler?.Invoke();
public static new void Hide() => HideHandler?.Invoke();
}
}
================================================
FILE: ContextMenuManager/Controls/FileExtensionDialog.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Collections.Generic;
namespace ContextMenuManager.Controls
{
sealed class FileExtensionDialog : SelectDialog
{
public string Extension
{
get => Selected.Trim();
set => Selected = value?.Trim();
}
public FileExtensionDialog()
{
this.CanEdit = true;
this.Title = AppString.Dialog.SelectExtension;
List items = new List();
using(var key = RegistryEx.GetRegistryKey(FileExtension.FILEEXTSPATH))
{
if(key != null)
{
foreach(string keyName in key.GetSubKeyNames())
{
if(keyName.StartsWith(".")) items.Add(keyName.Substring(1));
}
}
}
this.Items = items.ToArray();
}
protected override bool RunDialog(IntPtr hwndOwner)
{
bool flag = base.RunDialog(hwndOwner);
if(flag)
{
string extension = ObjectPath.RemoveIllegalChars(this.Extension);
int index = extension.LastIndexOf('.');
if(index >= 0) this.Extension = extension.Substring(index);
else this.Extension = $".{extension}";
}
return flag;
}
}
}
================================================
FILE: ContextMenuManager/Controls/FoldSubItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Controls.Interfaces;
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using static ContextMenuManager.Methods.ObjectPath;
namespace ContextMenuManager.Controls
{
class FoldSubItem : MyListItem
{
public FoldGroupItem FoldGroupItem { get; set; }
public void Indent()
{
int w = 40.DpiZoom();
this.Controls["Image"].Left += w;
this.Controls["Text"].Left += w;
}
}
class FoldGroupItem : MyListItem, IBtnShowMenuItem
{
private bool isFold;
public bool IsFold
{
get => isFold;
set
{
if(isFold == value) return;
isFold = value;
FoldMe(value);
}
}
public string GroupPath { get; set; }
public PathType PathType { get; set; }
public MenuButton BtnShowMenu { get; set; }
readonly PictureButton btnFold;
readonly PictureButton btnOpenPath;
readonly ToolStripMenuItem tsiFoldAll = new ToolStripMenuItem(AppString.Menu.FoldAll);
readonly ToolStripMenuItem tsiUnfoldAll = new ToolStripMenuItem(AppString.Menu.UnfoldAll);
public FoldGroupItem(string groupPath, PathType pathType)
{
btnFold = new PictureButton(AppImage.Up);
BtnShowMenu = new MenuButton(this);
btnOpenPath = new PictureButton(AppImage.Open);
if(pathType == PathType.File || pathType == PathType.Directory)
{
groupPath = Environment.ExpandEnvironmentVariables(groupPath);
}
string tip = null;
Action openPath = null;
switch(pathType)
{
case PathType.File:
tip = AppString.Menu.FileLocation;
this.Text = Path.GetFileNameWithoutExtension(groupPath);
this.Image = ResourceIcon.GetExtensionIcon(groupPath).ToBitmap();
openPath = () => ExternalProgram.JumpExplorer(groupPath, AppConfig.OpenMoreExplorer);
break;
case PathType.Directory:
tip = AppString.Menu.FileLocation;
this.Text = Path.GetFileNameWithoutExtension(groupPath);
this.Image = ResourceIcon.GetFolderIcon(groupPath).ToBitmap();
openPath = () => ExternalProgram.OpenDirectory(groupPath);
break;
case PathType.Registry:
tip = AppString.Menu.RegistryLocation;
openPath = () => ExternalProgram.JumpRegEdit(groupPath, null, AppConfig.OpenMoreRegedit);
break;
}
this.PathType = pathType;
this.GroupPath = groupPath;
this.Font = new Font(this.Font, FontStyle.Bold);
this.AddCtrs(new[] { btnFold, btnOpenPath });
this.ContextMenuStrip.Items.AddRange(new[] { tsiFoldAll, tsiUnfoldAll });
this.MouseDown += (sender, e) =>
{
if(e.Button == MouseButtons.Left) Fold();
};
btnFold.MouseDown += (sender, e) =>
{
Fold();
btnFold.Image = btnFold.BaseImage;
};
tsiFoldAll.Click += (sender, e) => FoldAll(true);
tsiUnfoldAll.Click += (sender, e) => FoldAll(false);
btnOpenPath.MouseDown += (sender, e) => openPath.Invoke();
ToolTipBox.SetToolTip(btnOpenPath, tip);
}
public void SetVisibleWithSubItemCount()
{
foreach(Control ctr in this.Parent.Controls)
{
if(ctr is FoldSubItem item && item.FoldGroupItem == this)
{
this.Visible = true;
return;
}
}
this.Visible = false;
}
private void Fold()
{
this.Parent.SuspendLayout();
this.IsFold = !this.IsFold;
this.Parent.ResumeLayout();
}
private void FoldMe(bool isFold)
{
btnFold.BaseImage = isFold ? AppImage.Down : AppImage.Up;
foreach(Control ctr in this.Parent?.Controls)
{
if(ctr is FoldSubItem item && item.FoldGroupItem == this) ctr.Visible = !isFold;
}
}
private void FoldAll(bool isFold)
{
this.Parent.SuspendLayout();
foreach(Control ctr in this.Parent.Controls)
{
if(ctr is FoldGroupItem groupItem) groupItem.IsFold = isFold;
}
this.Parent.ResumeLayout();
}
}
}
================================================
FILE: ContextMenuManager/Controls/GuidBlockedItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Controls.Interfaces;
using ContextMenuManager.Methods;
using System;
using System.Linq;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
class GuidBlockedItem : MyListItem, IBtnShowMenuItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiGuidItem, ITsiRegPathItem
{
public GuidBlockedItem(string value)
{
InitializeComponents();
this.Value = value;
if(GuidEx.TryParse(value, out Guid guid))
{
this.Guid = guid;
this.Image = GuidInfo.GetImage(guid);
this.ItemFilePath = GuidInfo.GetFilePath(Guid);
}
else
{
this.Guid = Guid.Empty;
this.Image = AppImage.SystemFile;
}
this.Text = this.ItemText;
}
public string Value { get; set; }
public Guid Guid { get; set; }
public string SearchText => Value;
public string ValueName => Value;
public string RegPath
{
get
{
foreach(string path in GuidBlockedList.BlockedPaths)
{
using(var key = RegistryEx.GetRegistryKey(path))
{
if(key == null) continue;
if(key.GetValueNames().Contains(Value, StringComparer.OrdinalIgnoreCase)) return path;
}
}
return null;
}
}
public string ItemText
{
get
{
string text;
if(GuidEx.TryParse(Value, out Guid guid)) text = GuidInfo.GetText(guid);
else text = AppString.Message.MalformedGuid;
text += "\n" + Value;
return text;
}
}
public string ItemFilePath { get; set; }
public MenuButton BtnShowMenu { get; set; }
public DetailedEditButton BtnDetailedEdit { get; set; }
public WebSearchMenuItem TsiSearch { get; set; }
public FileLocationMenuItem TsiFileLocation { get; set; }
public FilePropertiesMenuItem TsiFileProperties { get; set; }
public HandleGuidMenuItem TsiHandleGuid { get; set; }
public RegLocationMenuItem TsiRegLocation { get; set; }
readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
readonly ToolStripMenuItem TsiDelete = new ToolStripMenuItem(AppString.Menu.Delete);
private void InitializeComponents()
{
BtnShowMenu = new MenuButton(this);
BtnDetailedEdit = new DetailedEditButton(this);
TsiSearch = new WebSearchMenuItem(this);
TsiFileProperties = new FilePropertiesMenuItem(this);
TsiFileLocation = new FileLocationMenuItem(this);
TsiRegLocation = new RegLocationMenuItem(this);
TsiHandleGuid = new HandleGuidMenuItem(this);
ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiHandleGuid,
new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDelete });
TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch,
new ToolStripSeparator(), TsiFileProperties, TsiFileLocation, TsiRegLocation});
TsiDelete.Click += (sender, e) => DeleteMe();
}
public void DeleteMe()
{
if(AppMessageBox.Show(AppString.Message.ConfirmDelete, MessageBoxButtons.YesNo) != DialogResult.Yes) return;
Array.ForEach(GuidBlockedList.BlockedPaths, path => RegistryEx.DeleteValue(path, this.Value));
if(!this.Guid.Equals(Guid.Empty)) ExplorerRestarter.Show();
int index = this.Parent.Controls.GetChildIndex(this);
index -= (index < this.Parent.Controls.Count - 1) ? 0 : 1;
this.Parent.Controls[index].Focus();
this.Dispose();
}
}
}
================================================
FILE: ContextMenuManager/Controls/GuidBlockedList.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class GuidBlockedList : MyList
{
public const string HKLMBLOCKED = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked";
public const string HKCUBLOCKED = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked";
public static readonly string[] BlockedPaths = { HKLMBLOCKED, HKCUBLOCKED };
public void LoadItems()
{
this.AddNewItem();
this.LoadBlockedItems();
}
private void LoadBlockedItems()
{
List values = new List();
foreach(string path in BlockedPaths)
{
using(RegistryKey key = RegistryEx.GetRegistryKey(path))
{
if(key == null) continue;
foreach(string value in key.GetValueNames())
{
if(values.Contains(value, StringComparer.OrdinalIgnoreCase)) continue;
this.AddItem(new GuidBlockedItem(value));
values.Add(value);
}
}
}
}
private void AddNewItem()
{
NewItem newItem = new NewItem(AppString.Other.AddGuidBlockedItem);
this.AddItem(newItem);
newItem.AddNewItem += () =>
{
using(InputDialog dlg = new InputDialog { Title = AppString.Dialog.InputGuid })
{
if(GuidEx.TryParse(Clipboard.GetText(), out Guid guid)) dlg.Text = guid.ToString();
if(dlg.ShowDialog() != DialogResult.OK) return;
if(GuidEx.TryParse(dlg.Text, out guid))
{
string value = guid.ToString("B");
Array.ForEach(BlockedPaths, path => Registry.SetValue(path, value, ""));
for(int i = 1; i < Controls.Count; i++)
{
if(((GuidBlockedItem)Controls[i]).Guid.Equals(guid))
{
AppMessageBox.Show(AppString.Message.HasBeenAdded);
return;
}
}
this.InsertItem(new GuidBlockedItem(value), 1);
ExplorerRestarter.Show();
}
else AppMessageBox.Show(AppString.Message.MalformedGuid);
}
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/IEItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Controls.Interfaces;
using ContextMenuManager.Methods;
using Microsoft.Win32;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class IEItem : MyListItem, ITsiRegPathItem, ITsiFilePathItem, ITsiRegDeleteItem, ITsiCommandItem,
ITsiWebSearchItem, ITsiTextItem, ITsiRegExportItem, IBtnShowMenuItem, IChkVisibleItem
{
public static readonly string[] MeParts = { "MenuExt", "-MenuExt" };
public IEItem(string regPath)
{
InitializeComponents();
this.RegPath = regPath;
}
private string regPath;
public string RegPath
{
get => regPath;
set
{
regPath = value;
this.Text = this.ItemText;
this.Image = this.ItemImage;
}
}
public string ValueName => null;
private string KeyName => RegistryEx.GetKeyName(RegPath);
private string BackupPath => $@"{IEList.IEPath}\{(ItemVisible ? MeParts[1] : MeParts[0])}\{KeyName}";
private string MeKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath));
public string ItemText
{
get => RegistryEx.GetKeyName(RegPath);
set
{
string newPath = $@"{RegistryEx.GetParentPath(RegPath)}\{value.Replace("\\", "")}";
string defaultValue = Registry.GetValue(newPath, "", null)?.ToString();
if(!defaultValue.IsNullOrWhiteSpace())
{
AppMessageBox.Show(AppString.Message.HasBeenAdded);
}
else
{
RegistryEx.MoveTo(RegPath, newPath);
RegPath = newPath;
}
}
}
public bool ItemVisible
{
get => MeKeyName.Equals(MeParts[0], StringComparison.OrdinalIgnoreCase);
set
{
RegistryEx.MoveTo(RegPath, BackupPath);
RegPath = BackupPath;
}
}
public string ItemCommand
{
get => Registry.GetValue(RegPath, "", null)?.ToString();
set
{
Registry.SetValue(RegPath, "", value);
this.Image = this.ItemImage;
}
}
public string SearchText => $@"{AppString.SideBar.IEMenu} {Text}";
public string ItemFilePath => ObjectPath.ExtractFilePath(ItemCommand);
private Icon ItemIcon => ResourceIcon.GetIcon(ItemFilePath) ?? ResourceIcon.GetExtensionIcon(ItemFilePath);
private Image ItemImage => ItemIcon?.ToBitmap() ?? AppImage.NotFound;
public MenuButton BtnShowMenu { get; set; }
public VisibleCheckBox ChkVisible { get; set; }
public WebSearchMenuItem TsiSearch { get; set; }
public ChangeTextMenuItem TsiChangeText { get; set; }
public ChangeCommandMenuItem TsiChangeCommand { get; set; }
public FileLocationMenuItem TsiFileLocation { get; set; }
public FilePropertiesMenuItem TsiFileProperties { get; set; }
public RegLocationMenuItem TsiRegLocation { get; set; }
public RegExportMenuItem TsiRegExport { get; set; }
public DeleteMeMenuItem TsiDeleteMe { get; set; }
readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
private void InitializeComponents()
{
BtnShowMenu = new MenuButton(this);
ChkVisible = new VisibleCheckBox(this);
TsiChangeText = new ChangeTextMenuItem(this);
TsiChangeCommand = new ChangeCommandMenuItem(this);
TsiSearch = new WebSearchMenuItem(this);
TsiFileLocation = new FileLocationMenuItem(this);
TsiFileProperties = new FilePropertiesMenuItem(this);
TsiRegLocation = new RegLocationMenuItem(this);
TsiRegExport = new RegExportMenuItem(this);
TsiDeleteMe = new DeleteMeMenuItem(this);
ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText,
new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport});
}
public void DeleteMe()
{
RegistryEx.DeleteKeyTree(this.RegPath);
RegistryEx.DeleteKeyTree(this.BackupPath);
}
}
}
================================================
FILE: ContextMenuManager/Controls/IEList.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace ContextMenuManager.Controls
{
sealed class IEList : MyList
{
public const string IEPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer";
public void LoadItems()
{
AddNewItem();
LoadIEItems();
}
private void LoadIEItems()
{
List names = new List();
using(RegistryKey ieKey = RegistryEx.GetRegistryKey(IEPath))
{
if(ieKey == null) return;
foreach(string part in IEItem.MeParts)
{
using(RegistryKey meKey = ieKey.OpenSubKey(part))
{
if(meKey == null) continue;
foreach(string keyName in meKey.GetSubKeyNames())
{
if(names.Contains(keyName, StringComparer.OrdinalIgnoreCase)) continue;
using(RegistryKey key = meKey.OpenSubKey(keyName))
{
if(!string.IsNullOrEmpty(key.GetValue("")?.ToString()))
{
this.AddItem(new IEItem(key.Name));
names.Add(keyName);
}
}
}
}
}
}
}
private void AddNewItem()
{
NewItem newItem = new NewItem();
this.AddItem(newItem);
newItem.AddNewItem += () =>
{
using(NewIEDialog dlg = new NewIEDialog())
{
if(dlg.ShowDialog() != DialogResult.OK) return;
this.InsertItem(new IEItem(dlg.RegPath), 1);
}
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs
================================================
using BluePointLilac.Controls;
using ContextMenuManager.Methods;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface IBtnDeleteItem
{
DeleteButton BtnDelete { get; set; }
void DeleteMe();
}
sealed class DeleteButton : PictureButton
{
public DeleteButton(IBtnDeleteItem item) : base(AppImage.Delete)
{
MyListItem listItem = (MyListItem)item;
listItem.AddCtr(this);
this.MouseDown += (sender, e) =>
{
if(AppMessageBox.Show(AppString.Message.ConfirmDelete, MessageBoxButtons.YesNo) == DialogResult.Yes) item.DeleteMe();
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs
================================================
using BluePointLilac.Controls;
using ContextMenuManager.Methods;
namespace ContextMenuManager.Controls.Interfaces
{
interface IBtnMoveUpDownItem
{
MoveButton BtnMoveUp { get; set; }
MoveButton BtnMoveDown { get; set; }
}
sealed class MoveButton : PictureButton
{
public MoveButton(IBtnMoveUpDownItem item, bool isUp) : base(isUp ? AppImage.Up : AppImage.Down)
{
((MyListItem)item).AddCtr(this);
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs
================================================
using BluePointLilac.Controls;
using ContextMenuManager.Methods;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface IBtnShowMenuItem
{
ContextMenuStrip ContextMenuStrip { get; set; }
MenuButton BtnShowMenu { get; set; }
}
sealed class MenuButton : PictureButton
{
public MenuButton(IBtnShowMenuItem item) : base(AppImage.Setting)
{
item.ContextMenuStrip = new ContextMenuStrip();
((MyListItem)item).AddCtr(this);
bool isShow = false;
this.MouseDown += (sender, e) =>
{
if(!isShow) item.ContextMenuStrip.Show(this, 0, Height);
else item.ContextMenuStrip.Close();
isShow = !isShow;
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs
================================================
using BluePointLilac.Controls;
using ContextMenuManager.Methods;
namespace ContextMenuManager.Controls.Interfaces
{
interface IChkVisibleItem
{
bool ItemVisible { get; set; }
VisibleCheckBox ChkVisible { get; set; }
}
sealed class VisibleCheckBox : MyCheckBox
{
public VisibleCheckBox(IChkVisibleItem item)
{
MyListItem listItem = (MyListItem)item;
listItem.AddCtr(this);
this.CheckChanged += () => item.ItemVisible = this.Checked;
listItem.ParentChanged += (sender, e) =>
{
if(listItem.IsDisposed) return;
if(listItem.Parent == null) return;
this.Checked = item.ItemVisible;
if(listItem is FoldSubItem subItem && subItem.FoldGroupItem != null) return;
if(listItem.FindForm() is ShellStoreDialog.ShellStoreForm) return;
if(AppConfig.HideDisabledItems) listItem.Visible = this.Checked;
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/IProtectOpenItem.cs
================================================
namespace ContextMenuManager.Controls.Interfaces
{
interface IProtectOpenItem
{
bool ItemVisible { get; set; }
bool TryProtectOpenItem();
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.IO;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiAdministratorItem
{
ContextMenuStrip ContextMenuStrip { get; set; }
RunAsAdministratorItem TsiAdministrator { get; set; }
ShellLink ShellLink { get; }
}
sealed class RunAsAdministratorItem : ToolStripMenuItem
{
public RunAsAdministratorItem(ITsiAdministratorItem item) : base(AppString.Menu.RunAsAdministrator)
{
item.ContextMenuStrip.Opening += (sender, e) =>
{
if(item.ShellLink == null)
{
this.Enabled = false;
return;
}
string filePath = item.ShellLink.TargetPath;
string extension = Path.GetExtension(filePath)?.ToLower();
switch(extension)
{
case ".exe":
case ".bat":
case ".cmd":
this.Enabled = true;
break;
default:
this.Enabled = false;
break;
}
this.Checked = item.ShellLink.RunAsAdministrator;
};
this.Click += (sender, e) =>
{
item.ShellLink.RunAsAdministrator = !this.Checked;
item.ShellLink.Save();
if(item is WinXItem) ExplorerRestarter.Show();
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiCommandItem
{
string ItemCommand { get; set; }
ChangeCommandMenuItem TsiChangeCommand { get; set; }
}
sealed class ChangeCommandMenuItem : ToolStripMenuItem
{
public bool CommandCanBeEmpty { get; set; }
public ChangeCommandMenuItem(ITsiCommandItem item) : base(AppString.Menu.ChangeCommand)
{
this.Click += (sender, e) =>
{
string command = ChangeCommand(item.ItemCommand);
if(command != null) item.ItemCommand = command;
};
}
private string ChangeCommand(string command)
{
using(InputDialog dlg = new InputDialog())
{
dlg.Text = command;
dlg.Title = AppString.Menu.ChangeCommand;
dlg.Size = new Size(530, 260).DpiZoom();
if(dlg.ShowDialog() != DialogResult.OK) return null;
if(!CommandCanBeEmpty && string.IsNullOrEmpty(dlg.Text))
{
AppMessageBox.Show(AppString.Message.CommandCannotBeEmpty);
return ChangeCommand(command);
}
else return dlg.Text;
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.IO;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiDeleteItem
{
DeleteMeMenuItem TsiDeleteMe { get; set; }
void DeleteMe();
}
interface ITsiRegDeleteItem : ITsiDeleteItem
{
string Text { get; }
string RegPath { get; }
}
sealed class DeleteMeMenuItem : ToolStripMenuItem
{
public DeleteMeMenuItem(ITsiDeleteItem item) : base(AppString.Menu.Delete)
{
this.Click += (sender, e) =>
{
if(item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup)
{
if(AppMessageBox.Show(AppString.Message.DeleteButCanRestore,
MessageBoxButtons.YesNo) != DialogResult.Yes) return;
string date = DateTime.Today.ToString("yyyy-MM-dd");
string time = DateTime.Now.ToString("HH.mm.ss");
string filePath = $@"{AppConfig.BackupDir}\{date}\{regItem.Text} - {time}.reg";
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
ExternalProgram.ExportRegistry(regItem.RegPath, filePath);
}
else if(AppMessageBox.Show(AppString.Message.ConfirmDeletePermanently,
MessageBoxButtons.YesNo) != DialogResult.Yes) return;
MyListItem listItem = (MyListItem)item;
MyList list = (MyList)listItem.Parent;
int index = list.GetItemIndex(listItem);
if(index == list.Controls.Count - 1) index--;
try
{
item.DeleteMe();
}
catch
{
AppMessageBox.Show(AppString.Message.AuthorityProtection);
return;
}
list.Controls.Remove(listItem);
list.Controls[index].Focus();
listItem.Dispose();
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.IO;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiFilePathItem
{
string ItemFilePath { get; }
ContextMenuStrip ContextMenuStrip { get; set; }
FileLocationMenuItem TsiFileLocation { get; set; }
FilePropertiesMenuItem TsiFileProperties { get; set; }
}
sealed class FileLocationMenuItem : ToolStripMenuItem
{
public FileLocationMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileLocation)
{
item.ContextMenuStrip.Opening += (sender, e) =>
{
this.Visible = item.ItemFilePath != null;
};
this.Click += (sender, e) => ExternalProgram.JumpExplorer(item.ItemFilePath, AppConfig.OpenMoreExplorer);
}
}
sealed class FilePropertiesMenuItem : ToolStripMenuItem
{
public FilePropertiesMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileProperties)
{
item.ContextMenuStrip.Opening += (sender, e) =>
{
string path = item.ItemFilePath;
this.Visible = Directory.Exists(path) || File.Exists(path);
};
this.Click += (sender, e) => ExternalProgram.ShowPropertiesDialog(item.ItemFilePath);
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiGuidItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiGuidItem
{
Guid Guid { get; }
string ItemText { get; }
HandleGuidMenuItem TsiHandleGuid { get; set; }
DetailedEditButton BtnDetailedEdit { get; set; }
}
sealed class HandleGuidMenuItem : ToolStripMenuItem
{
public HandleGuidMenuItem(ITsiGuidItem item) : base(AppString.Menu.HandleGuid)
{
this.Item = item;
this.DropDownItems.AddRange(new ToolStripItem[] { TsiAddGuidDic,
new ToolStripSeparator(), TsiCopyGuid, TsiBlockGuid, TsiClsidLocation });
TsiCopyGuid.Click += (sender, e) => CopyGuid();
TsiBlockGuid.Click += (sender, e) => BlockGuid();
TsiAddGuidDic.Click += (sender, e) => AddGuidDic();
TsiClsidLocation.Click += (sender, e) => OpenClsidPath();
((MyListItem)item).ContextMenuStrip.Opening += (sender, e) => RefreshMenuItem();
}
readonly ToolStripMenuItem TsiCopyGuid = new ToolStripMenuItem(AppString.Menu.CopyGuid);
readonly ToolStripMenuItem TsiBlockGuid = new ToolStripMenuItem(AppString.Menu.BlockGuid);
readonly ToolStripMenuItem TsiAddGuidDic = new ToolStripMenuItem(AppString.Menu.AddGuidDic);
readonly ToolStripMenuItem TsiClsidLocation = new ToolStripMenuItem(AppString.Menu.ClsidLocation);
public ITsiGuidItem Item { get; set; }
private void CopyGuid()
{
string guid = Item.Guid.ToString("B");
Clipboard.SetText(guid);
AppMessageBox.Show($"{AppString.Message.CopiedToClipboard}\n{guid}",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void BlockGuid()
{
foreach(string path in GuidBlockedList.BlockedPaths)
{
if(TsiBlockGuid.Checked)
{
RegistryEx.DeleteValue(path, Item.Guid.ToString("B"));
}
else
{
if(Item.Guid.Equals(ShellExItem.LnkOpenGuid) && AppConfig.ProtectOpenItem)
{
if(AppMessageBox.Show(AppString.Message.PromptIsOpenItem,
MessageBoxButtons.YesNo) != DialogResult.Yes) return;
}
Microsoft.Win32.Registry.SetValue(path, Item.Guid.ToString("B"), string.Empty);
}
}
ExplorerRestarter.Show();
}
private void AddGuidDic()
{
using(AddGuidDicDialog dlg = new AddGuidDicDialog())
{
dlg.ItemText = GuidInfo.GetText(Item.Guid);
dlg.ItemIcon = GuidInfo.GetImage(Item.Guid);
var location = GuidInfo.GetIconLocation(Item.Guid);
dlg.ItemIconPath = location.IconPath;
dlg.ItemIconIndex = location.IconIndex;
IniWriter writer = new IniWriter
{
FilePath = AppConfig.UserGuidInfosDic,
DeleteFileWhenEmpty = true
};
string section = Item.Guid.ToString();
MyListItem listItem = (MyListItem)Item;
if(dlg.ShowDialog() != DialogResult.OK)
{
if(dlg.IsDelete)
{
writer.DeleteSection(section);
GuidInfo.RemoveDic(Item.Guid);
listItem.Text = Item.ItemText;
listItem.Image = GuidInfo.GetImage(Item.Guid);
}
return;
}
if(dlg.ItemText.IsNullOrWhiteSpace())
{
AppMessageBox.Show(AppString.Message.TextCannotBeEmpty);
return;
}
dlg.ItemText = ResourceString.GetDirectString(dlg.ItemText);
if(dlg.ItemText.IsNullOrWhiteSpace())
{
AppMessageBox.Show(AppString.Message.StringParsingFailed);
return;
}
else
{
GuidInfo.RemoveDic(Item.Guid);
writer.SetValue(section, "Text", dlg.ItemText);
writer.SetValue(section, "Icon", dlg.ItemIconLocation);
listItem.Text = dlg.ItemText;
listItem.Image = dlg.ItemIcon;
}
}
}
private void OpenClsidPath()
{
string clsidPath = GuidInfo.GetClsidPath(Item.Guid);
ExternalProgram.JumpRegEdit(clsidPath, null, AppConfig.OpenMoreRegedit);
}
private void RefreshMenuItem()
{
TsiClsidLocation.Visible = GuidInfo.GetClsidPath(Item.Guid) != null;
TsiBlockGuid.Visible = TsiBlockGuid.Checked = false;
if(Item is ShellExItem)
{
TsiBlockGuid.Visible = true;
foreach(string path in GuidBlockedList.BlockedPaths)
{
if(Microsoft.Win32.Registry.GetValue(path, Item.Guid.ToString("B"), null) != null)
{
TsiBlockGuid.Checked = true; break;
}
}
}
}
sealed class AddGuidDicDialog : CommonDialog
{
public Image ItemIcon { get; set; }
public string ItemText { get; set; }
public bool IsDelete { get; private set; }
public string ItemIconPath { get; set; }
public int ItemIconIndex { get; set; }
public string ItemIconLocation
{
get
{
if(ItemIconPath == null) return null;
return $"{ItemIconPath},{ItemIconIndex}";
}
}
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(AddGuidDicForm frm = new AddGuidDicForm())
{
frm.ItemText = this.ItemText;
frm.ItemIcon = this.ItemIcon;
frm.ItemIconPath = this.ItemIconPath;
frm.ItemIconIndex = this.ItemIconIndex;
frm.TopMost = AppConfig.TopMost;
bool flag = frm.ShowDialog() == DialogResult.OK;
if(flag)
{
this.ItemText = frm.ItemText;
this.ItemIcon = frm.ItemIcon;
this.ItemIconPath = frm.ItemIconPath;
this.ItemIconIndex = frm.ItemIconIndex;
}
this.IsDelete = frm.IsDelete;
return flag;
}
}
sealed class AddGuidDicForm : Form
{
public AddGuidDicForm()
{
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
this.Font = SystemFonts.MenuFont;
this.Text = AppString.Dialog.AddGuidDic;
this.ShowIcon = this.ShowInTaskbar = false;
this.MaximizeBox = this.MinimizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.StartPosition = FormStartPosition.CenterParent;
InitializeComponents();
}
public string ItemText
{
get => txtName.Text;
set => txtName.Text = value;
}
public Image ItemIcon
{
get => picIcon.Image;
set => picIcon.Image = value;
}
public string ItemIconPath { get; set; }
public int ItemIconIndex { get; set; }
public bool IsDelete { get; private set; }
readonly TextBox txtName = new TextBox();
readonly Label lblName = new Label
{
Text = AppString.Dialog.ItemText,
AutoSize = true
};
readonly Label lblIcon = new Label
{
Text = AppString.Menu.ItemIcon,
AutoSize = true
};
readonly PictureBox picIcon = new PictureBox
{
Size = SystemInformation.IconSize
};
readonly Button btnBrowse = new Button
{
Text = AppString.Dialog.Browse,
AutoSize = true
};
readonly Button btnOK = new Button
{
Text = ResourceString.OK,
DialogResult = DialogResult.OK,
AutoSize = true
};
readonly Button btnCancel = new Button
{
Text = ResourceString.Cancel,
DialogResult = DialogResult.Cancel,
AutoSize = true
};
readonly Button btnDelete = new Button
{
Text = AppString.Dialog.DeleteGuidDic,
DialogResult = DialogResult.Cancel,
AutoSize = true
};
private void InitializeComponents()
{
this.Controls.AddRange(new Control[] { lblName, txtName, lblIcon, picIcon, btnBrowse, btnDelete, btnOK, btnCancel });
int a = 20.DpiZoom();
lblName.Left = lblName.Top = lblIcon.Left = btnDelete.Left = txtName.Top = a;
txtName.Left = lblName.Right + a;
btnOK.Left = btnDelete.Right + a;
btnCancel.Left = btnOK.Right + a;
txtName.Width = btnCancel.Right - txtName.Left;
btnBrowse.Left = btnCancel.Right - btnBrowse.Width;
picIcon.Left = btnOK.Left + (btnOK.Width - picIcon.Width) / 2;
btnBrowse.Top = txtName.Bottom + a;
picIcon.Top = btnBrowse.Top + (btnBrowse.Height - picIcon.Height) / 2;
lblIcon.Top = btnBrowse.Top + (btnBrowse.Height - lblIcon.Height) / 2;
btnDelete.Top = btnOK.Top = btnCancel.Top = btnBrowse.Bottom + a;
this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a);
ToolTipBox.SetToolTip(btnDelete, AppString.Tip.DeleteGuidDic);
btnBrowse.Click += (sender, e) => SelectIcon();
btnDelete.Click += (sender, e) => this.IsDelete = true;
}
private void SelectIcon()
{
using(IconDialog dlg = new IconDialog())
{
dlg.IconPath = this.ItemIconPath;
dlg.IconIndex = this.ItemIconIndex;
if(dlg.ShowDialog() != DialogResult.OK) return;
using(Icon icon = ResourceIcon.GetIcon(dlg.IconPath, dlg.IconIndex))
{
Image image = icon?.ToBitmap();
if(image == null) return;
picIcon.Image = image;
this.ItemIconPath = dlg.IconPath;
this.ItemIconIndex = dlg.IconIndex;
}
}
}
}
}
}
sealed class DetailedEditButton : PictureButton
{
public DetailedEditButton(ITsiGuidItem item) : base(AppImage.SubItems)
{
MyListItem listItem = (MyListItem)item;
listItem.AddCtr(this);
ToolTipBox.SetToolTip(this, AppString.SideBar.DetailedEdit);
listItem.ParentChanged += (sender, e) =>
{
if(listItem.IsDisposed) return;
if(listItem.Parent == null) return;
this.Visible = XmlDicHelper.DetailedEditGuidDic.ContainsKey(item.Guid);
};
this.MouseDown += (sender, e) =>
{
using(DetailedEditDialog dlg = new DetailedEditDialog())
{
dlg.GroupGuid = item.Guid;
dlg.ShowDialog();
}
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiIconItem
{
ChangeIconMenuItem TsiChangeIcon { get; set; }
string IconLocation { get; set; }
string IconPath { get; set; }
int IconIndex { get; set; }
Image Image { get; set; }
Icon ItemIcon { get; }
}
sealed class ChangeIconMenuItem : ToolStripMenuItem
{
public ChangeIconMenuItem(ITsiIconItem item) : base(AppString.Menu.ChangeIcon)
{
this.Click += (sender, e) =>
{
using(IconDialog dlg = new IconDialog())
{
dlg.IconPath = item.IconPath;
dlg.IconIndex = item.IconIndex;
if(dlg.ShowDialog() != DialogResult.OK) return;
using(Icon icon = ResourceIcon.GetIcon(dlg.IconPath, dlg.IconIndex))
{
Image image = icon?.ToBitmap();
if(image == null) return;
item.Image = image;
item.IconPath = dlg.IconPath;
item.IconIndex = dlg.IconIndex;
item.IconLocation = $"{dlg.IconPath},{dlg.IconIndex}";
}
}
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.IO;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiRegExportItem
{
string Text { get; set; }
string RegPath { get; }
ContextMenuStrip ContextMenuStrip { get; set; }
RegExportMenuItem TsiRegExport { get; set; }
}
sealed class RegExportMenuItem : ToolStripMenuItem
{
public RegExportMenuItem(ITsiRegExportItem item) : base(AppString.Menu.ExportRegistry)
{
item.ContextMenuStrip.Opening += (sender, e) =>
{
using(var key = RegistryEx.GetRegistryKey(item.RegPath))
this.Visible = key != null;
};
this.Click += (sender, e) =>
{
using(SaveFileDialog dlg = new SaveFileDialog())
{
string date = DateTime.Today.ToString("yyyy-MM-dd");
string time = DateTime.Now.ToString("HH.mm.ss");
string filePath = $@"{AppConfig.BackupDir}\{date}\{item.Text} - {time}.reg";
string dirPath = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
Directory.CreateDirectory(dirPath);
dlg.FileName = fileName;
dlg.InitialDirectory = dirPath;
dlg.Filter = $"{AppString.Dialog.RegistryFile}|*.reg";
if(dlg.ShowDialog() == DialogResult.OK)
{
ExternalProgram.ExportRegistry(item.RegPath, dlg.FileName);
}
if(Directory.GetFileSystemEntries(dirPath).Length == 0)
{
Directory.Delete(dirPath);
}
}
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiRegPathItem
{
string RegPath { get; }
string ValueName { get; }
ContextMenuStrip ContextMenuStrip { get; set; }
RegLocationMenuItem TsiRegLocation { get; set; }
}
sealed class RegLocationMenuItem : ToolStripMenuItem
{
public RegLocationMenuItem(ITsiRegPathItem item) : base(AppString.Menu.RegistryLocation)
{
this.Click += (sender, e) => ExternalProgram.JumpRegEdit(item.RegPath, item.ValueName, AppConfig.OpenMoreRegedit);
item.ContextMenuStrip.Opening += (sender, e) =>
{
using(var key = RegistryEx.GetRegistryKey(item.RegPath))
this.Visible = key != null;
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiShortcutCommandItem
{
ShellLink ShellLink { get; }
ShortcutCommandMenuItem TsiChangeCommand { get; set; }
ContextMenuStrip ContextMenuStrip { get; set; }
}
sealed class ShortcutCommandMenuItem : ToolStripMenuItem
{
public ShortcutCommandMenuItem(ITsiShortcutCommandItem item) : base(AppString.Menu.ChangeCommand)
{
item.ContextMenuStrip.Opening += (sender, e) =>
{
this.Visible = !string.IsNullOrEmpty(item.ShellLink?.TargetPath);
};
}
public bool ChangeCommand(ShellLink shellLink)
{
using(CommandDialog dlg = new CommandDialog())
{
dlg.Command = shellLink.TargetPath;
dlg.Arguments = shellLink.Arguments;
if(dlg.ShowDialog() != DialogResult.OK) return false;
shellLink.TargetPath = dlg.Command;
shellLink.Arguments = dlg.Arguments;
shellLink.Save();
return true;
}
}
sealed class CommandDialog : CommonDialog
{
public string Command { get; set; }
public string Arguments { get; set; }
public override void Reset() { }
protected override bool RunDialog(IntPtr hwndOwner)
{
using(CommandForm frm = new CommandForm())
{
frm.Command = this.Command;
frm.Arguments = this.Arguments;
frm.TopMost = AppConfig.TopMost;
bool flag = frm.ShowDialog() == DialogResult.OK;
if(flag)
{
this.Command = frm.Command;
this.Arguments = frm.Arguments;
}
return flag;
}
}
sealed class CommandForm : ResizeLimitedForm
{
public CommandForm()
{
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
this.VerticalResizable = false;
this.Font = SystemFonts.MessageBoxFont;
this.Text = AppString.Menu.ChangeCommand;
this.SizeGripStyle = SizeGripStyle.Hide;
this.StartPosition = FormStartPosition.CenterParent;
this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false;
InitializeComponents();
}
public string Command
{
get => txtCommand.Text;
set => txtCommand.Text = value;
}
public string Arguments
{
get => txtArguments.Text;
set => txtArguments.Text = value;
}
readonly Label lblCommand = new Label
{
Text = AppString.Dialog.ItemCommand,
AutoSize = true
};
readonly Label lblArguments = new Label
{
Text = AppString.Dialog.CommandArguments,
AutoSize = true
};
readonly TextBox txtCommand = new TextBox();
readonly TextBox txtArguments = new TextBox();
readonly Button btnOK = new Button
{
DialogResult = DialogResult.OK,
Text = ResourceString.OK,
AutoSize = true
};
readonly Button btnCancel = new Button
{
DialogResult = DialogResult.Cancel,
Text = ResourceString.Cancel,
AutoSize = true
};
private void InitializeComponents()
{
this.Controls.AddRange(new Control[] { lblCommand, lblArguments, txtCommand, txtArguments, btnOK, btnCancel });
int a = 20.DpiZoom();
lblArguments.Left = lblCommand.Left = lblCommand.Top = txtCommand.Top = a;
lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a;
btnOK.Top = btnCancel.Top = txtArguments.Bottom + a;
int b = Math.Max(lblCommand.Width, lblArguments.Width) + 3 * a;
this.ClientSize = new Size(250.DpiZoom() + b, btnOK.Bottom + a);
btnOK.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top;
btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a;
btnOK.Left = btnCancel.Left - btnOK.Width - a;
this.Resize += (sender, e) =>
{
txtArguments.Width = txtCommand.Width = this.ClientSize.Width - b;
txtArguments.Left = txtCommand.Left = btnCancel.Right - txtCommand.Width;
};
this.OnResize(null);
this.MinimumSize = this.Size;
}
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiTextItem
{
string Text { get; set; }
string ItemText { get; set; }
ChangeTextMenuItem TsiChangeText { get; set; }
}
sealed class ChangeTextMenuItem : ToolStripMenuItem
{
public ChangeTextMenuItem(ITsiTextItem item) : base(AppString.Menu.ChangeText)
{
this.Click += (sender, e) =>
{
string name = ChangeText(item.Text);
if(name != null) item.ItemText = name;
};
}
private string ChangeText(string text)
{
using(InputDialog dlg = new InputDialog { Text = text, Title = AppString.Menu.ChangeText })
{
if(dlg.ShowDialog() != DialogResult.OK) return null;
if(dlg.Text.Length == 0)
{
AppMessageBox.Show(AppString.Message.TextCannotBeEmpty);
return ChangeText(text);
}
else if(ResourceString.GetDirectString(dlg.Text).Length == 0)
{
AppMessageBox.Show(AppString.Message.StringParsingFailed);
return ChangeText(text);
}
else return dlg.Text;
}
}
}
}
================================================
FILE: ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs
================================================
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System.Windows.Forms;
namespace ContextMenuManager.Controls.Interfaces
{
interface ITsiWebSearchItem
{
string SearchText { get; }
WebSearchMenuItem TsiSearch { get; set; }
}
sealed class WebSearchMenuItem : ToolStripMenuItem
{
public WebSearchMenuItem(ITsiWebSearchItem item) : base(AppString.Menu.WebSearch)
{
this.Click += (sender, e) =>
{
string url = AppConfig.EngineUrl.Replace("%s", item.SearchText);
ExternalProgram.OpenWebUrl(url);
};
}
}
}
================================================
FILE: ContextMenuManager/Controls/LanguagesBox.cs
================================================
using BluePointLilac.Controls;
using BluePointLilac.Methods;
using ContextMenuManager.Methods;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
namespace ContextMenuManager.Controls
{
sealed class LanguagesBox : FlowLayoutPanel
{
public LanguagesBox()
{
this.SuspendLayout();
this.Dock = DockStyle.Fill;
this.Font = SystemFonts.MenuFont;
this.Font = new Font(this.Font.FontFamily, this.Font.Size + 1F);
this.Controls.AddRange(new Control[] { cmbLanguages, btnOpenDir, btnDownLoad, btnTranslate, lblThank, pnlTranslators });
this.VisibleChanged += (sender, e) => this.SetEnabled(this.Visible);
cmbLanguages.SelectionChangeCommitted += (sender, e) => ChangeLanguage();
btnOpenDir.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(AppConfig.LangsDir);
lblThank.MouseEnter += (sender, e) => lblThank.ForeColor = Color.FromArgb(0, 162, 255);
lblThank.MouseLeave += (sender, e) => lblThank.ForeColor = Color.DimGray;
btnDownLoad.MouseDown += (sender, e) =>
{
this.Cursor = Cursors.WaitCursor;
this.ShowLanguageDialog();
this.Cursor = Cursors.Default;
};
btnTranslate.MouseDown += (sender, e) =>
{
using(TranslateDialog dlg = new TranslateDialog())
{
dlg.ShowDialog();
}
};
ToolTipBox.SetToolTip(btnOpenDir, AppString.Menu.FileLocation);
ToolTipBox.SetToolTip(btnDownLoad, AppString.Dialog.DownloadLanguages);
ToolTipBox.SetToolTip(btnTranslate, AppString.Dialog.TranslateTool);
lblHeader.Font = new Font(this.Font, FontStyle.Bold);
cmbLanguages.AutosizeDropDownWidth();
this.OnResize(null);
this.ResumeLayout();
}
readonly ComboBox cmbLanguages = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Width = 170.DpiZoom(),
};
readonly PictureButton btnOpenDir = new PictureButton(AppImage.Open);
readonly PictureButton btnDownLoad = new PictureButton(AppImage.DownLoad);
readonly PictureButton btnTranslate = new PictureButton(AppImage.Translate);
readonly ToolTip toolTip = new ToolTip { InitialDelay = 1 };
readonly Panel pnlTranslators = new Panel
{
BorderStyle = BorderStyle.FixedSingle,
AutoScroll = true
};
readonly Label lblHeader = new Label
{
Text = AppString.Other.Translators + "\r\n" + new string('-', 96),
ForeColor = Color.DarkCyan,
Dock = DockStyle.Top,
AutoSize = true
};
readonly Label lblThank = new Label
{
Font = new Font("Lucida Handwriting", 11F),
Text = "Thank you for your translation!",
ForeColor = Color.DimGray,
AutoSize = true,
};
readonly List languages = new List();
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
int a = 20.DpiZoom();
pnlTranslators.Width = this.ClientSize.Width - 2 * a;
pnlTranslators.Height = this.ClientSize.Height - pnlTranslators.Top - a;
cmbLanguages.Margin = pnlTranslators.Margin = lblThank.Margin = btnOpenDir.Margin
= btnDownLoad.Margin = btnTranslate.Margin = new Padding(a, a, 0, 0);
}
public void LoadLanguages()
{
cmbLanguages.Items.Clear();
cmbLanguages.Items.Add("(default) 简体中文");
languages.Clear();
languages.Add("default");
pnlTranslators.SuspendLayout();
pnlTranslators.Controls.Remove(lblHeader);
foreach(Control ctr in pnlTranslators.Controls) ctr.Dispose();
pnlTranslators.Controls.Clear();
pnlTranslators.Controls.Add(lblHeader);
if(Directory.Exists(AppConfig.LangsDir))
{
Dictionary