diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100755 index abf36c5..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100755 index abf36c5..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs new file mode 100755 index 0000000..08a160a --- /dev/null +++ b/SetLnkApp/Program.cs @@ -0,0 +1,34 @@ +using ShellLinkPlus; +using System; +using System.IO; +using System.Linq; + +namespace SetLnkApp +{ + class Program + { + static void Main(string[] args) + { + bool existing = File.Exists(args[0]); + using (ShellLink shortcut = new ShellLink(existing ? args[0] : null)) + { + if (existing) + { + if (args.Length > 2) + { + throw new Exception("For an existing shortcut, only pass AppUserModelId"); + } + } + else + { + shortcut.TargetPath = args[2]; + //shortcut.Arguments = args.Length > 3 ? string.Join(" ", args.Skip(3)) : ""; + shortcut.Arguments = string.Join(" ", args.Skip(3)); + } + shortcut.AppUserModelID = args[1]; + + shortcut.Save(args[0]); + } + } + } +} diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100755 index abf36c5..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs new file mode 100755 index 0000000..08a160a --- /dev/null +++ b/SetLnkApp/Program.cs @@ -0,0 +1,34 @@ +using ShellLinkPlus; +using System; +using System.IO; +using System.Linq; + +namespace SetLnkApp +{ + class Program + { + static void Main(string[] args) + { + bool existing = File.Exists(args[0]); + using (ShellLink shortcut = new ShellLink(existing ? args[0] : null)) + { + if (existing) + { + if (args.Length > 2) + { + throw new Exception("For an existing shortcut, only pass AppUserModelId"); + } + } + else + { + shortcut.TargetPath = args[2]; + //shortcut.Arguments = args.Length > 3 ? string.Join(" ", args.Skip(3)) : ""; + shortcut.Arguments = string.Join(" ", args.Skip(3)); + } + shortcut.AppUserModelID = args[1]; + + shortcut.Save(args[0]); + } + } + } +} diff --git a/SetLnkApp/Properties/launchSettings.json b/SetLnkApp/Properties/launchSettings.json new file mode 100755 index 0000000..420478a --- /dev/null +++ b/SetLnkApp/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "SetLnkApp": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100755 index abf36c5..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs new file mode 100755 index 0000000..08a160a --- /dev/null +++ b/SetLnkApp/Program.cs @@ -0,0 +1,34 @@ +using ShellLinkPlus; +using System; +using System.IO; +using System.Linq; + +namespace SetLnkApp +{ + class Program + { + static void Main(string[] args) + { + bool existing = File.Exists(args[0]); + using (ShellLink shortcut = new ShellLink(existing ? args[0] : null)) + { + if (existing) + { + if (args.Length > 2) + { + throw new Exception("For an existing shortcut, only pass AppUserModelId"); + } + } + else + { + shortcut.TargetPath = args[2]; + //shortcut.Arguments = args.Length > 3 ? string.Join(" ", args.Skip(3)) : ""; + shortcut.Arguments = string.Join(" ", args.Skip(3)); + } + shortcut.AppUserModelID = args[1]; + + shortcut.Save(args[0]); + } + } + } +} diff --git a/SetLnkApp/Properties/launchSettings.json b/SetLnkApp/Properties/launchSettings.json new file mode 100755 index 0000000..420478a --- /dev/null +++ b/SetLnkApp/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "SetLnkApp": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj new file mode 100755 index 0000000..d453e9a --- /dev/null +++ b/SetLnkApp/SetLnkApp.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/App.config b/App.config deleted file mode 100755 index 5754728..0000000 --- a/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100755 index 0f447ee..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JumpListTest -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/Form1.cs b/Form1.cs deleted file mode 100755 index 74b2085..0000000 --- a/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace JumpListTest -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/JumpListTest.csproj b/JumpListTest.csproj deleted file mode 100755 index 9ad6f29..0000000 --- a/JumpListTest.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} - WinExe - JumpListTest - JumpListTest - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/JumpListTest.sln b/JumpListTest.sln deleted file mode 100755 index ad79cd7..0000000 --- a/JumpListTest.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListTest", "JumpListTest.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} - EndGlobalSection -EndGlobal diff --git a/JumpListUtil.sln b/JumpListUtil.sln new file mode 100755 index 0000000..c91e4de --- /dev/null +++ b/JumpListUtil.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JumpListUtil", "JumpListUtil\JumpListUtil.csproj", "{66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetLnkApp", "SetLnkApp\SetLnkApp.csproj", "{D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64CB67B-A07A-4A10-8CB3-83C5F4D3FF3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {60F13606-E5AC-4632-9273-AED10038EAB1} + EndGlobalSection +EndGlobal diff --git a/JumpListUtil/App.config b/JumpListUtil/App.config new file mode 100755 index 0000000..de2fc4c --- /dev/null +++ b/JumpListUtil/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj new file mode 100755 index 0000000..5f31de9 --- /dev/null +++ b/JumpListUtil/JumpListUtil.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2} + WinExe + JumpListUtil + JumpListUtil + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs new file mode 100755 index 0000000..a13d723 --- /dev/null +++ b/JumpListUtil/Program.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Shell; + +namespace JumpListUtil +{ + public class Entry + { + public string Target { get; set; } + public string Icon { get; set; } + public int IconIndex { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } + static class Program + { + [DllImport("shell32.dll", SetLastError = true)] + static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + System.Windows.Application wpfApp = new System.Windows.Application(); + string thisProgram = Process.GetCurrentProcess().MainModule.FileName; + + // parse all arguments + string shortcutsFile = "shortcuts.json"; + string appUserModelId = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; + int startIndex = 0; + bool onlyRefreshJumpList = false; + + int i = 0; + string[] args = Environment.GetCommandLineArgs(); + bool ParseFlag(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length > 2) + { + args[i] = "-" + args[i].Substring(2); + i--; + } + return true; + } + else if (args[i] == "--" + longName) + { + return true; + } + return false; + } + string ParseArg(char shortName, string longName) + { + if (args[i].IndexOf("-" + shortName) == 0) + { + if (args[i].Length == 2) + { + i++; + return args[i]; + } + else + { + return args[i].Substring(2); + } + } + else if (args[i].IndexOf("--" + longName + "=") == 0) + { + return args[i].Substring(longName.Length + 3); + } + return null; + } + + string val = null; + for (i = 1; i < args.Length; i++) + { + if ((val = ParseArg('s', "shortcuts")) != null) + { + shortcutsFile = val; + } + else if ((val = ParseArg('i', "startIndex")) != null) + { + startIndex = int.Parse(val); + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if ((val = ParseArg('a', "appUserModelId")) != null) + { + appUserModelId = val; + } + else if (ParseFlag('r', "refresh")) + { + onlyRefreshJumpList = true; + } + } + + SetCurrentProcessExplicitAppUserModelID(appUserModelId); + + List jumpItems = new List(); + + Entry[] entries = null; + + try + { + string shortcutJson = ""; + using (FileStream input = File.OpenRead(shortcutsFile)) + { + shortcutJson = new StreamReader(input).ReadToEnd(); + } + + + entries = (Entry[])JsonSerializer.Deserialize(shortcutJson, typeof(Entry[]), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); + } + catch (Exception e) + { + MessageBox.Show("Failed to load shortcut entries: " + e); + } + + if (entries != null) + { + foreach (Entry entry in entries) + { + JumpTask task = new JumpTask(); + task.ApplicationPath = entry.Target; + task.Description = entry.Description; + task.Title = entry.Name; + task.IconResourcePath = entry.Icon; + task.IconResourceIndex = entry.IconIndex; + + jumpItems.Add(task); + } + + jumpItems.Insert(0, new JumpTask + { + Title = "Refresh", + Description = "Refreshes the Jump List", + ApplicationPath = thisProgram, + WorkingDirectory = Environment.CurrentDirectory, + IconResourcePath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\imageres.dll"), + IconResourceIndex = 228, + Arguments = "-r" + }); + + JumpList.SetJumpList(wpfApp, new JumpList(jumpItems, false, false)); + + if (!onlyRefreshJumpList) + { + if (startIndex >= 0 && startIndex < entries.Length) + { + Entry start = entries[startIndex]; + Process p = Process.Start(new ProcessStartInfo + { + FileName = start.Target, + UseShellExecute = true + }); + } + else + { + MessageBox.Show("Shortcut index " + startIndex + " is outside the range of valid entries (" + entries.Length + " entries)"); + } + } + } + + //Application.EnableVisualStyles(); + //Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(); + } + } +} diff --git a/JumpListUtil/Properties/AssemblyInfo.cs b/JumpListUtil/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..e929884 --- /dev/null +++ b/JumpListUtil/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JumpListUtil")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JumpListUtil")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JumpListUtil/Properties/Resources.Designer.cs b/JumpListUtil/Properties/Resources.Designer.cs new file mode 100755 index 0000000..ed070d5 --- /dev/null +++ b/JumpListUtil/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/JumpListUtil/Properties/Resources.resx b/JumpListUtil/Properties/Resources.resx new file mode 100755 index 0000000..ffecec8 --- /dev/null +++ b/JumpListUtil/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JumpListUtil/Properties/Settings.Designer.cs b/JumpListUtil/Properties/Settings.Designer.cs new file mode 100755 index 0000000..8593114 --- /dev/null +++ b/JumpListUtil/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JumpListUtil.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/JumpListUtil/Properties/Settings.settings b/JumpListUtil/Properties/Settings.settings new file mode 100755 index 0000000..abf36c5 --- /dev/null +++ b/JumpListUtil/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/JumpListUtil/ShellLink.cs b/JumpListUtil/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/JumpListUtil/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +} diff --git a/JumpListUtil/packages.config b/JumpListUtil/packages.config new file mode 100755 index 0000000..abb0b89 --- /dev/null +++ b/JumpListUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs deleted file mode 100755 index 17420a8..0000000 --- a/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Shell; - -namespace JumpListTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - System.Windows.Application wpfApp = new System.Windows.Application(); - JumpTask task = new JumpTask(); - task.ApplicationPath = "wt.exe"; - task.Description = "Work Terminal"; - task.Title = "Terminal"; - JumpList.SetJumpList(wpfApp, new JumpList(new List { task }, false, false)); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(); - - - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100755 index 1b650b3..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JumpListTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JumpListTest")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66f50737-a71d-4d5b-83f2-4f06b9f9f9b2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100755 index f29ecc8..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JumpListTest.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100755 index ffecec8..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100755 index fa4d9ff..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JumpListTest.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100755 index abf36c5..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs new file mode 100755 index 0000000..08a160a --- /dev/null +++ b/SetLnkApp/Program.cs @@ -0,0 +1,34 @@ +using ShellLinkPlus; +using System; +using System.IO; +using System.Linq; + +namespace SetLnkApp +{ + class Program + { + static void Main(string[] args) + { + bool existing = File.Exists(args[0]); + using (ShellLink shortcut = new ShellLink(existing ? args[0] : null)) + { + if (existing) + { + if (args.Length > 2) + { + throw new Exception("For an existing shortcut, only pass AppUserModelId"); + } + } + else + { + shortcut.TargetPath = args[2]; + //shortcut.Arguments = args.Length > 3 ? string.Join(" ", args.Skip(3)) : ""; + shortcut.Arguments = string.Join(" ", args.Skip(3)); + } + shortcut.AppUserModelID = args[1]; + + shortcut.Save(args[0]); + } + } + } +} diff --git a/SetLnkApp/Properties/launchSettings.json b/SetLnkApp/Properties/launchSettings.json new file mode 100755 index 0000000..420478a --- /dev/null +++ b/SetLnkApp/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "SetLnkApp": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj new file mode 100755 index 0000000..d453e9a --- /dev/null +++ b/SetLnkApp/SetLnkApp.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/SetLnkApp/ShellLink.cs b/SetLnkApp/ShellLink.cs new file mode 100755 index 0000000..6d13f4e --- /dev/null +++ b/SetLnkApp/ShellLink.cs @@ -0,0 +1,486 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +/**************************************************************************** + * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ + ***************************************************************************/ + +namespace ShellLinkPlus +{ + // Modified from http://smdn.jp/programming/tips/createlnk/ + // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects + // /Creating_and_Modifying_Shortcuts/article.asp + // Partly based on Sending toast notifications from desktop apps sample + public class ShellLink : IDisposable + { + #region Win32 and COM + + // IShellLink Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046")] + private interface IShellLinkW + { + uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); + uint GetIDList(out IntPtr ppidl); + uint SetIDList(IntPtr pidl); + uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + uint GetHotKey(out ushort pwHotkey); + uint SetHotKey(ushort wHotKey); + uint GetShowCmd(out int piShowCmd); + uint SetShowCmd(int iShowCmd); + uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + uint Resolve(IntPtr hwnd, uint fFlags); + uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // ShellLink CoClass (ShellLink object) + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("00021401-0000-0000-C000-000000000046")] + private class CShellLink { } + + // WIN32_FIND_DATAW Structure + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + private 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; + } + + // IPropertyStore Interface + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + private interface IPropertyStore + { + uint GetCount([Out] out uint cProps); + uint GetAt([In] uint iProp, out PropertyKey pkey); + uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); + uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); + uint Commit(); + } + + // PropertyKey Structure + // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct PropertyKey + { + #region Fields + + private Guid formatId; // Unique GUID for property + private Int32 propertyId; // Property identifier (PID) + + #endregion + + #region Public Properties + + public Guid FormatId + { + get + { + return formatId; + } + } + + public Int32 PropertyId + { + get + { + return propertyId; + } + } + + #endregion + + #region Constructor + + public PropertyKey(Guid formatId, Int32 propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + + public PropertyKey(string formatId, Int32 propertyId) + { + this.formatId = new Guid(formatId); + this.propertyId = propertyId; + } + + #endregion + } + + // PropVariant Class (only for string value) + // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 + // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 + // /interop-with-propvariants-in-net.aspx + [StructLayout(LayoutKind.Explicit)] + private sealed class PropVariant : IDisposable + { + #region Fields + + [FieldOffset(0)] + ushort valueType; // Value type + + // [FieldOffset(2)] + // ushort wReserved1; // Reserved field + // [FieldOffset(4)] + // ushort wReserved2; // Reserved field + // [FieldOffset(6)] + // ushort wReserved3; // Reserved field + + [FieldOffset(8)] + IntPtr ptr; // Value + + #endregion + + #region Public Properties + + // Value type (System.Runtime.InteropServices.VarEnum) + public VarEnum VarType + { + get { return (VarEnum)valueType; } + set { valueType = (ushort)value; } + } + + // Whether value is empty or null + public bool IsNullOrEmpty + { + get + { + return (valueType == (ushort)VarEnum.VT_EMPTY || + valueType == (ushort)VarEnum.VT_NULL); + } + } + + // Value (only for string value) + public string Value + { + get + { + return Marshal.PtrToStringUni(ptr); + } + } + + #endregion + + #region Constructor + + public PropVariant() + { } + + // Construct with string value + public PropVariant(string value) + { + if (value == null) + throw new ArgumentException("Failed to set value."); + + valueType = (ushort)VarEnum.VT_LPWSTR; + ptr = Marshal.StringToCoTaskMemUni(value); + } + + #endregion + + #region Destructor + + ~PropVariant() + { + Dispose(); + } + + public void Dispose() + { + PropVariantClear(this); + GC.SuppressFinalize(this); + } + + #endregion + } + + [DllImport("Ole32.dll", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] PropVariant pvar); + + #endregion + + #region Fields + + private IShellLinkW shellLinkW = null; + + // Name = System.AppUserModel.ID + // ShellPKey = PKEY_AppUserModel_ID + // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 + // PropID = 5 + // Type = String (VT_LPWSTR) + private readonly PropertyKey AppUserModelIDKey = + new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); + + private const int MAX_PATH = 260; + private const int INFOTIPSIZE = 1024; + + private const int STGM_READ = 0x00000000; // STGM constants + private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags + + #endregion + + #region Private Properties (Interfaces) + + private IPersistFile PersistFile + { + get + { + IPersistFile PersistFile = shellLinkW as IPersistFile; + + if (PersistFile == null) + throw new COMException("Failed to create IPersistFile."); + else + return PersistFile; + } + } + + private IPropertyStore PropertyStore + { + get + { + IPropertyStore PropertyStore = shellLinkW as IPropertyStore; + + if (PropertyStore == null) + throw new COMException("Failed to create IPropertyStore."); + else + return PropertyStore; + } + } + + #endregion + + #region Public Properties (Minimal) + + // Path of loaded shortcut file + public string ShortcutFile + { + get + { + string shortcutFile; + + PersistFile.GetCurFile(out shortcutFile); + + return shortcutFile; + } + } + + // Path of target file + public string TargetPath + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder targetPath = new StringBuilder(MAX_PATH); + + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + + VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + SLGP_UNCPRIORITY)); + + return targetPath.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetPath(value)); + } + } + + public string Arguments + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + + return arguments.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetArguments(value)); + } + } + + public string Description + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder description = new StringBuilder(INFOTIPSIZE); + + VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + + return description.ToString(); + } + set + { + VerifySucceeded(shellLinkW.SetDescription(value)); + } + } + + public Tuple IconLocation + { + get + { + // No limitation to length of buffer string in the case of Unicode though. + StringBuilder icon = new StringBuilder(INFOTIPSIZE); + + int index; + VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + + return new Tuple(icon.ToString(), index); + } + set + { + VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + } + } + + // AppUserModelID to be used for Windows 7 or later. + public string AppUserModelID + { + get + { + using (PropVariant pv = new PropVariant()) + { + VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + + if (pv.Value == null) + return "Null"; + else + return pv.Value; + } + } + set + { + using (PropVariant pv = new PropVariant(value)) + { + VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); + VerifySucceeded(PropertyStore.Commit()); + } + } + } + + #endregion + + #region Constructor + + public ShellLink() + : this(null) + { } + + // Construct with loading shortcut file. + public ShellLink(string file) + { + try + { + shellLinkW = (IShellLinkW)new CShellLink(); + } + catch + { + throw new COMException("Failed to create ShellLink object."); + } + + if (file != null) + Load(file); + } + + #endregion + + #region Destructor + + ~ShellLink() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (shellLinkW != null) + { + // Release all references. + Marshal.FinalReleaseComObject(shellLinkW); + shellLinkW = null; + } + } + + #endregion + + #region Methods + + // Save shortcut file. + public void Save() + { + string file = ShortcutFile; + + if (file == null) + throw new InvalidOperationException("File name is not given."); + else + Save(file); + } + + public void Save(string file) + { + if (file == null) + throw new ArgumentNullException("File name is required."); + else + PersistFile.Save(file, true); + } + + // Load shortcut file. + public void Load(string file) + { + if (!File.Exists(file)) + throw new FileNotFoundException("File is not found.", file); + else + PersistFile.Load(file, STGM_READ); + } + + // Verify if operation succeeded. + public static void VerifySucceeded(uint hresult) + { + if (hresult > 1) + throw new InvalidOperationException("Failed with HRESULT: " + + hresult.ToString("X")); + } + + #endregion + } +}