diff --git a/LICENSE b/LICENSE.md
similarity index 95%
rename from LICENSE
rename to LICENSE.md
index df27d6e..b651374 100644
--- a/LICENSE
+++ b/LICENSE.md
@@ -1,6 +1,7 @@
-MIT License
-Copyright (c) 2019 Axel Kesseler
+# MIT License
+
+Copyright (c) 2019 plexdata.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 0df2f92..58afecb 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,9 @@
+
# EnvironmentManager
-Managing tool for Windows environment variables.
+
+Mainly, this program is designed to switch Windows environment variables using the tray icon
+menu. But the program also allows to manage (creating, modifying and deleting) all environment
+variables.
+
+Another feature of this program is the possibility to relaunch the program with administrator
+privileges as well as to install or remove auto-launch behavior on user logon.
diff --git a/code/HISTORY.md b/code/HISTORY.md
new file mode 100644
index 0000000..ae3ef76
--- /dev/null
+++ b/code/HISTORY.md
@@ -0,0 +1,5 @@
+
+**1.0.0.0**
+
+- Initial draft.
+- Published on [https://github.com/akesseler/EnvironmentManager](https://github.com/akesseler/EnvironmentManager).
diff --git a/code/README.md b/code/README.md
new file mode 100644
index 0000000..62d727e
--- /dev/null
+++ b/code/README.md
@@ -0,0 +1,6 @@
+
+## Project Build
+
+Best way to build the whole project is to use _Visual Studio 2017 Community_. Thereafter,
+download the complete sources, open the solution file ``EnvironmentManager.sln``, switch
+to release and rebuild all.
diff --git a/code/clean.cmd b/code/clean.cmd
new file mode 100644
index 0000000..603e08e
--- /dev/null
+++ b/code/clean.cmd
@@ -0,0 +1,41 @@
+@echo off
+
+goto CHOICE_BINARIES
+
+:CHOICE_BINARIES
+
+choice /M "Do you really want to remove all \"bin\" and \"obj\" folders"
+
+if %ERRORLEVEL% == 1 (
+ goto CLEAN_BINARIES
+) else (
+ goto CHOICE_PACKAGES
+)
+
+:CLEAN_BINARIES
+
+echo Clean up all "bin" and "obj" folders...
+
+for /d /r %%x in (bin, obj) do rmdir "%%x" /s /q 2> nul
+
+:CHOICE_PACKAGES
+
+choice /M "Do you really want to remove all folders in \"packages\""
+
+if %ERRORLEVEL% == 1 (
+ goto CLEAN_PACKAGES
+) else (
+ goto CHOICE_FINISHED
+)
+
+:CLEAN_PACKAGES
+
+echo Clean up all "packages"...
+
+for /d /r %%x in (packages) do rmdir "%%x" /s /q 2> nul
+
+:CHOICE_FINISHED
+
+echo Done!
+
+pause
diff --git a/code/src/EnvironmentManager.sln b/code/src/EnvironmentManager.sln
new file mode 100644
index 0000000..1e93da2
--- /dev/null
+++ b/code/src/EnvironmentManager.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.645
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvironmentManager", "EnvironmentManager\EnvironmentManager.csproj", "{C1467758-5E28-4BA1-B416-D8E4685A5609}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C1467758-5E28-4BA1-B416-D8E4685A5609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1467758-5E28-4BA1-B416-D8E4685A5609}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1467758-5E28-4BA1-B416-D8E4685A5609}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1467758-5E28-4BA1-B416-D8E4685A5609}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E763ED87-E880-43D3-8A13-070E62875753}
+ EndGlobalSection
+EndGlobal
diff --git a/code/src/EnvironmentManager/Controls/SplitContainerEx.cs b/code/src/EnvironmentManager/Controls/SplitContainerEx.cs
new file mode 100644
index 0000000..5537ed4
--- /dev/null
+++ b/code/src/EnvironmentManager/Controls/SplitContainerEx.cs
@@ -0,0 +1,123 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Drawing;
+using System.Diagnostics;
+using System.Windows.Forms;
+using System.Windows.Forms.VisualStyles;
+
+namespace Plexdata.EnvironmentManager.Controls
+{
+ // Find a quite nice example of a splitter based on a TableLayoutPanel...
+ // http://stackoverflow.com/questions/5033690/add-button-controls-to-splitcontainer-splitter/5046984#5046984
+
+ public class SplitContainerEx : SplitContainer
+ {
+ public SplitContainerEx()
+ : base()
+ {
+ this.SetStyle(ControlStyles.DoubleBuffer, true);
+ this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+ this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+ this.SetStyle(ControlStyles.UserPaint, true);
+ this.SetStyle(ControlStyles.ResizeRedraw, true);
+
+ base.TabStop = false;
+ }
+
+ protected override void OnKeyUp(KeyEventArgs args)
+ {
+ base.OnKeyUp(args);
+ // Needed to disable painted focus rectangle...
+ this.Refresh();
+ }
+
+ protected override void OnPaint(PaintEventArgs args)
+ {
+ try
+ {
+ Size size = new Size(4, 40); // Gripper dimension...
+ Rectangle rect = this.SplitterRectangle;
+ VisualStyleRenderer renderer = null;
+
+ if (this.Orientation == Orientation.Vertical)
+ {
+ rect.Y = (rect.Bottom - (rect.Top + size.Height)) / 2;
+ rect.Height = size.Height;
+ rect.Width = size.Width;
+ rect.X += Math.Max((this.SplitterWidth - rect.Width) / 2, 0) - 1;
+
+ if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.Rebar.Gripper.Normal))
+ {
+ renderer = new VisualStyleRenderer(VisualStyleElement.Rebar.Gripper.Normal);
+ }
+ }
+ else
+ {
+ rect.X = (rect.Right - (rect.Left + size.Height)) / 2;
+ rect.Height = size.Width;
+ rect.Width = size.Height;
+ rect.Y += Math.Max((this.SplitterWidth - rect.Height) / 2, 0) - 1;
+
+ if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.Rebar.GripperVertical.Normal))
+ {
+ renderer = new VisualStyleRenderer(VisualStyleElement.Rebar.GripperVertical.Normal);
+ }
+ }
+
+ if (renderer != null)
+ {
+ renderer.DrawBackground(args.Graphics, rect, args.ClipRectangle);
+ }
+
+ if (base.Focused && base.TabStop)
+ {
+ ControlPaint.DrawFocusRectangle(args.Graphics,
+ Rectangle.Inflate(this.SplitterRectangle, -1, -1),
+ this.ForeColor, this.BackColor);
+ }
+ }
+ catch (Exception exception)
+ {
+ Debug.WriteLine(exception);
+ }
+ }
+
+ protected override void OnDoubleClick(EventArgs args)
+ {
+ base.OnDoubleClick(args);
+ if (this.Orientation == Orientation.Vertical)
+ {
+ this.SplitterDistance = this.ClientSize.Width / 2;
+ }
+ else
+ {
+ this.SplitterDistance = this.ClientSize.Height / 2;
+ }
+ this.Refresh();
+ }
+ }
+}
+
diff --git a/code/src/EnvironmentManager/Dialogs/SettingsDialog.Designer.cs b/code/src/EnvironmentManager/Dialogs/SettingsDialog.Designer.cs
new file mode 100644
index 0000000..dae25c8
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/SettingsDialog.Designer.cs
@@ -0,0 +1,249 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+namespace Plexdata.EnvironmentManager.Dialogs
+{
+ partial class SettingsDialog
+ {
+ ///
+ /// 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.btnCancel = new System.Windows.Forms.Button();
+ this.btnAccept = new System.Windows.Forms.Button();
+ this.btnRemove = new System.Windows.Forms.Button();
+ this.btnInstall = new System.Windows.Forms.Button();
+ this.lblRemove = new System.Windows.Forms.Label();
+ this.lblInstall = new System.Windows.Forms.Label();
+ this.tblRegistration = new System.Windows.Forms.TableLayoutPanel();
+ this.tabContent = new System.Windows.Forms.TabControl();
+ this.tabRegistration = new System.Windows.Forms.TabPage();
+ this.tabArguments = new System.Windows.Forms.TabPage();
+ this.txtArguments = new System.Windows.Forms.TextBox();
+ this.tblRegistration.SuspendLayout();
+ this.tabContent.SuspendLayout();
+ this.tabRegistration.SuspendLayout();
+ this.tabArguments.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // btnCancel
+ //
+ this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(397, 277);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(75, 23);
+ this.btnCancel.TabIndex = 2;
+ this.btnCancel.Text = "&Cancel";
+ this.btnCancel.UseVisualStyleBackColor = true;
+ //
+ // btnAccept
+ //
+ this.btnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnAccept.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.btnAccept.Location = new System.Drawing.Point(316, 277);
+ this.btnAccept.Name = "btnAccept";
+ this.btnAccept.Size = new System.Drawing.Size(75, 23);
+ this.btnAccept.TabIndex = 1;
+ this.btnAccept.Text = "&OK";
+ this.btnAccept.UseVisualStyleBackColor = true;
+ //
+ // btnRemove
+ //
+ this.btnRemove.Location = new System.Drawing.Point(344, 104);
+ this.btnRemove.Name = "btnRemove";
+ this.btnRemove.Size = new System.Drawing.Size(75, 23);
+ this.btnRemove.TabIndex = 3;
+ this.btnRemove.Text = "&Remove";
+ this.btnRemove.UseVisualStyleBackColor = true;
+ this.btnRemove.Click += new System.EventHandler(this.OnRemoveClicked);
+ //
+ // btnInstall
+ //
+ this.btnInstall.Location = new System.Drawing.Point(344, 3);
+ this.btnInstall.Name = "btnInstall";
+ this.btnInstall.Size = new System.Drawing.Size(75, 23);
+ this.btnInstall.TabIndex = 1;
+ this.btnInstall.Text = "&Install";
+ this.btnInstall.UseVisualStyleBackColor = true;
+ this.btnInstall.Click += new System.EventHandler(this.OnInstallClicked);
+ //
+ // lblRemove
+ //
+ this.lblRemove.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lblRemove.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblRemove.Location = new System.Drawing.Point(3, 101);
+ this.lblRemove.Name = "lblRemove";
+ this.lblRemove.Size = new System.Drawing.Size(335, 102);
+ this.lblRemove.TabIndex = 2;
+ this.lblRemove.Text = "Click button [Remove] to delete program registration for auto-launch on user logi" +
+ "n.";
+ //
+ // lblInstall
+ //
+ this.lblInstall.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lblInstall.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblInstall.Location = new System.Drawing.Point(3, 0);
+ this.lblInstall.Name = "lblInstall";
+ this.lblInstall.Size = new System.Drawing.Size(335, 101);
+ this.lblInstall.TabIndex = 0;
+ this.lblInstall.Text = "Click button [Install] to register program for auto-launch on user login.";
+ //
+ // tblRegistration
+ //
+ this.tblRegistration.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tblRegistration.ColumnCount = 2;
+ this.tblRegistration.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tblRegistration.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tblRegistration.Controls.Add(this.btnRemove, 1, 1);
+ this.tblRegistration.Controls.Add(this.lblInstall, 0, 0);
+ this.tblRegistration.Controls.Add(this.btnInstall, 1, 0);
+ this.tblRegistration.Controls.Add(this.lblRemove, 0, 1);
+ this.tblRegistration.Location = new System.Drawing.Point(15, 15);
+ this.tblRegistration.Margin = new System.Windows.Forms.Padding(10);
+ this.tblRegistration.Name = "tblRegistration";
+ this.tblRegistration.RowCount = 2;
+ this.tblRegistration.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tblRegistration.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tblRegistration.Size = new System.Drawing.Size(422, 203);
+ this.tblRegistration.TabIndex = 0;
+ //
+ // tabContent
+ //
+ this.tabContent.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tabContent.Controls.Add(this.tabRegistration);
+ this.tabContent.Controls.Add(this.tabArguments);
+ this.tabContent.Location = new System.Drawing.Point(12, 12);
+ this.tabContent.Name = "tabContent";
+ this.tabContent.SelectedIndex = 0;
+ this.tabContent.Size = new System.Drawing.Size(460, 259);
+ this.tabContent.TabIndex = 0;
+ //
+ // tabRegistration
+ //
+ this.tabRegistration.Controls.Add(this.tblRegistration);
+ this.tabRegistration.Location = new System.Drawing.Point(4, 22);
+ this.tabRegistration.Name = "tabRegistration";
+ this.tabRegistration.Padding = new System.Windows.Forms.Padding(5);
+ this.tabRegistration.Size = new System.Drawing.Size(452, 233);
+ this.tabRegistration.TabIndex = 0;
+ this.tabRegistration.Text = "Registration";
+ this.tabRegistration.UseVisualStyleBackColor = true;
+ //
+ // tabArguments
+ //
+ this.tabArguments.Controls.Add(this.txtArguments);
+ this.tabArguments.Location = new System.Drawing.Point(4, 22);
+ this.tabArguments.Name = "tabArguments";
+ this.tabArguments.Padding = new System.Windows.Forms.Padding(5);
+ this.tabArguments.Size = new System.Drawing.Size(452, 233);
+ this.tabArguments.TabIndex = 1;
+ this.tabArguments.Text = "Arguments";
+ this.tabArguments.UseVisualStyleBackColor = true;
+ //
+ // txtArguments
+ //
+ this.txtArguments.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtArguments.BackColor = System.Drawing.SystemColors.Window;
+ this.txtArguments.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtArguments.Location = new System.Drawing.Point(15, 15);
+ this.txtArguments.Margin = new System.Windows.Forms.Padding(10);
+ this.txtArguments.Multiline = true;
+ this.txtArguments.Name = "txtArguments";
+ this.txtArguments.ReadOnly = true;
+ this.txtArguments.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.txtArguments.Size = new System.Drawing.Size(422, 203);
+ this.txtArguments.TabIndex = 0;
+ this.txtArguments.WordWrap = false;
+ //
+ // SettingsDialog
+ //
+ this.AcceptButton = this.btnAccept;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(484, 312);
+ this.Controls.Add(this.tabContent);
+ this.Controls.Add(this.btnAccept);
+ this.Controls.Add(this.btnCancel);
+ this.DoubleBuffered = true;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.MinimumSize = new System.Drawing.Size(390, 300);
+ this.Name = "SettingsDialog";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Settings";
+ this.tblRegistration.ResumeLayout(false);
+ this.tabContent.ResumeLayout(false);
+ this.tabRegistration.ResumeLayout(false);
+ this.tabArguments.ResumeLayout(false);
+ this.tabArguments.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button btnAccept;
+ private System.Windows.Forms.Button btnInstall;
+ private System.Windows.Forms.Label lblInstall;
+ private System.Windows.Forms.Button btnRemove;
+ private System.Windows.Forms.Label lblRemove;
+ private System.Windows.Forms.TableLayoutPanel tblRegistration;
+ private System.Windows.Forms.TabControl tabContent;
+ private System.Windows.Forms.TabPage tabRegistration;
+ private System.Windows.Forms.TabPage tabArguments;
+ private System.Windows.Forms.TextBox txtArguments;
+ }
+}
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Dialogs/SettingsDialog.cs b/code/src/EnvironmentManager/Dialogs/SettingsDialog.cs
new file mode 100644
index 0000000..f1cc789
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/SettingsDialog.cs
@@ -0,0 +1,216 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.ArgumentParser.Extensions;
+using Plexdata.EnvironmentManager.Extensions;
+using Plexdata.EnvironmentManager.Internals;
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Dialogs
+{
+ public partial class SettingsDialog : Form
+ {
+ public SettingsDialog()
+ {
+ this.InitializeComponent();
+ this.Icon = Properties.Resources.MainIcon;
+
+ PermissionCheck.SetButtonShield(this.btnRemove, !PermissionCheck.IsRunAsAdmin);
+ PermissionCheck.SetButtonShield(this.btnInstall, !PermissionCheck.IsRunAsAdmin);
+
+ this.SetDoubleBuffered(this.tabContent);
+ this.SetDoubleBuffered(this.tabRegistration);
+ this.SetDoubleBuffered(this.tabArguments);
+ this.SetDoubleBuffered(this.txtArguments); // Actually, it does not really has an effect. But why?
+ }
+
+ protected override void OnLoad(EventArgs args)
+ {
+ base.OnLoad(args);
+ this.txtArguments.Text = Program.Arguments.Generate();
+ }
+
+ protected override void OnShown(EventArgs args)
+ {
+ using (new WaitCursor(this))
+ {
+ this.LoadSettings();
+ base.OnShown(args);
+ this.UpdateButtons();
+ }
+ }
+
+ protected override void OnClosing(CancelEventArgs args)
+ {
+ base.OnClosing(args);
+ this.SaveSettings();
+ }
+
+ private void OnInstallClicked(Object sender, EventArgs args)
+ {
+ using (new WaitCursor(this))
+ {
+ this.Enabled = false;
+
+ try
+ {
+ this.ExecuteCommand("--install", !PermissionCheck.IsRunAsAdmin);
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Critical("Unexpected error while executing task create command.", exception);
+ }
+ finally
+ {
+ this.Enabled = true;
+ }
+
+ this.UpdateButtons();
+ }
+ }
+
+ private void OnRemoveClicked(Object sender, EventArgs args)
+ {
+ using (new WaitCursor(this))
+ {
+ this.Enabled = false;
+
+ try
+ {
+ this.ExecuteCommand("--remove", !PermissionCheck.IsRunAsAdmin);
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Critical("Unexpected error while executing task remove command.", exception);
+ }
+ finally
+ {
+ this.Enabled = true;
+ }
+
+ this.UpdateButtons();
+ }
+ }
+
+ private void UpdateButtons()
+ {
+ try
+ {
+ Boolean installed = TaskScheduler.IsInstalled();
+
+ this.btnRemove.Enabled = installed;
+ this.btnInstall.Enabled = !installed;
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Critical("Unexpected error while updating buttons.", exception);
+
+ this.btnRemove.Enabled = false;
+ this.btnInstall.Enabled = false;
+ }
+
+ this.Update();
+ }
+
+ private Boolean ExecuteCommand(String arguments, Boolean elevated)
+ {
+ try
+ {
+ ProcessStartInfo info = new ProcessStartInfo
+ {
+ Verb = elevated ? "runas" : String.Empty,
+ Arguments = arguments ?? String.Empty,
+ FileName = Assembly.GetExecutingAssembly().Location,
+ WindowStyle = ProcessWindowStyle.Hidden
+ };
+
+ using (Process process = Process.Start(info))
+ {
+ process.WaitForExit();
+ return process.ExitCode == 0;
+ }
+ }
+ catch (Win32Exception exception)
+ {
+ const Int32 ERROR_CANCELLED = 1223;
+
+ if (exception.NativeErrorCode == ERROR_CANCELLED)
+ {
+ Program.Logger.Warning("User rejected command execution in settings dialog.", new (String, Object)[] { ("Arguments", arguments), ("Elevated", elevated) });
+ return false;
+ }
+ else
+ {
+ Program.Logger.Critical("Unexpected error while command execution in settings dialog.", exception);
+ throw exception;
+ }
+ }
+ }
+
+ private void SetDoubleBuffered(Control control)
+ {
+ typeof(Control).InvokeMember("DoubleBuffered",
+ BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
+ null, control, new Object[] { true });
+ }
+
+ private void LoadSettings()
+ {
+ try
+ {
+ this.DesktopBounds = Program.ReadSettingsValue(this.GetType().Name, nameof(this.DesktopBounds))
+ .StringToBounds(this.StandardBounds(new Size(500, 350)));
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Loading settings for settings dialog from configuration has failed.", exception);
+ }
+
+ this.EnsureScreenLocation();
+ }
+
+ private void SaveSettings()
+ {
+ try
+ {
+ Program.SaveSettingsValue(this.GetType().Name, nameof(this.DesktopBounds), this.DesktopBounds.BoundsToString());
+ Program.SaveSettings();
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Saving settings for settings dialog into configuration has failed.", exception);
+ }
+ }
+ }
+}
+
+
+
+
diff --git a/code/src/EnvironmentManager/Dialogs/SettingsDialog.resx b/code/src/EnvironmentManager/Dialogs/SettingsDialog.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/SettingsDialog.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Dialogs/VariableDialog.Designer.cs b/code/src/EnvironmentManager/Dialogs/VariableDialog.Designer.cs
new file mode 100644
index 0000000..5558b8d
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/VariableDialog.Designer.cs
@@ -0,0 +1,256 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+namespace Plexdata.EnvironmentManager.Dialogs
+{
+ partial class VariableDialog
+ {
+ ///
+ /// 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.btnCancel = new System.Windows.Forms.Button();
+ this.btnAccept = new System.Windows.Forms.Button();
+ this.valScope = new System.Windows.Forms.ComboBox();
+ this.lblScope = new System.Windows.Forms.Label();
+ this.valLabel = new System.Windows.Forms.TextBox();
+ this.valValue = new System.Windows.Forms.TextBox();
+ this.lblLabel = new System.Windows.Forms.Label();
+ this.lblValue = new System.Windows.Forms.Label();
+ this.labelError = new System.Windows.Forms.ErrorProvider(this.components);
+ this.valueError = new System.Windows.Forms.ErrorProvider(this.components);
+ this.valShift = new System.Windows.Forms.TextBox();
+ this.lblShift = new System.Windows.Forms.Label();
+ this.tabLayout = new System.Windows.Forms.TableLayoutPanel();
+ ((System.ComponentModel.ISupportInitialize)(this.labelError)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.valueError)).BeginInit();
+ this.tabLayout.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // btnCancel
+ //
+ this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(297, 327);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(75, 23);
+ this.btnCancel.TabIndex = 2;
+ this.btnCancel.Text = "&Cancel";
+ this.btnCancel.UseVisualStyleBackColor = true;
+ //
+ // btnAccept
+ //
+ this.btnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnAccept.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.btnAccept.Location = new System.Drawing.Point(216, 327);
+ this.btnAccept.Name = "btnAccept";
+ this.btnAccept.Size = new System.Drawing.Size(75, 23);
+ this.btnAccept.TabIndex = 1;
+ this.btnAccept.Text = "&OK";
+ this.btnAccept.UseVisualStyleBackColor = true;
+ //
+ // valScope
+ //
+ this.valScope.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.valScope.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.valScope.FormattingEnabled = true;
+ this.valScope.Location = new System.Drawing.Point(63, 3);
+ this.valScope.Name = "valScope";
+ this.valScope.Size = new System.Drawing.Size(294, 21);
+ this.valScope.TabIndex = 1;
+ //
+ // lblScope
+ //
+ this.lblScope.AutoSize = true;
+ this.lblScope.Location = new System.Drawing.Point(3, 0);
+ this.lblScope.Name = "lblScope";
+ this.lblScope.Size = new System.Drawing.Size(41, 13);
+ this.lblScope.TabIndex = 0;
+ this.lblScope.Text = "&Scope:";
+ //
+ // valLabel
+ //
+ this.valLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.valLabel.Location = new System.Drawing.Point(63, 30);
+ this.valLabel.Name = "valLabel";
+ this.valLabel.Size = new System.Drawing.Size(294, 20);
+ this.valLabel.TabIndex = 3;
+ //
+ // valValue
+ //
+ this.valValue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.valValue.Location = new System.Drawing.Point(63, 56);
+ this.valValue.Multiline = true;
+ this.valValue.Name = "valValue";
+ this.valValue.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.valValue.Size = new System.Drawing.Size(294, 122);
+ this.valValue.TabIndex = 5;
+ this.valValue.WordWrap = false;
+ //
+ // lblLabel
+ //
+ this.lblLabel.AutoSize = true;
+ this.lblLabel.Location = new System.Drawing.Point(3, 27);
+ this.lblLabel.Name = "lblLabel";
+ this.lblLabel.Size = new System.Drawing.Size(33, 13);
+ this.lblLabel.TabIndex = 2;
+ this.lblLabel.Text = "&Label";
+ //
+ // lblValue
+ //
+ this.lblValue.AutoSize = true;
+ this.lblValue.Location = new System.Drawing.Point(3, 53);
+ this.lblValue.Name = "lblValue";
+ this.lblValue.Size = new System.Drawing.Size(34, 13);
+ this.lblValue.TabIndex = 4;
+ this.lblValue.Text = "&Value";
+ //
+ // labelError
+ //
+ this.labelError.ContainerControl = this;
+ //
+ // valueError
+ //
+ this.valueError.ContainerControl = this;
+ //
+ // valShift
+ //
+ this.valShift.AcceptsReturn = true;
+ this.valShift.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.valShift.Location = new System.Drawing.Point(63, 184);
+ this.valShift.Multiline = true;
+ this.valShift.Name = "valShift";
+ this.valShift.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.valShift.Size = new System.Drawing.Size(294, 122);
+ this.valShift.TabIndex = 7;
+ this.valShift.WordWrap = false;
+ //
+ // lblShift
+ //
+ this.lblShift.AutoSize = true;
+ this.lblShift.Location = new System.Drawing.Point(3, 181);
+ this.lblShift.Name = "lblShift";
+ this.lblShift.Size = new System.Drawing.Size(28, 13);
+ this.lblShift.TabIndex = 6;
+ this.lblShift.Text = "S&hift";
+ //
+ // tabLayout
+ //
+ this.tabLayout.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tabLayout.ColumnCount = 2;
+ this.tabLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 60F));
+ this.tabLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tabLayout.Controls.Add(this.valScope, 1, 0);
+ this.tabLayout.Controls.Add(this.valValue, 1, 2);
+ this.tabLayout.Controls.Add(this.valShift, 1, 3);
+ this.tabLayout.Controls.Add(this.lblScope, 0, 0);
+ this.tabLayout.Controls.Add(this.valLabel, 1, 1);
+ this.tabLayout.Controls.Add(this.lblShift, 0, 3);
+ this.tabLayout.Controls.Add(this.lblLabel, 0, 1);
+ this.tabLayout.Controls.Add(this.lblValue, 0, 2);
+ this.tabLayout.Location = new System.Drawing.Point(12, 12);
+ this.tabLayout.Name = "tabLayout";
+ this.tabLayout.RowCount = 4;
+ this.tabLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tabLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tabLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tabLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tabLayout.Size = new System.Drawing.Size(360, 309);
+ this.tabLayout.TabIndex = 0;
+ //
+ // VariableDialog
+ //
+ this.AcceptButton = this.btnAccept;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(384, 362);
+ this.Controls.Add(this.tabLayout);
+ this.Controls.Add(this.btnAccept);
+ this.Controls.Add(this.btnCancel);
+ this.DoubleBuffered = true;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.MinimumSize = new System.Drawing.Size(400, 400);
+ this.Name = "VariableDialog";
+ this.ShowInTaskbar = false;
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Environment Variable";
+ ((System.ComponentModel.ISupportInitialize)(this.labelError)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.valueError)).EndInit();
+ this.tabLayout.ResumeLayout(false);
+ this.tabLayout.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button btnAccept;
+ private System.Windows.Forms.ComboBox valScope;
+ private System.Windows.Forms.Label lblScope;
+ private System.Windows.Forms.TextBox valLabel;
+ private System.Windows.Forms.TextBox valValue;
+ private System.Windows.Forms.Label lblLabel;
+ private System.Windows.Forms.Label lblValue;
+ private System.Windows.Forms.ErrorProvider labelError;
+ private System.Windows.Forms.ErrorProvider valueError;
+ private System.Windows.Forms.TextBox valShift;
+ private System.Windows.Forms.TableLayoutPanel tabLayout;
+ private System.Windows.Forms.Label lblShift;
+ }
+}
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Dialogs/VariableDialog.cs b/code/src/EnvironmentManager/Dialogs/VariableDialog.cs
new file mode 100644
index 0000000..e24330c
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/VariableDialog.cs
@@ -0,0 +1,190 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.EnvironmentManager.Extensions;
+using Plexdata.EnvironmentManager.Internals;
+using Plexdata.EnvironmentManager.Models;
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Dialogs
+{
+ public partial class VariableDialog : Form
+ {
+ public VariableDialog(EnvironmentVariableTarget scope)
+ : base()
+ {
+ this.InitializeComponent();
+ this.Icon = Properties.Resources.MainIcon;
+ this.Variable = new EnvironmentVariable(scope);
+ }
+
+ public VariableDialog(EnvironmentVariable variable)
+ : base()
+ {
+ this.InitializeComponent();
+ this.Icon = Properties.Resources.MainIcon;
+ if (variable == null) { throw new ArgumentNullException(nameof(variable)); }
+ this.Variable = (EnvironmentVariable)variable.Clone();
+ }
+
+ public EnvironmentVariable Variable { get; private set; }
+
+ protected override void OnLoad(EventArgs args)
+ {
+ base.OnLoad(args);
+
+ this.Text += this.Variable.IsCreated ? " (NEW)" : " (EDIT)";
+
+ this.valScope.DataSource = Enum.GetValues(this.Variable.Scope.GetType());
+ this.valScope.SelectedItem = this.Variable.Scope;
+ this.valScope.Enabled = false;
+
+ this.valLabel.Text = this.Variable.Label;
+ this.valLabel.TextChanged += this.OnLabelTextChanged;
+ this.valLabel.ReadOnly = this.Variable.IsDeleted;
+ this.valLabel.BackColor = this.valLabel.ReadOnly ? SystemColors.Info : SystemColors.Window;
+
+ this.valValue.Text = this.Variable.Value;
+ this.valValue.TextChanged += this.OnValueTextChanged;
+ this.valValue.ReadOnly = this.Variable.IsDeleted;
+ this.valValue.BackColor = this.valValue.ReadOnly ? SystemColors.Info : SystemColors.Window;
+
+ this.valShift.Lines = this.Variable.Shift;
+ this.valShift.TextChanged += this.OnShiftTextChanged;
+ this.valShift.ReadOnly = this.Variable.IsDeleted;
+ this.valShift.BackColor = this.valShift.ReadOnly ? SystemColors.Info : SystemColors.Window;
+
+ this.btnAccept.Enabled = this.CanEnableAccept();
+ this.btnAccept.Click += this.OnAcceptClicked;
+ }
+
+ protected override void OnShown(EventArgs args)
+ {
+ using (new WaitCursor(this))
+ {
+ this.LoadSettings();
+ base.OnShown(args);
+ }
+ }
+
+ protected override void OnClosing(CancelEventArgs args)
+ {
+ base.OnClosing(args);
+ this.SaveSettings();
+ }
+
+ private void OnLabelTextChanged(Object sender, EventArgs args)
+ {
+ if (!String.IsNullOrWhiteSpace(this.valLabel.Text))
+ {
+ this.labelError.SetError(this.valLabel, String.Empty);
+ }
+ else
+ {
+ this.labelError.SetIconPadding(this.valLabel, 2);
+ this.labelError.SetIconAlignment(this.valLabel, ErrorIconAlignment.MiddleLeft);
+ this.labelError.SetError(this.valLabel, "The label for an environment variable is required.!");
+ }
+
+ this.btnAccept.Enabled = this.CanEnableAccept();
+ }
+
+ private void OnValueTextChanged(Object sender, EventArgs args)
+ {
+ if (!String.IsNullOrWhiteSpace(this.valValue.Text))
+ {
+ this.valueError.SetError(this.valValue, String.Empty);
+ }
+ else
+ {
+ this.valueError.SetIconPadding(this.valValue, 2);
+ this.valueError.SetIconAlignment(this.valValue, ErrorIconAlignment.TopLeft);
+ this.valueError.SetError(this.valValue, "The value for an environment variable is required.!");
+ }
+
+ this.btnAccept.Enabled = this.CanEnableAccept();
+ }
+
+ private void OnShiftTextChanged(Object sender, EventArgs args)
+ {
+ this.btnAccept.Enabled = this.CanEnableAccept();
+ }
+
+ private void OnAcceptClicked(Object sender, EventArgs args)
+ {
+ if (String.IsNullOrWhiteSpace(this.valLabel.Text) || String.IsNullOrWhiteSpace(this.valValue.Text))
+ {
+ base.DialogResult = DialogResult.None;
+ return;
+ }
+
+ this.Variable.Scope = (EnvironmentVariableTarget)this.valScope.SelectedItem;
+ this.Variable.Label = this.valLabel.Text;
+ this.Variable.Value = this.valValue.Text;
+
+ this.Variable.Shift = this.valShift.Lines;
+ }
+
+ private Boolean CanEnableAccept()
+ {
+ return
+ !this.Variable.IsReadonly &&
+ !this.Variable.IsDeleted &&
+ !String.IsNullOrWhiteSpace(this.valLabel.Text) &&
+ !String.IsNullOrWhiteSpace(this.valValue.Text);
+ }
+
+ private void LoadSettings()
+ {
+ try
+ {
+ this.DesktopBounds = Program.ReadSettingsValue(this.GetType().Name, nameof(this.DesktopBounds))
+ .StringToBounds(this.StandardBounds(new Size(400, 400)));
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Loading settings for variable dialog from configuration has failed.", exception);
+ }
+
+ this.EnsureScreenLocation();
+ }
+
+ private void SaveSettings()
+ {
+ try
+ {
+ Program.SaveSettingsValue(this.GetType().Name, nameof(this.DesktopBounds), this.DesktopBounds.BoundsToString());
+ Program.SaveSettings();
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Saving settings for variable dialog into configuration has failed.", exception);
+ }
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Dialogs/VariableDialog.resx b/code/src/EnvironmentManager/Dialogs/VariableDialog.resx
new file mode 100644
index 0000000..8a3010a
--- /dev/null
+++ b/code/src/EnvironmentManager/Dialogs/VariableDialog.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 121, 17
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/EnvironmentManager.csproj b/code/src/EnvironmentManager/EnvironmentManager.csproj
new file mode 100644
index 0000000..b32e560
--- /dev/null
+++ b/code/src/EnvironmentManager/EnvironmentManager.csproj
@@ -0,0 +1,170 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C1467758-5E28-4BA1-B416-D8E4685A5609}
+ WinExe
+ Plexdata.EnvironmentManager
+ pdenvmgr
+ v4.7.2
+ 512
+ true
+ true
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.0
+ false
+ false
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+ Images\MainIcon.ico
+
+
+
+ ..\packages\Plexdata.ArgumentParser.NET.1.0.0.12\lib\net472\Plexdata.ArgumentParser.NET.dll
+
+
+ ..\packages\Plexdata.CfgParser.NET.1.0.0.1\lib\net472\Plexdata.CfgParser.NET.dll
+
+
+ ..\packages\Plexdata.LogWriter.Abstraction.1.0.2.4\lib\netstandard2.0\Plexdata.LogWriter.Abstraction.dll
+
+
+ ..\packages\Plexdata.LogWriter.Persistent.1.0.2.4\lib\netstandard2.0\Plexdata.LogWriter.Persistent.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Component
+
+
+ Form
+
+
+ SettingsDialog.cs
+
+
+ Form
+
+
+ VariableDialog.cs
+
+
+
+
+
+
+
+
+ Form
+
+
+ MainForm.cs
+
+
+
+
+
+
+
+ SettingsDialog.cs
+
+
+ VariableDialog.cs
+
+
+ MainForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.7.2 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Exceptions/EnvironmentException.cs b/code/src/EnvironmentManager/Exceptions/EnvironmentException.cs
new file mode 100644
index 0000000..8caa208
--- /dev/null
+++ b/code/src/EnvironmentManager/Exceptions/EnvironmentException.cs
@@ -0,0 +1,62 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.EnvironmentManager.Models;
+using System;
+
+namespace Plexdata.EnvironmentManager.Exceptions
+{
+ public class EnvironmentException : Exception
+ {
+ public EnvironmentException()
+ : base()
+ {
+ this.Variable = null;
+ }
+
+ public EnvironmentException(EnvironmentVariable variable, String message)
+ : base(message)
+ {
+ this.Variable = variable;
+ }
+
+ public EnvironmentException(EnvironmentVariable variable, Exception exception)
+ : base(exception != null ? exception.Message : String.Empty, exception)
+ {
+ this.Variable = variable;
+ }
+
+ public EnvironmentVariable Variable { get; private set; }
+
+ public override String ToString()
+ {
+ return
+ $"{base.Message}" +
+ $"{Environment.NewLine}" +
+ $"{(this.Variable != null ? this.Variable.ToString() : "")}" +
+ $"{Environment.NewLine}" +
+ $"{base.StackTrace}";
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Extensions/SettingsExtension.cs b/code/src/EnvironmentManager/Extensions/SettingsExtension.cs
new file mode 100644
index 0000000..679b5a7
--- /dev/null
+++ b/code/src/EnvironmentManager/Extensions/SettingsExtension.cs
@@ -0,0 +1,232 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Extensions
+{
+ public static class SettingsExtension
+ {
+ private static Char[] Delimiters = new Char[] { ',', ';', ':' };
+ private static Char Delimiter = SettingsExtension.Delimiters[0];
+
+ public static Rectangle StandardBounds(this Form form)
+ {
+ if (form == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ return form.StandardBounds(form.Size);
+ }
+
+ public static Rectangle StandardBounds(this Form form, Size suggestion)
+ {
+ if (form == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ Size minimumSize = form.MinimumSize;
+ Size currentSize = suggestion;
+ Rectangle workingArea = Screen.PrimaryScreen.WorkingArea;
+
+ Int32 w = SettingsExtension.ApplyLimitations(currentSize.Width, minimumSize.Width, workingArea.Width);
+ Int32 h = SettingsExtension.ApplyLimitations(currentSize.Height, minimumSize.Height, workingArea.Height);
+
+ Size s = new Size(w, h);
+ Point p = SettingsExtension.ScreenCentered(s);
+
+ return new Rectangle(p, s);
+ }
+
+ public static void EnsureScreenLocation(this Form form)
+ {
+ if (form == null)
+ {
+ return;
+ }
+
+ Rectangle bounds = Screen.PrimaryScreen.WorkingArea;
+ Point location = form.Location;
+ Size size = form.Size;
+
+ foreach (Screen screen in Screen.AllScreens)
+ {
+ if (screen.Bounds.Contains(new Rectangle(location, size)))
+ {
+ return; // Location is still within bounds.
+ }
+ }
+
+ // Adjust X location to be on the primary screen!
+
+ Int32 x = 0;
+ if (location.X < bounds.Left)
+ {
+ x = bounds.Left;
+ }
+ else if (location.X + size.Width > bounds.Left + bounds.Right)
+ {
+ x = bounds.Right - size.Width;
+ }
+ else
+ {
+ x = location.X;
+ }
+
+ // Adjust Y location to be on the primary screen!
+
+ Int32 y = 0;
+ if (location.Y < bounds.Top)
+ {
+ y = bounds.Top;
+ }
+ else if (location.Y + size.Height > bounds.Top + bounds.Bottom)
+ {
+ y = bounds.Bottom - size.Height;
+ }
+ else
+ {
+ y = location.Y;
+ }
+
+ form.Location = new Point(x, y);
+ }
+
+ public static Point ScreenCentered(this Size size)
+ {
+ Rectangle bounds = Screen.PrimaryScreen.WorkingArea;
+
+ return new Point(
+ (bounds.Width - SettingsExtension.ApplyLimitations(size.Width, 0, bounds.Width)) / 2,
+ (bounds.Height - SettingsExtension.ApplyLimitations(size.Height, 0, bounds.Height)) / 2
+ );
+ }
+
+ public static Boolean IsValid(this Rectangle value)
+ {
+ return value != null && value.Left >= 0 && value.Top >= 0 && value.Width > 0 && value.Height > 0;
+ }
+
+ public static Rectangle StringToBounds(this String value, Rectangle standard)
+ {
+ if (!standard.IsValid())
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ Rectangle result = standard;
+
+ if (String.IsNullOrWhiteSpace(value))
+ {
+ return result;
+ }
+
+ String[] array = value.Split(SettingsExtension.Delimiters, StringSplitOptions.None);
+
+ if (array.Length > 0 && Int32.TryParse(array[0].Trim(), out Int32 x))
+ {
+ result.X = x;
+ }
+ else
+ {
+ result.X = standard.X;
+ }
+
+ if (array.Length > 1 && Int32.TryParse(array[1].Trim(), out Int32 y))
+ {
+ result.Y = y;
+ }
+ else
+ {
+ result.Y = standard.Y;
+ }
+
+ if (array.Length > 2 && Int32.TryParse(array[2].Trim(), out Int32 w))
+ {
+ result.Width = w;
+ }
+ else
+ {
+ result.Width = standard.Width;
+ }
+
+ if (array.Length > 3 && Int32.TryParse(array[3].Trim(), out Int32 h))
+ {
+ result.Height = h;
+ }
+ else
+ {
+ result.Height = standard.Height;
+ }
+
+ return result;
+ }
+
+ public static String BoundsToString(this Rectangle value)
+ {
+ if (!value.IsValid())
+ {
+ return String.Empty;
+ }
+
+ return $"{value.X}{SettingsExtension.Delimiter}{value.Y}{SettingsExtension.Delimiter}{value.Width}{SettingsExtension.Delimiter}{value.Height}";
+ }
+
+ public static Int32 StringToInteger(this String value, Int32 lower, Int32 upper, Int32 standard)
+ {
+ if (Int32.TryParse(value, out Int32 result))
+ {
+ return SettingsExtension.ApplyLimitations(result, lower, upper);
+ }
+ else
+ {
+ return standard;
+ }
+ }
+
+ public static String IntegerToString(this Int32 value)
+ {
+ return value.ToString();
+ }
+
+ private static Int32 ApplyLimitations(Int32 value, Int32 lower, Int32 upper)
+ {
+ if (value < lower)
+ {
+ return lower;
+ }
+
+ if (value > upper)
+ {
+ return upper;
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Images/MainIcon.ico b/code/src/EnvironmentManager/Images/MainIcon.ico
new file mode 100644
index 0000000..9c5c7e4
Binary files /dev/null and b/code/src/EnvironmentManager/Images/MainIcon.ico differ
diff --git a/code/src/EnvironmentManager/Images/create_32x32.png b/code/src/EnvironmentManager/Images/create_32x32.png
new file mode 100644
index 0000000..6ed3ab2
Binary files /dev/null and b/code/src/EnvironmentManager/Images/create_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/delete_32x32.png b/code/src/EnvironmentManager/Images/delete_32x32.png
new file mode 100644
index 0000000..fb82b6a
Binary files /dev/null and b/code/src/EnvironmentManager/Images/delete_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/dump_32x32.png b/code/src/EnvironmentManager/Images/dump_32x32.png
new file mode 100644
index 0000000..fd1e159
Binary files /dev/null and b/code/src/EnvironmentManager/Images/dump_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/exit_32x32.png b/code/src/EnvironmentManager/Images/exit_32x32.png
new file mode 100644
index 0000000..b8117c8
Binary files /dev/null and b/code/src/EnvironmentManager/Images/exit_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/follow_32x32.png b/code/src/EnvironmentManager/Images/follow_32x32.png
new file mode 100644
index 0000000..e4ea810
Binary files /dev/null and b/code/src/EnvironmentManager/Images/follow_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/modify_32x32.png b/code/src/EnvironmentManager/Images/modify_32x32.png
new file mode 100644
index 0000000..c4d1e8c
Binary files /dev/null and b/code/src/EnvironmentManager/Images/modify_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/save_32x32.png b/code/src/EnvironmentManager/Images/save_32x32.png
new file mode 100644
index 0000000..c721a8a
Binary files /dev/null and b/code/src/EnvironmentManager/Images/save_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/settings_32x32.png b/code/src/EnvironmentManager/Images/settings_32x32.png
new file mode 100644
index 0000000..a08f4d3
Binary files /dev/null and b/code/src/EnvironmentManager/Images/settings_32x32.png differ
diff --git a/code/src/EnvironmentManager/Images/shield_32x32.png b/code/src/EnvironmentManager/Images/shield_32x32.png
new file mode 100644
index 0000000..fcd3750
Binary files /dev/null and b/code/src/EnvironmentManager/Images/shield_32x32.png differ
diff --git a/code/src/EnvironmentManager/Internals/PermissionCheck.cs b/code/src/EnvironmentManager/Internals/PermissionCheck.cs
new file mode 100644
index 0000000..b8e3d12
--- /dev/null
+++ b/code/src/EnvironmentManager/Internals/PermissionCheck.cs
@@ -0,0 +1,83 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Internals
+{
+ internal static class PermissionCheck
+ {
+ private static Boolean? isRunAsAdmin = null;
+
+ internal static Boolean IsRunAsAdmin
+ {
+ get
+ {
+ // NOTE: This state will not change over the process lifetime.
+
+ if (!PermissionCheck.isRunAsAdmin.HasValue)
+ {
+ PermissionCheck.isRunAsAdmin = PermissionCheck.IsUserAnAdmin();
+ }
+
+ return PermissionCheck.isRunAsAdmin.Value;
+ }
+ }
+
+ internal static void SetButtonShield(Button button, Boolean visible)
+ {
+ const Int32 BCM_SETSHIELD = 0x0000160C;
+
+ if (button != null)
+ {
+ // Important because otherwise shield is not shown!
+ if (button.FlatStyle != FlatStyle.System)
+ {
+ button.FlatStyle = FlatStyle.System;
+ }
+
+ HandleRef hWnd = new HandleRef(button, button.Handle);
+ IntPtr lParam = visible ? new IntPtr(1) : new IntPtr(0);
+
+ PermissionCheck.SendMessage(hWnd, BCM_SETSHIELD, IntPtr.Zero, lParam);
+ }
+ }
+
+ #region Win32 related declaration and implementation section.
+
+ // Windows 2000 Professional / Windows 2000 Server
+ // Remarks are taken from the MSDN: "This function is a wrapper for CheckTokenMembership.
+ // It is recommended to call that function directly to determine Administrator group status
+ // rather than calling IsUserAnAdmin."
+ [DllImport("shell32.dll", CallingConvention = CallingConvention.StdCall)]
+ private static extern Boolean IsUserAnAdmin();
+
+ [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true, CharSet = CharSet.Unicode)]
+ private static extern IntPtr SendMessage(HandleRef hWnd, Int32 message, IntPtr wParam, IntPtr lParam);
+
+ #endregion // Win32 related declaration and implementation section.
+ }
+}
diff --git a/code/src/EnvironmentManager/Internals/SelfElevation.cs b/code/src/EnvironmentManager/Internals/SelfElevation.cs
new file mode 100644
index 0000000..860771f
--- /dev/null
+++ b/code/src/EnvironmentManager/Internals/SelfElevation.cs
@@ -0,0 +1,87 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Internals
+{
+ internal static class SelfElevation
+ {
+ public static Boolean Elevate(String parameters)
+ {
+ return Elevate(parameters, false);
+ }
+
+ public static Boolean Elevate(String parameters, Boolean wait)
+ {
+ try
+ {
+ // Be aware, starting a process with different window styles does not really
+ // work! Therefore, starting the sibling process with administrator privileges
+ // uses appropriated command line arguments instead.
+
+ ProcessStartInfo info = new ProcessStartInfo
+ {
+ Verb = "runas",
+ Arguments = parameters,
+ FileName = Application.ExecutablePath
+ };
+
+ if (wait)
+ {
+ Process process = Process.Start(info);
+ process.WaitForExit();
+
+ // By definition the self elevated program
+ // returns zero if execution was successful!
+ return process.ExitCode == 0;
+ }
+ else
+ {
+ Process.Start(info);
+ return true;
+ }
+ }
+ catch (Win32Exception exception)
+ {
+ const Int32 ERROR_CANCELLED = 1223;
+
+ if (exception.NativeErrorCode == ERROR_CANCELLED)
+ {
+ Program.Logger.Warning("User rejected self-elevation.", new (String, Object)[] { ("Parameters", parameters), ("Wait", wait) });
+ return false;
+ }
+ else
+ {
+ Program.Logger.Critical("Unexpected error while self-elevation.", exception);
+ throw exception;
+ }
+ }
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Internals/TaskScheduler.cs b/code/src/EnvironmentManager/Internals/TaskScheduler.cs
new file mode 100644
index 0000000..70d652f
--- /dev/null
+++ b/code/src/EnvironmentManager/Internals/TaskScheduler.cs
@@ -0,0 +1,174 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Internals
+{
+ internal static class TaskScheduler
+ {
+ public static Boolean IsInstalled()
+ {
+ return TaskScheduler.ExecuteCommand(TaskScheduler.GetQueryTaskCommandArguments());
+ }
+
+ public static Boolean Create()
+ {
+ return TaskScheduler.ExecuteCommand(TaskScheduler.GetCreateTaskCommandArguments(), !PermissionCheck.IsRunAsAdmin);
+ }
+
+ public static Boolean Delete()
+ {
+ // Not really nice, because of UAC window is shown three times.
+
+ Boolean result = false;
+ Boolean elevated = !PermissionCheck.IsRunAsAdmin;
+
+ // Might be unsuccessful, e.g. is not running.
+ TaskScheduler.ExecuteCommand(TaskScheduler.GetFinishTaskCommandArguments(), elevated);
+
+ result = TaskScheduler.ExecuteCommand(TaskScheduler.GetRemoveTaskCommandArguments(), elevated);
+
+ // Might be unsuccessful, e.g. is not running.
+ TaskScheduler.ExecuteCommand(TaskScheduler.GetRemoveRootCommandArguments(), elevated);
+
+ return result;
+ }
+
+ private static Boolean ExecuteCommand(String arguments)
+ {
+ return TaskScheduler.ExecuteCommand(arguments, false);
+ }
+
+ private static Boolean ExecuteCommand(String arguments, Boolean elevated)
+ {
+ try
+ {
+ ProcessStartInfo info = new ProcessStartInfo
+ {
+ Verb = elevated ? "runas" : String.Empty,
+ Arguments = arguments ?? String.Empty,
+ FileName = TaskScheduler.GetTaskSchedulerExecutable(),
+ WindowStyle = ProcessWindowStyle.Hidden
+ };
+
+ Debug.WriteLine(($"{info.Verb} {info.FileName} {info.Arguments}").Trim());
+
+ using (Process process = Process.Start(info))
+ {
+ process.WaitForExit();
+ return process.ExitCode == 0;
+ }
+ }
+ catch (Win32Exception exception)
+ {
+ const Int32 ERROR_CANCELLED = 1223;
+
+ if (exception.NativeErrorCode == ERROR_CANCELLED)
+ {
+ Program.Logger.Warning("User rejected command execution in task scheduler.", new (String, Object)[] { ("Arguments", arguments), ("Elevated", elevated) });
+ return false;
+ }
+ else
+ {
+ Program.Logger.Critical("Unexpected error while command execution in task scheduler.", exception);
+ throw exception;
+ }
+ }
+ }
+
+ private static String GetTaskSchedulerExecutable()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"\"%SystemRoot%\\System32\\schtasks.exe\""
+ );
+ }
+
+ private static String GetTaskRootName()
+ {
+ return $"\\Plexdata";
+ }
+
+ private static String GetTaskFullName()
+ {
+ return $"{TaskScheduler.GetTaskRootName()}\\{Application.ProductName} (Tray Icon Runner)";
+ }
+
+ private static String GetTaskFullPath()
+ {
+ return $"'{Assembly.GetExecutingAssembly().Location}' --tray-icon";
+ }
+
+ public static String GetQueryTaskCommandArguments()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"/QUERY " +
+ $"/TN \"{TaskScheduler.GetTaskFullName()}\""
+ );
+ }
+
+ public static String GetCreateTaskCommandArguments()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"/CREATE " +
+ $"/TN \"{TaskScheduler.GetTaskFullName()}\" " +
+ $"/TR \"{TaskScheduler.GetTaskFullPath()}\" " +
+ $"/SC ONLOGON " +
+ $"/RL HIGHEST " +
+ $"/RU %USERDOMAIN%\\%USERNAME%"
+ );
+ }
+
+ public static String GetFinishTaskCommandArguments()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"/END " +
+ $"/TN \"{TaskScheduler.GetTaskFullName()}\""
+ );
+ }
+
+ public static String GetRemoveRootCommandArguments()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"/DELETE " +
+ $"/TN \"{TaskScheduler.GetTaskRootName()}\" " +
+ $"/F"
+ );
+ }
+
+ public static String GetRemoveTaskCommandArguments()
+ {
+ return Environment.ExpandEnvironmentVariables(
+ $"/DELETE " +
+ $"/TN \"{TaskScheduler.GetTaskFullName()}\" " +
+ $"/F"
+ );
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Internals/WaitCursor.cs b/code/src/EnvironmentManager/Internals/WaitCursor.cs
new file mode 100644
index 0000000..348a2a0
--- /dev/null
+++ b/code/src/EnvironmentManager/Internals/WaitCursor.cs
@@ -0,0 +1,83 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager.Internals
+{
+ internal class WaitCursor : IDisposable
+ {
+ private WaitCursor()
+ : base()
+ {
+ this.Control = null;
+ this.Cursor = Cursors.Default;
+ }
+
+ public WaitCursor(Control control)
+ : this(control, false)
+ {
+ }
+
+ public WaitCursor(Control control, Boolean highest)
+ : this()
+ {
+ if (highest)
+ {
+ if (control != null)
+ {
+ Control parent = control.Parent;
+
+ while (parent != null)
+ {
+ control = parent;
+ parent = control.Parent;
+ }
+ }
+ }
+
+ this.Control = control;
+
+ if (this.Control != null)
+ {
+ this.Cursor = this.Control.Cursor;
+ this.Control.Cursor = Cursors.WaitCursor;
+ }
+ }
+
+ public Control Control { get; private set; }
+
+ public Cursor Cursor { get; private set; }
+
+ public void Dispose()
+ {
+ if (this.Control != null)
+ {
+ this.Control.Cursor = Cursor;
+ }
+ }
+ }
+
+}
diff --git a/code/src/EnvironmentManager/MainForm.Designer.cs b/code/src/EnvironmentManager/MainForm.Designer.cs
new file mode 100644
index 0000000..70d159f
--- /dev/null
+++ b/code/src/EnvironmentManager/MainForm.Designer.cs
@@ -0,0 +1,449 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+namespace Plexdata.EnvironmentManager
+{
+ partial class MainForm
+ {
+ ///
+ /// 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.tbsMain = new System.Windows.Forms.ToolStrip();
+ this.tbbExit = new System.Windows.Forms.ToolStripButton();
+ this.tbsSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.tbbSave = new System.Windows.Forms.ToolStripButton();
+ this.tbsSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+ this.tbbCreate = new System.Windows.Forms.ToolStripButton();
+ this.tbbModify = new System.Windows.Forms.ToolStripButton();
+ this.tbbDelete = new System.Windows.Forms.ToolStripButton();
+ this.tbsSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+ this.tbbElevate = new System.Windows.Forms.ToolStripButton();
+ this.tbbDump = new System.Windows.Forms.ToolStripButton();
+ this.tbbHide = new System.Windows.Forms.ToolStripButton();
+ this.tbsSeparator4 = new System.Windows.Forms.ToolStripSeparator();
+ this.tbbSettings = new System.Windows.Forms.ToolStripButton();
+ this.stbMain = new System.Windows.Forms.StatusStrip();
+ this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
+ this.notifyMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+ this.cmiExit = new System.Windows.Forms.ToolStripMenuItem();
+ this.cmiShow = new System.Windows.Forms.ToolStripMenuItem();
+ this.cmiSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.cmiHide = new System.Windows.Forms.ToolStripMenuItem();
+ this.splContainer = new Plexdata.EnvironmentManager.Controls.SplitContainerEx();
+ this.grpUser = new System.Windows.Forms.GroupBox();
+ this.lsvUser = new System.Windows.Forms.ListView();
+ this.grpMachine = new System.Windows.Forms.GroupBox();
+ this.lsvMachine = new System.Windows.Forms.ListView();
+ this.tbsMain.SuspendLayout();
+ this.notifyMenu.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splContainer)).BeginInit();
+ this.splContainer.Panel1.SuspendLayout();
+ this.splContainer.Panel2.SuspendLayout();
+ this.splContainer.SuspendLayout();
+ this.grpUser.SuspendLayout();
+ this.grpMachine.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // tbsMain
+ //
+ this.tbsMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.tbbExit,
+ this.tbsSeparator1,
+ this.tbbSave,
+ this.tbsSeparator2,
+ this.tbbCreate,
+ this.tbbModify,
+ this.tbbDelete,
+ this.tbsSeparator3,
+ this.tbbElevate,
+ this.tbbDump,
+ this.tbbHide,
+ this.tbsSeparator4,
+ this.tbbSettings});
+ this.tbsMain.Location = new System.Drawing.Point(0, 0);
+ this.tbsMain.Name = "tbsMain";
+ this.tbsMain.Size = new System.Drawing.Size(660, 47);
+ this.tbsMain.TabIndex = 2;
+ this.tbsMain.Text = "toolStrip1";
+ //
+ // tbbExit
+ //
+ this.tbbExit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbExit.Image = global::Plexdata.EnvironmentManager.Properties.Resources.ExitLargeIcon;
+ this.tbbExit.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbExit.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbExit.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbExit.Name = "tbbExit";
+ this.tbbExit.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbExit.Size = new System.Drawing.Size(42, 42);
+ this.tbbExit.Text = "Exit";
+ this.tbbExit.ToolTipText = "Close main window and exit application.";
+ this.tbbExit.Click += new System.EventHandler(this.OnExitClicked);
+ //
+ // tbsSeparator1
+ //
+ this.tbsSeparator1.Name = "tbsSeparator1";
+ this.tbsSeparator1.Size = new System.Drawing.Size(6, 47);
+ //
+ // tbbSave
+ //
+ this.tbbSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbSave.Image = global::Plexdata.EnvironmentManager.Properties.Resources.SaveLargeIcon;
+ this.tbbSave.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbSave.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbSave.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbSave.Name = "tbbSave";
+ this.tbbSave.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbSave.Size = new System.Drawing.Size(42, 42);
+ this.tbbSave.Text = "Save";
+ this.tbbSave.ToolTipText = "Write all changes back to the system.";
+ this.tbbSave.Click += new System.EventHandler(this.OnSaveClicked);
+ //
+ // tbsSeparator2
+ //
+ this.tbsSeparator2.Name = "tbsSeparator2";
+ this.tbsSeparator2.Size = new System.Drawing.Size(6, 47);
+ //
+ // tbbCreate
+ //
+ this.tbbCreate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbCreate.Image = global::Plexdata.EnvironmentManager.Properties.Resources.CreateLargeIcon;
+ this.tbbCreate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbCreate.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbCreate.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbCreate.Name = "tbbCreate";
+ this.tbbCreate.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbCreate.Size = new System.Drawing.Size(42, 42);
+ this.tbbCreate.Text = "Create";
+ this.tbbCreate.ToolTipText = "Create new environment variable.";
+ this.tbbCreate.Click += new System.EventHandler(this.OnCreateClicked);
+ //
+ // tbbModify
+ //
+ this.tbbModify.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbModify.Image = global::Plexdata.EnvironmentManager.Properties.Resources.ModifyLargeIcon;
+ this.tbbModify.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbModify.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbModify.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbModify.Name = "tbbModify";
+ this.tbbModify.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbModify.Size = new System.Drawing.Size(42, 42);
+ this.tbbModify.Text = "Modify";
+ this.tbbModify.ToolTipText = "Modify selected environment variable.";
+ this.tbbModify.Click += new System.EventHandler(this.OnModifyClicked);
+ //
+ // tbbDelete
+ //
+ this.tbbDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbDelete.Image = global::Plexdata.EnvironmentManager.Properties.Resources.DeleteLargeIcon;
+ this.tbbDelete.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbDelete.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbDelete.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbDelete.Name = "tbbDelete";
+ this.tbbDelete.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbDelete.Size = new System.Drawing.Size(42, 42);
+ this.tbbDelete.Text = "Delete";
+ this.tbbDelete.ToolTipText = "Indicate selected environment variable as deleted.";
+ this.tbbDelete.Click += new System.EventHandler(this.OnDeleteClicked);
+ //
+ // tbsSeparator3
+ //
+ this.tbsSeparator3.Name = "tbsSeparator3";
+ this.tbsSeparator3.Size = new System.Drawing.Size(6, 47);
+ //
+ // tbbElevate
+ //
+ this.tbbElevate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbElevate.Image = global::Plexdata.EnvironmentManager.Properties.Resources.ShieldLargeIcon;
+ this.tbbElevate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbElevate.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbElevate.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbElevate.Name = "tbbElevate";
+ this.tbbElevate.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbElevate.Size = new System.Drawing.Size(42, 42);
+ this.tbbElevate.Text = "Elevate";
+ this.tbbElevate.ToolTipText = "Restart application in administrator mode.";
+ this.tbbElevate.Click += new System.EventHandler(this.OnElevateClicked);
+ //
+ // tbbDump
+ //
+ this.tbbDump.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbDump.Image = global::Plexdata.EnvironmentManager.Properties.Resources.DumpLargeIcon;
+ this.tbbDump.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbDump.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbDump.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbDump.Name = "tbbDump";
+ this.tbbDump.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbDump.Size = new System.Drawing.Size(42, 42);
+ this.tbbDump.Text = "Dump";
+ this.tbbDump.ToolTipText = "Dump all environment variables.";
+ this.tbbDump.Click += new System.EventHandler(this.OnDumpClicked);
+ //
+ // tbbHide
+ //
+ this.tbbHide.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbHide.Image = global::Plexdata.EnvironmentManager.Properties.Resources.FollowLargeIcon;
+ this.tbbHide.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbHide.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbHide.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbHide.Name = "tbbHide";
+ this.tbbHide.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbHide.Size = new System.Drawing.Size(42, 42);
+ this.tbbHide.Text = "Hide";
+ this.tbbHide.ToolTipText = "Hide user interface and run in tray icon mode.";
+ this.tbbHide.Click += new System.EventHandler(this.OnHideClicked);
+ //
+ // tbsSeparator4
+ //
+ this.tbsSeparator4.Name = "tbsSeparator4";
+ this.tbsSeparator4.Size = new System.Drawing.Size(6, 47);
+ //
+ // tbbSettings
+ //
+ this.tbbSettings.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.tbbSettings.Image = global::Plexdata.EnvironmentManager.Properties.Resources.SettingsLargeIcon;
+ this.tbbSettings.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
+ this.tbbSettings.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.tbbSettings.Margin = new System.Windows.Forms.Padding(2, 2, 2, 3);
+ this.tbbSettings.Name = "tbbSettings";
+ this.tbbSettings.Padding = new System.Windows.Forms.Padding(3);
+ this.tbbSettings.Size = new System.Drawing.Size(42, 42);
+ this.tbbSettings.Text = "Settings";
+ this.tbbSettings.ToolTipText = "Modify application settings.";
+ this.tbbSettings.Click += new System.EventHandler(this.OnSettingsClicked);
+ //
+ // stbMain
+ //
+ this.stbMain.Location = new System.Drawing.Point(0, 532);
+ this.stbMain.Name = "stbMain";
+ this.stbMain.Size = new System.Drawing.Size(660, 22);
+ this.stbMain.TabIndex = 3;
+ this.stbMain.Text = "statusStrip1";
+ //
+ // notifyIcon
+ //
+ this.notifyIcon.ContextMenuStrip = this.notifyMenu;
+ this.notifyIcon.Text = "Environment Manager";
+ this.notifyIcon.Visible = true;
+ this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.OnNotifyDoubleClicked);
+ //
+ // notifyMenu
+ //
+ this.notifyMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.cmiExit,
+ this.cmiShow,
+ this.cmiSeparator1,
+ this.cmiHide});
+ this.notifyMenu.Name = "notifyMenu";
+ this.notifyMenu.Size = new System.Drawing.Size(104, 76);
+ this.notifyMenu.Opening += new System.ComponentModel.CancelEventHandler(this.OnNotifyMenuOpening);
+ //
+ // cmiExit
+ //
+ this.cmiExit.Name = "cmiExit";
+ this.cmiExit.Size = new System.Drawing.Size(103, 22);
+ this.cmiExit.Text = "&Exit";
+ this.cmiExit.Click += new System.EventHandler(this.OnNotifyExitClicked);
+ //
+ // cmiShow
+ //
+ this.cmiShow.Name = "cmiShow";
+ this.cmiShow.Size = new System.Drawing.Size(103, 22);
+ this.cmiShow.Text = "&Show";
+ this.cmiShow.Click += new System.EventHandler(this.OnNotifyShowClicked);
+ //
+ // cmiSeparator1
+ //
+ this.cmiSeparator1.Name = "cmiSeparator1";
+ this.cmiSeparator1.Size = new System.Drawing.Size(100, 6);
+ //
+ // cmiHide
+ //
+ this.cmiHide.Name = "cmiHide";
+ this.cmiHide.Size = new System.Drawing.Size(103, 22);
+ this.cmiHide.Text = "&Hide";
+ this.cmiHide.Click += new System.EventHandler(this.OnNotifyHideClicked);
+ //
+ // splContainer
+ //
+ this.splContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splContainer.Location = new System.Drawing.Point(0, 47);
+ this.splContainer.Name = "splContainer";
+ this.splContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
+ //
+ // splContainer.Panel1
+ //
+ this.splContainer.Panel1.Controls.Add(this.grpUser);
+ this.splContainer.Panel1.Padding = new System.Windows.Forms.Padding(0, 10, 0, 5);
+ this.splContainer.Panel1MinSize = 150;
+ //
+ // splContainer.Panel2
+ //
+ this.splContainer.Panel2.Controls.Add(this.grpMachine);
+ this.splContainer.Panel2MinSize = 150;
+ this.splContainer.Size = new System.Drawing.Size(660, 485);
+ this.splContainer.SplitterDistance = 178;
+ this.splContainer.SplitterWidth = 10;
+ this.splContainer.TabIndex = 0;
+ this.splContainer.TabStop = false;
+ //
+ // grpUser
+ //
+ this.grpUser.Controls.Add(this.lsvUser);
+ this.grpUser.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.grpUser.Location = new System.Drawing.Point(0, 10);
+ this.grpUser.Name = "grpUser";
+ this.grpUser.Padding = new System.Windows.Forms.Padding(10);
+ this.grpUser.Size = new System.Drawing.Size(660, 163);
+ this.grpUser.TabIndex = 0;
+ this.grpUser.TabStop = false;
+ this.grpUser.Text = "User Scope";
+ //
+ // lsvUser
+ //
+ this.lsvUser.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lsvUser.FullRowSelect = true;
+ this.lsvUser.LabelWrap = false;
+ this.lsvUser.Location = new System.Drawing.Point(10, 23);
+ this.lsvUser.MultiSelect = false;
+ this.lsvUser.Name = "lsvUser";
+ this.lsvUser.ShowGroups = false;
+ this.lsvUser.ShowItemToolTips = true;
+ this.lsvUser.Size = new System.Drawing.Size(640, 130);
+ this.lsvUser.Sorting = System.Windows.Forms.SortOrder.Ascending;
+ this.lsvUser.TabIndex = 0;
+ this.lsvUser.UseCompatibleStateImageBehavior = false;
+ this.lsvUser.View = System.Windows.Forms.View.Details;
+ this.lsvUser.Click += new System.EventHandler(this.OnListViewEnter);
+ this.lsvUser.Enter += new System.EventHandler(this.OnListViewEnter);
+ //
+ // grpMachine
+ //
+ this.grpMachine.Controls.Add(this.lsvMachine);
+ this.grpMachine.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.grpMachine.Location = new System.Drawing.Point(0, 0);
+ this.grpMachine.Name = "grpMachine";
+ this.grpMachine.Padding = new System.Windows.Forms.Padding(10);
+ this.grpMachine.Size = new System.Drawing.Size(660, 297);
+ this.grpMachine.TabIndex = 0;
+ this.grpMachine.TabStop = false;
+ this.grpMachine.Text = "Machine Scope";
+ //
+ // lsvMachine
+ //
+ this.lsvMachine.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lsvMachine.FullRowSelect = true;
+ this.lsvMachine.LabelWrap = false;
+ this.lsvMachine.Location = new System.Drawing.Point(10, 23);
+ this.lsvMachine.MultiSelect = false;
+ this.lsvMachine.Name = "lsvMachine";
+ this.lsvMachine.ShowGroups = false;
+ this.lsvMachine.ShowItemToolTips = true;
+ this.lsvMachine.Size = new System.Drawing.Size(640, 264);
+ this.lsvMachine.Sorting = System.Windows.Forms.SortOrder.Ascending;
+ this.lsvMachine.TabIndex = 0;
+ this.lsvMachine.UseCompatibleStateImageBehavior = false;
+ this.lsvMachine.View = System.Windows.Forms.View.Details;
+ this.lsvMachine.Click += new System.EventHandler(this.OnListViewEnter);
+ this.lsvMachine.Enter += new System.EventHandler(this.OnListViewEnter);
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(660, 554);
+ this.Controls.Add(this.splContainer);
+ this.Controls.Add(this.tbsMain);
+ this.Controls.Add(this.stbMain);
+ this.Name = "MainForm";
+ this.Text = "Environment Manager";
+ this.tbsMain.ResumeLayout(false);
+ this.tbsMain.PerformLayout();
+ this.notifyMenu.ResumeLayout(false);
+ this.splContainer.Panel1.ResumeLayout(false);
+ this.splContainer.Panel2.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.splContainer)).EndInit();
+ this.splContainer.ResumeLayout(false);
+ this.grpUser.ResumeLayout(false);
+ this.grpMachine.ResumeLayout(false);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ListView lsvMachine;
+ private System.Windows.Forms.ListView lsvUser;
+ private System.Windows.Forms.GroupBox grpUser;
+ private System.Windows.Forms.GroupBox grpMachine;
+ private Controls.SplitContainerEx splContainer;
+ private System.Windows.Forms.ToolStrip tbsMain;
+ private System.Windows.Forms.ToolStripButton tbbExit;
+ private System.Windows.Forms.ToolStripSeparator tbsSeparator1;
+ private System.Windows.Forms.ToolStripButton tbbSave;
+ private System.Windows.Forms.ToolStripSeparator tbsSeparator2;
+ private System.Windows.Forms.ToolStripButton tbbCreate;
+ private System.Windows.Forms.ToolStripButton tbbModify;
+ private System.Windows.Forms.ToolStripButton tbbDelete;
+ private System.Windows.Forms.ToolStripButton tbbElevate;
+ private System.Windows.Forms.ToolStripButton tbbDump;
+ private System.Windows.Forms.StatusStrip stbMain;
+ private System.Windows.Forms.ToolStripSeparator tbsSeparator4;
+ private System.Windows.Forms.NotifyIcon notifyIcon;
+ private System.Windows.Forms.ContextMenuStrip notifyMenu;
+ private System.Windows.Forms.ToolStripMenuItem cmiExit;
+ private System.Windows.Forms.ToolStripMenuItem cmiShow;
+ private System.Windows.Forms.ToolStripSeparator cmiSeparator1;
+ private System.Windows.Forms.ToolStripMenuItem cmiHide;
+ private System.Windows.Forms.ToolStripButton tbbHide;
+ private System.Windows.Forms.ToolStripSeparator tbsSeparator3;
+ private System.Windows.Forms.ToolStripButton tbbSettings;
+ }
+}
+
diff --git a/code/src/EnvironmentManager/MainForm.cs b/code/src/EnvironmentManager/MainForm.cs
new file mode 100644
index 0000000..6c20f6f
--- /dev/null
+++ b/code/src/EnvironmentManager/MainForm.cs
@@ -0,0 +1,716 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.EnvironmentManager.Dialogs;
+using Plexdata.EnvironmentManager.Extensions;
+using Plexdata.EnvironmentManager.Internals;
+using Plexdata.EnvironmentManager.Models;
+using Plexdata.EnvironmentManager.Serializers;
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager
+{
+ // TODO: Consider sorting by column, see below how:
+ // https://support.microsoft.com/de-de/help/319401/how-to-sort-a-listview-control-by-a-column-in-visual-c
+
+ public partial class MainForm : Form
+ {
+ private ListView activeListView = null;
+ private Font defaultFont = new Font("Consolas", 11.25f);
+
+ public MainForm()
+ {
+ this.InitializeComponent();
+
+ this.notifyIcon.Visible = Program.Arguments.IsMinimized;
+
+ this.SetDoubleBuffered(this.lsvUser);
+ this.lsvUser.DoubleClick += this.OnListViewDoubleClick;
+ this.lsvUser.KeyDown += this.OnListViewKeyDown;
+
+ this.SetDoubleBuffered(this.lsvMachine);
+ this.lsvMachine.DoubleClick += this.OnListViewDoubleClick;
+ this.lsvMachine.KeyDown += this.OnListViewKeyDown;
+
+ this.Icon = Properties.Resources.MainIcon;
+ this.notifyIcon.Icon = Properties.Resources.MainIcon;
+
+ if (PermissionCheck.IsRunAsAdmin)
+ {
+ this.Text += " (ADMIN)";
+ this.notifyIcon.Text += " (ADMIN)";
+ }
+#if !DEBUG
+ this.tbbDump.Visible = false;
+#endif
+ }
+
+ #region Overwritten protected methods
+
+ protected override void OnLoad(EventArgs args)
+ {
+ base.OnLoad(args);
+ this.PerformReload();
+ }
+
+ protected override void OnShown(EventArgs args)
+ {
+ this.LoadSettings();
+ base.OnShown(args);
+ }
+
+ protected override void SetVisibleCore(Boolean value)
+ {
+ base.SetVisibleCore(this.notifyIcon.Visible ? false : value);
+ }
+
+ protected override void OnClosing(CancelEventArgs args)
+ {
+ base.OnClosing(args);
+ this.SaveSettings();
+ }
+
+ #endregion
+
+ #region List view events
+
+ private void OnListViewKeyDown(Object sender, KeyEventArgs args)
+ {
+ switch (args.KeyCode)
+ {
+ case Keys.Insert:
+ args.Handled = true;
+ this.HandleCreate();
+ break;
+ case Keys.F2:
+ case Keys.Enter:
+ args.Handled = true;
+ this.HandleModify();
+ break;
+ case Keys.Delete:
+ args.Handled = true;
+ this.HandleDelete();
+ break;
+ }
+ }
+
+ private void OnListViewDoubleClick(Object sender, EventArgs args)
+ {
+ this.HandleModify();
+ }
+
+ private void OnListViewEnter(Object sender, EventArgs args)
+ {
+ this.activeListView = sender as ListView;
+ this.EnableButtons();
+ }
+
+ #endregion
+
+ #region Tool bar events
+
+ private void OnCreateClicked(Object sender, EventArgs args)
+ {
+ this.HandleCreate();
+ }
+
+ private void OnDeleteClicked(Object sender, EventArgs args)
+ {
+ this.HandleDelete();
+ }
+
+ private void OnModifyClicked(Object sender, EventArgs args)
+ {
+ this.HandleModify();
+ }
+
+ private void OnElevateClicked(Object sender, EventArgs args)
+ {
+ String message = "Do you want to start program in administration mode?";
+
+ if (this.CheckModified())
+ {
+ message += String.Format("{0}{0}Be aware, all made changes are lost!", Environment.NewLine);
+ }
+
+ if (DialogResult.Yes == MessageBox.Show(this, message, this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2))
+ {
+ if (SelfElevation.Elevate(String.Empty))
+ {
+ this.Close();
+ Application.Exit();
+ }
+ }
+ }
+
+ private void OnDumpClicked(Object sender, EventArgs args)
+ {
+ using (new WaitCursor(this))
+ {
+ foreach (ListViewItem item in this.lsvUser.Items)
+ {
+ Program.Logger.Trace(((EnvironmentVariable)item.Tag).ToString());
+ }
+
+ foreach (ListViewItem item in this.lsvMachine.Items)
+ {
+ Program.Logger.Trace(((EnvironmentVariable)item.Tag).ToString());
+ }
+ }
+ }
+
+ private void OnSaveClicked(Object sender, EventArgs args)
+ {
+ String message = "Possibly dangerous operation! Do you really want to apply the environment changes you made?";
+
+ if (DialogResult.Yes == MessageBox.Show(this, message, this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2))
+ {
+ IList exceptions = new List();
+
+ using (new WaitCursor(this))
+ {
+ if (!EnvironmentSerializer.Save(this.GetModifiedVariables(this.lsvUser), ref exceptions))
+ {
+ Program.Logger.Fatal("Error occurred while serializing user scope environment.");
+ }
+
+ this.SetupListView(this.lsvUser, EnvironmentSerializer.Load(EnvironmentVariableTarget.User));
+
+ if (PermissionCheck.IsRunAsAdmin)
+ {
+ if (!EnvironmentSerializer.Save(this.GetModifiedVariables(this.lsvMachine), ref exceptions))
+ {
+ Program.Logger.Fatal("Error occurred while serializing machine scope environment.");
+ }
+
+ this.SetupListView(this.lsvMachine, EnvironmentSerializer.Load(EnvironmentVariableTarget.Machine));
+ }
+
+ foreach (Exception exception in exceptions)
+ {
+ Program.Logger.Error(exception);
+ }
+
+ Program.SaveSettings();
+ }
+ }
+ }
+
+ private void OnExitClicked(Object sender, EventArgs args)
+ {
+ this.Close();
+ Application.Exit();
+ }
+
+ private void OnHideClicked(Object sender, EventArgs args)
+ {
+ this.notifyIcon.Visible = true;
+ this.Hide();
+ }
+
+ private void OnSettingsClicked(Object sender, EventArgs args)
+ {
+ SettingsDialog dialog = new SettingsDialog();
+
+ dialog.ShowDialog(this);
+
+ if (dialog.DialogResult == DialogResult.OK)
+ {
+ // TODO: Something useful later on...
+ }
+ }
+
+ #endregion
+
+ #region Notify icon events
+
+ private void OnNotifyDoubleClicked(Object sender, MouseEventArgs args)
+ {
+ this.OnNotifyShowClicked(sender, args);
+ }
+
+ private void OnNotifyMenuOpening(Object sender, CancelEventArgs args)
+ {
+ Point point = Control.MousePosition;
+
+ using (new WaitCursor(this))
+ {
+ this.notifyMenu.Items.Clear();
+
+ if (!this.Visible)
+ {
+ IEnumerable variables =
+ EnvironmentSerializer.Load(EnvironmentVariableTarget.User)
+ .Concat(EnvironmentSerializer.Load(EnvironmentVariableTarget.Machine))
+ .Where(x => x.Shift != null && x.Shift.Length > 0);
+
+ foreach (EnvironmentVariable variable in variables)
+ {
+ ToolStripMenuItem strip = new ToolStripMenuItem(variable.Label)
+ {
+ Tag = variable.Scope,
+ };
+
+ foreach (String shift in variable.Shift)
+ {
+ ToolStripMenuItem child = new ToolStripMenuItem(shift)
+ {
+ Checked = String.Compare(shift, variable.Value) == 0
+ };
+
+ child.Click += this.OnNotifyItemClicked;
+ strip.DropDownItems.Add(child);
+ }
+
+ this.notifyMenu.Items.Add(strip);
+ }
+
+ if (variables.Any())
+ {
+ this.notifyMenu.Items.Add(this.cmiSeparator1);
+ }
+
+ this.notifyMenu.Items.Add(this.cmiShow);
+ }
+ else
+ {
+ this.notifyMenu.Items.Add(this.cmiHide);
+ }
+
+ this.notifyMenu.Items.Add(this.cmiExit);
+ }
+
+ this.notifyMenu.Show(point, ToolStripDropDownDirection.AboveLeft);
+ }
+
+ private void OnNotifyItemClicked(Object sender, EventArgs args)
+ {
+ if (!(sender is ToolStripMenuItem affected) || affected.OwnerItem == null)
+ {
+ return;
+ }
+
+ if (!Enum.IsDefined(typeof(EnvironmentVariableTarget), affected.OwnerItem.Tag))
+ {
+ return;
+ }
+
+ EnvironmentVariableTarget target = (EnvironmentVariableTarget)affected.OwnerItem.Tag;
+ String label = affected.OwnerItem.Text;
+ String value = affected.Text;
+
+ try
+ {
+ if (target == EnvironmentVariableTarget.Machine && !PermissionCheck.IsRunAsAdmin)
+ {
+ String message = "Administrator privileges required to change this environment variable!";
+ MessageBox.Show(message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ Environment.SetEnvironmentVariable(label, value, target);
+ }
+ catch (Exception exception)
+ {
+
+ String message = "Could not change environment variable.";
+ Program.Logger.Error(message, exception, new (String, Object)[] { ("Label", label), ("Value", value), ("Target", target.ToString()) });
+ MessageBox.Show($"{message}{Environment.NewLine}{Environment.NewLine}{exception.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void OnNotifyShowClicked(Object sender, EventArgs args)
+ {
+ this.notifyIcon.Visible = false;
+ this.PerformReload();
+ this.Show();
+ }
+
+ private void OnNotifyHideClicked(Object sender, EventArgs args)
+ {
+ this.OnHideClicked(sender, args);
+ }
+
+ private void OnNotifyExitClicked(Object sender, EventArgs args)
+ {
+ this.OnExitClicked(sender, args);
+ }
+
+ #endregion
+
+ #region Other private methods
+
+ private void PerformReload()
+ {
+ using (new WaitCursor(this))
+ {
+ this.SetupListView(this.lsvUser, EnvironmentSerializer.Load(EnvironmentVariableTarget.User));
+ this.SetupListView(this.lsvMachine, EnvironmentSerializer.Load(EnvironmentVariableTarget.Machine));
+
+ if (PermissionCheck.IsRunAsAdmin)
+ {
+ this.tbbElevate.Visible = false;
+ this.tbsSeparator4.Visible = false;
+ }
+
+ this.tbbSave.Enabled = this.CheckModified();
+
+ if (this.lsvUser.Items.Count > 0)
+ {
+ this.activeListView = this.lsvUser;
+ }
+ else if (this.lsvMachine.Items.Count > 0)
+ {
+ this.activeListView = this.lsvMachine;
+ }
+
+ if (this.activeListView != null && this.activeListView.Items.Count > 0)
+ {
+ this.activeListView.Items[0].Selected = true;
+ }
+
+ this.EnableButtons();
+ }
+ }
+
+ private ListViewItem CreateEntry(EnvironmentVariable variable)
+ {
+ return new ListViewItem(new String[] { variable.Label, variable.Value })
+ {
+ Tag = variable,
+ ToolTipText = variable.GetTooltip(),
+ Font = this.GetItemFont(variable),
+ ForeColor = this.GetForeColor(variable),
+ BackColor = this.GetBackColor(variable)
+ };
+ }
+
+ private void SetupListView(ListView affected, IEnumerable variables)
+ {
+ affected.Clear();
+ affected.Columns.Clear();
+
+ this.EnsureColumns(affected);
+
+ foreach (EnvironmentVariable current in variables)
+ {
+ affected.Items.Add(this.CreateEntry(current));
+ }
+
+ this.ResizeColumns(affected);
+ }
+
+ private void AppendListView(ListView affected, EnvironmentVariable variable)
+ {
+ this.EnsureColumns(affected);
+
+ ListViewItem current = this.CreateEntry(variable);
+
+ affected.Items.Add(current);
+
+ current.Selected = true;
+ current.EnsureVisible();
+
+ this.ResizeColumns(affected);
+ }
+
+ private void UpdateListView(ListViewItem affected, EnvironmentVariable variable)
+ {
+ affected.SubItems[0].Text = variable.Label;
+ affected.SubItems[1].Text = variable.Value;
+ affected.Tag = variable;
+ affected.ToolTipText = variable.GetTooltip();
+ affected.Font = this.GetItemFont(variable);
+ affected.ForeColor = this.GetForeColor(variable);
+ affected.BackColor = this.GetBackColor(variable);
+ }
+
+ private void HandleCreate()
+ {
+ if (this.activeListView == null)
+ {
+ return;
+ }
+
+ if (this.activeListView == this.lsvMachine && !PermissionCheck.IsRunAsAdmin)
+ {
+ return;
+ }
+
+ EnvironmentVariableTarget scope = this.activeListView == this.lsvMachine ? EnvironmentVariableTarget.Machine : EnvironmentVariableTarget.User;
+
+ VariableDialog dialog = new VariableDialog(scope);
+
+ dialog.ShowDialog(this);
+
+ if (dialog.DialogResult == DialogResult.OK)
+ {
+ this.AppendListView(this.activeListView, dialog.Variable);
+ }
+
+ this.tbbSave.Enabled = this.CheckModified();
+ }
+
+ private void HandleModify()
+ {
+ if (this.activeListView == null)
+ {
+ return;
+ }
+
+ if (this.activeListView == this.lsvMachine && !PermissionCheck.IsRunAsAdmin)
+ {
+ return;
+ }
+
+ if (this.activeListView.SelectedItems == null || this.activeListView.SelectedItems.Count < 1)
+ {
+ return;
+ }
+
+ if (this.activeListView.SelectedItems[0].Tag is EnvironmentVariable current)
+ {
+ VariableDialog dialog = new VariableDialog(current);
+
+ dialog.ShowDialog(this);
+
+ if (dialog.DialogResult == DialogResult.OK)
+ {
+ this.UpdateListView(this.activeListView.SelectedItems[0], dialog.Variable);
+ }
+ }
+
+ this.tbbSave.Enabled = this.CheckModified();
+ }
+
+ private void HandleDelete()
+ {
+ if (this.activeListView == null)
+ {
+ return;
+ }
+
+ if (this.activeListView == this.lsvMachine && !PermissionCheck.IsRunAsAdmin)
+ {
+ return;
+ }
+
+ if (this.activeListView.SelectedItems == null || this.activeListView.SelectedItems.Count < 1)
+ {
+ return;
+ }
+
+ ListViewItem selected = this.activeListView.SelectedItems[0];
+
+ if (selected.Tag is EnvironmentVariable current)
+ {
+ if (current.IsCreated)
+ {
+ selected.Remove();
+ }
+ else
+ {
+ current.TryChangeStage(EnvironmentVariable.Stages.Deleted);
+
+ selected.ToolTipText = current.GetTooltip();
+ selected.Font = this.GetItemFont(current);
+ selected.ForeColor = this.GetForeColor(current);
+ selected.BackColor = this.GetBackColor(current);
+ }
+ }
+
+ this.tbbSave.Enabled = this.CheckModified();
+ }
+
+ private Color GetForeColor(EnvironmentVariable variable)
+ {
+ switch (variable.Stage)
+ {
+ case EnvironmentVariable.Stages.Created:
+ return Color.Black;
+ case EnvironmentVariable.Stages.Changed:
+ return Color.Black;
+ case EnvironmentVariable.Stages.Deleted:
+ return Color.WhiteSmoke;
+ case EnvironmentVariable.Stages.Nothing:
+ default:
+ return Color.Black;
+ }
+ }
+
+ private Color GetBackColor(EnvironmentVariable variable)
+ {
+ switch (variable.Stage)
+ {
+ case EnvironmentVariable.Stages.Created:
+ return Color.Lime;
+ case EnvironmentVariable.Stages.Changed:
+ return Color.Khaki;
+ case EnvironmentVariable.Stages.Deleted:
+ return Color.Crimson;
+ case EnvironmentVariable.Stages.Nothing:
+ default:
+ return Color.White;
+ }
+ }
+
+ private Font GetItemFont(EnvironmentVariable variable)
+ {
+ switch (variable.Stage)
+ {
+ case EnvironmentVariable.Stages.Created:
+ case EnvironmentVariable.Stages.Changed:
+ case EnvironmentVariable.Stages.Deleted:
+ case EnvironmentVariable.Stages.Nothing:
+ default:
+ return this.defaultFont;
+ }
+ }
+
+ private void EnableButtons()
+ {
+ if (this.activeListView == this.lsvMachine)
+ {
+ Boolean enabled = this.CheckSelected(this.lsvMachine) && PermissionCheck.IsRunAsAdmin;
+
+ this.tbbCreate.Enabled = PermissionCheck.IsRunAsAdmin;
+ this.tbbModify.Enabled = enabled;
+ this.tbbDelete.Enabled = enabled;
+ }
+ else
+ {
+ Boolean enabled = this.CheckSelected(this.lsvUser);
+
+ this.tbbCreate.Enabled = true;
+ this.tbbModify.Enabled = enabled;
+ this.tbbDelete.Enabled = enabled;
+ }
+ }
+
+ private void EnsureColumns(ListView affected)
+ {
+ if (affected.Columns.Count < 1)
+ {
+ affected.Columns.AddRange(
+ new ColumnHeader[]
+ {
+ new ColumnHeader() { Text = nameof(EnvironmentVariable.Label) },
+ new ColumnHeader() { Text = nameof(EnvironmentVariable.Value) },
+ });
+ }
+ }
+
+ private void ResizeColumns(ListView affected)
+ {
+ foreach (ColumnHeader column in affected.Columns)
+ {
+ column.Width = -2;
+ }
+ }
+
+ private Boolean CheckSelected(ListView affected)
+ {
+ return affected.Items.Count > 0 && affected.Items
+ .Cast()
+ .Any(x => x.Selected);
+ }
+
+ private Boolean CheckModified()
+ {
+ return this.CountModified(this.lsvUser) > 0 || this.CountModified(this.lsvMachine) > 0;
+ }
+
+ private Int32 CountModified(ListView affected)
+ {
+ return affected.Items
+ .Cast()
+ .Select(x => x.Tag as EnvironmentVariable)
+ .Where(x => x.IsModified)
+ .Count();
+ }
+
+ private IEnumerable GetModifiedVariables(ListView affected)
+ {
+ return affected.Items
+ .Cast()
+ .Where(x => (x.Tag as EnvironmentVariable).IsModified)
+ .Select(x => (x.Tag as EnvironmentVariable))
+ .AsEnumerable();
+ }
+
+ private void SetDoubleBuffered(Control control)
+ {
+ typeof(Control).InvokeMember("DoubleBuffered",
+ BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
+ null, control, new Object[] { true });
+ }
+
+ private void LoadSettings()
+ {
+ try
+ {
+ // Keep this order of settings assignments!
+
+ this.DesktopBounds = Program.ReadSettingsValue(this.GetType().Name, nameof(this.DesktopBounds))
+ .StringToBounds(this.StandardBounds(new Size(700, 600)));
+
+ Int32 minimumDistance = this.splContainer.Panel1.Top + this.splContainer.Panel1MinSize;
+ Int32 maximumDistance = this.splContainer.Panel2.Bottom - this.splContainer.Panel2MinSize;
+ Int32 defaultDistance = this.splContainer.SplitterDistance;
+
+ this.splContainer.SplitterDistance = Program.ReadSettingsValue(this.GetType().Name, nameof(this.splContainer.SplitterDistance))
+ .StringToInteger(minimumDistance, maximumDistance, defaultDistance);
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Loading settings for main window from configuration has failed.", exception);
+ }
+
+ this.EnsureScreenLocation();
+ }
+
+ private void SaveSettings()
+ {
+ try
+ {
+ Program.SaveSettingsValue(this.GetType().Name, nameof(this.splContainer.SplitterDistance), this.splContainer.SplitterDistance.IntegerToString());
+ Program.SaveSettingsValue(this.GetType().Name, nameof(this.DesktopBounds), this.DesktopBounds.BoundsToString());
+ Program.SaveSettings();
+ }
+ catch (Exception exception)
+ {
+ Program.Logger.Error("Saving settings for main window into configuration has failed.", exception);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/code/src/EnvironmentManager/MainForm.resx b/code/src/EnvironmentManager/MainForm.resx
new file mode 100644
index 0000000..15899a0
--- /dev/null
+++ b/code/src/EnvironmentManager/MainForm.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 113, 17
+
+
+ 209, 17
+
+
+ 315, 17
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Models/CommandArguments.cs b/code/src/EnvironmentManager/Models/CommandArguments.cs
new file mode 100644
index 0000000..c9d2bb2
--- /dev/null
+++ b/code/src/EnvironmentManager/Models/CommandArguments.cs
@@ -0,0 +1,52 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.ArgumentParser.Attributes;
+using System;
+
+namespace Plexdata.EnvironmentManager.Models
+{
+ [HelpLicense]
+ [HelpUtilize]
+ [HelpPreface]
+ [ParametersGroup]
+ public class CommandArguments
+ {
+ [SwitchParameter(BriefLabel = "t", SolidLabel = "tray-icon", IsExclusive = true)]
+ [HelpSummary("Use this argument to initially start the program minimized and as tray icon.")]
+ public Boolean IsMinimized { get; set; }
+
+ [SwitchParameter(BriefLabel = "i", SolidLabel = "install", IsExclusive = true)]
+ [HelpSummary("Use this option to register the program for auto-launch on user login. This option requires a program startup with administrator privileges.")]
+ public Boolean IsInstall { get; set; }
+
+ [SwitchParameter(BriefLabel = "r", SolidLabel = "remove", IsExclusive = true)]
+ [HelpSummary("Use this option to remove the program registration for auto-launch on user login. This option requires a program startup with administrator privileges.")]
+ public Boolean IsRemove { get; set; }
+
+ [SwitchParameter(BriefLabel = "?", SolidLabel = "help")]
+ [HelpSummary("Show this arguments help screen.")]
+ public Boolean IsHelp { get; set; }
+ }
+}
diff --git a/code/src/EnvironmentManager/Models/EnvironmentVariable.cs b/code/src/EnvironmentManager/Models/EnvironmentVariable.cs
new file mode 100644
index 0000000..804082a
--- /dev/null
+++ b/code/src/EnvironmentManager/Models/EnvironmentVariable.cs
@@ -0,0 +1,432 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.CfgParser.Entities;
+using Plexdata.EnvironmentManager.Internals;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Plexdata.EnvironmentManager.Models
+{
+ public class EnvironmentVariable : ICloneable
+ {
+ private static readonly String ConfigSectionName = "EnvironmentVariables";
+ private static readonly String LineSeparator = "[CRLF]";
+ private static readonly String[] LineSeparators = new String[] { EnvironmentVariable.LineSeparator };
+
+ private Stages stage;
+ private EnvironmentVariableTarget scope;
+ private String start;
+ private String label;
+ private String value;
+ private String[] shift;
+
+ public enum Stages
+ {
+ // Already existing and nothing applied yet.
+ Nothing,
+
+ // Created by construction and should never change again.
+ Created,
+
+ // Changed by property but should not overwrite an other status
+ Changed,
+
+ // Overwrites any other status.
+ Deleted,
+ };
+
+ public EnvironmentVariable(EnvironmentVariableTarget scope)
+ {
+ this.scope = scope;
+ this.start = String.Empty;
+ this.label = "NEW";
+ this.value = String.Empty;
+ this.shift = new String[0];
+ this.stage = Stages.Created;
+ }
+
+ public EnvironmentVariable(EnvironmentVariableTarget scope, DictionaryEntry entry)
+ {
+ String key = entry.Key != null ? entry.Key.ToString() : String.Empty;
+ String val = entry.Value != null ? entry.Value.ToString() : String.Empty;
+
+ this.scope = scope;
+ this.start = key.Trim();
+ this.label = key.Trim();
+ this.value = val.Trim();
+ this.shift = new String[0];
+ this.stage = Stages.Nothing;
+ }
+
+ private EnvironmentVariable(EnvironmentVariable other)
+ {
+ this.scope = other.scope;
+ this.start = other.start;
+ this.label = other.label;
+ this.value = other.value;
+ this.shift = other.shift;
+ this.stage = other.stage;
+ }
+
+ public Stages Stage
+ {
+ get
+ {
+ return this.stage;
+ }
+ set
+ {
+ this.TryChangeStage(value);
+ }
+ }
+
+ public EnvironmentVariableTarget Scope
+ {
+ get
+ {
+ return this.scope;
+ }
+ set
+ {
+ if (this.scope != value)
+ {
+ this.scope = value;
+ this.TryChangeStage(Stages.Changed);
+ }
+ }
+ }
+
+ public String Start
+ {
+ get
+ {
+ return this.start;
+ }
+ }
+
+ public String Label
+ {
+ get
+ {
+ return this.label;
+ }
+ set
+ {
+ value = (value ?? String.Empty).Trim();
+
+ if (this.label != value)
+ {
+ this.label = value;
+ this.TryChangeStage(Stages.Changed);
+ }
+ }
+ }
+
+ public String Value
+ {
+ get
+ {
+ return this.value;
+ }
+ set
+ {
+ value = (value ?? String.Empty).Trim();
+
+ if (this.value != value)
+ {
+ this.value = value;
+ this.TryChangeStage(Stages.Changed);
+ }
+ }
+ }
+
+ public String[] Shift
+ {
+ get
+ {
+ if (this.shift == null)
+ {
+ this.shift = new String[0];
+ }
+ return this.shift;
+ }
+ set
+ {
+ value = this.GetArray(value);
+
+ if (!Enumerable.SequenceEqual(this.shift, value))
+ {
+ this.shift = value;
+ this.TryChangeStage(Stages.Changed);
+ }
+ }
+ }
+
+ public Boolean IsCreated
+ {
+ get
+ {
+ return this.stage == Stages.Created;
+ }
+ }
+
+ public Boolean IsChanged
+ {
+ get
+ {
+ return this.stage == Stages.Changed;
+ }
+ }
+
+ public Boolean IsDeleted
+ {
+ get
+ {
+ return this.stage == Stages.Deleted;
+ }
+ }
+
+ public Boolean IsRenamed
+ {
+ get
+ {
+ return this.IsModified && this.start.Length > 0 && String.Compare(this.start, this.label) != 0;
+ }
+ }
+
+ public Boolean IsModified
+ {
+ get
+ {
+ return this.stage != Stages.Nothing;
+ }
+ }
+
+ public Boolean IsReadonly
+ {
+ get
+ {
+ return this.scope == EnvironmentVariableTarget.Machine && !PermissionCheck.IsRunAsAdmin;
+ }
+ }
+
+ public void LoadSettings(ConfigContent content)
+ {
+ if (content == null)
+ {
+ return;
+ }
+
+ ConfigSection section = content.Find(EnvironmentVariable.ConfigSectionName);
+
+ if (section == null)
+ {
+ return;
+ }
+
+ ConfigValue value = section.Find(this.Label);
+
+ if (value == null)
+ {
+ return;
+ }
+
+ this.shift = this.IntoArray(value.Value);
+ }
+
+ public void SaveSettings(ConfigContent content)
+ {
+ if (content == null)
+ {
+ return;
+ }
+
+ ConfigSection section = content.Find(EnvironmentVariable.ConfigSectionName);
+
+ if (section == null)
+ {
+ section = content.Append(EnvironmentVariable.ConfigSectionName);
+ }
+
+ ConfigValue value = section.Find(this.Label);
+
+ if (value == null)
+ {
+ value = new ConfigValue(this.Label);
+ }
+
+ value.Value = this.FromArray(this.shift);
+
+ section[this.Label] = value;
+ }
+
+ public void FreeSettings(ConfigContent content)
+ {
+ if (content == null)
+ {
+ return;
+ }
+
+ ConfigSection section = content.Find(EnvironmentVariable.ConfigSectionName);
+
+ if (section == null)
+ {
+ return;
+ }
+
+ if (!String.IsNullOrWhiteSpace(this.Start))
+ {
+ section.Remove(this.Start);
+ }
+
+ section.Remove(this.Label);
+ }
+
+ public String GetTooltip()
+ {
+ String scope = this.scope.ToString();
+ String stage = this.stage.ToString();
+ String label = this.GetString(this.label);
+ String value = this.GetString(this.value);
+
+ if (value.Length > 60) { value = value.Substring(0, 57) + "..."; }
+
+ return
+ $"{nameof(this.Scope)}:\t{scope}{Environment.NewLine}" +
+ $"{nameof(this.Stage)}:\t{stage}{Environment.NewLine}" +
+ $"{nameof(this.Label)}:\t{label}{Environment.NewLine}" +
+ $"{nameof(this.Value)}:\t{value}";
+ }
+
+ public void TryChangeStage(Stages status)
+ {
+ if (this.stage != status)
+ {
+ // Delete overwrites any other status.
+ if (status == Stages.Deleted)
+ {
+ this.stage = Stages.Deleted;
+ }
+
+ // May become Created, Changed or Deleted.
+ if (this.stage == Stages.Nothing)
+ {
+ this.stage = status;
+ }
+ }
+ }
+
+ public Object Clone()
+ {
+ return new EnvironmentVariable(this);
+ }
+
+ public override String ToString()
+ {
+ return
+ $"{nameof(this.Scope)}: {this.scope.ToString()}, " +
+ $"{nameof(this.IsCreated)}: {this.IsCreated}, " +
+ $"{nameof(this.IsChanged)}: {this.IsChanged}, " +
+ $"{nameof(this.IsDeleted)}: {this.IsDeleted}, " +
+ $"{nameof(this.IsRenamed)}: {this.IsRenamed}, " +
+ $"{nameof(this.IsModified)}: {this.IsModified}, " +
+ $"{nameof(this.IsReadonly)}: {this.IsReadonly}, " +
+ $"{nameof(this.Start)}: {this.GetString(this.start)}, " +
+ $"{nameof(this.Label)}: {this.GetString(this.label)}, " +
+ $"{nameof(this.Value)}: {this.GetString(this.value)}, " +
+ $"{nameof(this.Shift)}: {this.GetString(this.shift)}";
+ }
+
+ private String GetString(String value)
+ {
+ if (value == null)
+ {
+ return "";
+ }
+
+ if (value.Trim().Length < 1)
+ {
+ return "";
+ }
+
+ return value;
+ }
+
+ private String GetString(String[] value)
+ {
+ if (value == null)
+ {
+ return "";
+ }
+
+ if (value.Length < 1)
+ {
+ return "";
+ }
+
+ return String.Join(EnvironmentVariable.LineSeparator, value);
+ }
+
+ private String[] GetArray(String[] value)
+ {
+ return this.IntoArray(this.FromArray(value));
+ }
+
+ private String FromArray(String[] value)
+ {
+ if (value == null)
+ {
+ return String.Empty;
+ }
+
+ if (value.Length < 1)
+ {
+ return String.Empty;
+ }
+
+ return String.Join(EnvironmentVariable.LineSeparator, value);
+ }
+
+ private String[] IntoArray(String value)
+ {
+ List result = new List();
+
+ if (!String.IsNullOrWhiteSpace(value))
+ {
+ foreach (String current in value.Split(EnvironmentVariable.LineSeparators, StringSplitOptions.None))
+ {
+ if (!String.IsNullOrWhiteSpace(current))
+ {
+ result.Add(current.Trim());
+ }
+ }
+ }
+
+ return result.ToArray();
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Program.cs b/code/src/EnvironmentManager/Program.cs
new file mode 100644
index 0000000..b13900b
--- /dev/null
+++ b/code/src/EnvironmentManager/Program.cs
@@ -0,0 +1,322 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.ArgumentParser.Extensions;
+using Plexdata.CfgParser.Converters;
+using Plexdata.CfgParser.Entities;
+using Plexdata.CfgParser.Processors;
+using Plexdata.CfgParser.Settings;
+using Plexdata.EnvironmentManager.Internals;
+using Plexdata.EnvironmentManager.Models;
+using Plexdata.LogWriter.Abstraction;
+using Plexdata.LogWriter.Definitions;
+using Plexdata.LogWriter.Extensions;
+using Plexdata.LogWriter.Logging;
+using Plexdata.LogWriter.Settings;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace Plexdata.EnvironmentManager
+{
+ static class Program
+ {
+ private static ILogger logger = null;
+ private static ConfigContent settings = null;
+ private static CommandArguments arguments = null;
+
+ [STAThread]
+ static void Main()
+ {
+ Application.ThreadException += Program.OnUnhandledThreadException;
+ AppDomain.CurrentDomain.UnhandledException += Program.OnUnhandledDomainException;
+
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+
+ if (Program.Arguments.IsHelp)
+ {
+ MessageBox.Show(Program.Arguments.Generate(70), "Help", MessageBoxButtons.OK, MessageBoxIcon.Question);
+ return;
+ }
+
+ if (Program.Arguments.IsInstall)
+ {
+ Program.RunInstall();
+ return;
+ }
+
+ if (Program.Arguments.IsRemove)
+ {
+ Program.RunRemove();
+ return;
+ }
+
+ Application.Run(new MainForm());
+ }
+
+ internal static ILogger Logger
+ {
+ get
+ {
+ if (Program.logger == null)
+ {
+ Program.logger = new PersistentLogger(Program.GetLoggerSettings());
+ }
+
+ return Program.logger;
+ }
+ }
+
+ internal static ConfigContent Settings
+ {
+ get
+ {
+ if (Program.settings == null)
+ {
+ Program.settings = ConfigReader.Read(Program.GetSettingsFilename());
+
+ if (!Program.settings.Header.IsValid)
+ {
+ Program.settings.Header = ConfigSettings.CreateDefaultHeader("Auto-generated configuration file.", true);
+ }
+ }
+
+ return Program.settings;
+ }
+ }
+
+ internal static CommandArguments Arguments
+ {
+ get
+ {
+ if (Program.arguments == null)
+ {
+ Program.arguments = new CommandArguments();
+ List helper = new List(Environment.GetCommandLineArgs());
+
+ if (helper.Count > 1)
+ {
+ helper.RemoveAt(0); // Remove filename of executing assembly (required).
+ Program.arguments.Process(helper);
+ }
+ }
+
+ return Program.arguments;
+ }
+ }
+
+ internal static void SaveSettings()
+ {
+ if (Program.settings != null)
+ {
+ ConfigWriter.Write(Program.settings, Program.GetSettingsFilename(), true);
+ }
+ }
+
+ internal static String ReadSettingsValue(String sectionName, String valueName)
+ {
+ if (String.IsNullOrWhiteSpace(sectionName) || String.IsNullOrWhiteSpace(valueName))
+ {
+ return String.Empty;
+ }
+
+ ConfigSection section = Program.Settings.Find(sectionName);
+
+ if (section == null)
+ {
+ return String.Empty;
+ }
+
+ ConfigValue value = section.Find(valueName);
+
+ if (value == null)
+ {
+ return String.Empty;
+
+ }
+
+ return value.Value;
+ }
+
+ internal static void SaveSettingsValue(String sectionName, String valueName, String valueData)
+ {
+ if (String.IsNullOrWhiteSpace(sectionName) || String.IsNullOrWhiteSpace(valueName))
+ {
+ return;
+ }
+
+ ConfigSection section = Program.Settings.Find(sectionName);
+
+ if (section == null)
+ {
+ section = Program.Settings.Append(sectionName);
+ }
+
+ ConfigValue value = section.Find(valueName);
+
+ if (value == null)
+ {
+ value = new ConfigValue(valueName);
+ }
+
+ value.Value = valueData ?? String.Empty;
+
+ section[valueName] = value;
+ }
+
+ private static void RunInstall()
+ {
+ if (!PermissionCheck.IsRunAsAdmin)
+ {
+ MessageBox.Show("This option requires administrator privileges.", "Install", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ if (TaskScheduler.IsInstalled())
+ {
+ return;
+ }
+
+ if (!TaskScheduler.Create())
+ {
+ MessageBox.Show("Registration failed, task not created.", "Install", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private static void RunRemove()
+ {
+ if (!PermissionCheck.IsRunAsAdmin)
+ {
+ MessageBox.Show("This option requires administrator privileges.", "Remove", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ if (!TaskScheduler.IsInstalled())
+ {
+ return;
+ }
+
+ if (!TaskScheduler.Delete())
+ {
+ MessageBox.Show("Unregistration failed, Task possibly not completely removed.", "Remove", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private static String GetSettingsFilename()
+ {
+ String filename = Path.ChangeExtension(
+ Path.GetFileName(Application.ExecutablePath), "ini");
+
+ String filepath = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ typeof(Program).Namespace.Replace('.', Path.DirectorySeparatorChar));
+
+ if (!Directory.Exists(filepath))
+ {
+ Directory.CreateDirectory(filepath);
+ }
+
+ String fullname = Path.Combine(filepath, filename);
+
+ if (!File.Exists(fullname))
+ {
+ using (StreamWriter writer = File.CreateText(fullname))
+ {
+ writer.Close();
+ }
+ }
+
+ return fullname;
+ }
+
+ private static String GetLoggingFilename()
+ {
+ return Path.ChangeExtension(Program.GetSettingsFilename(), "log");
+ }
+
+ private static IPersistentLoggerSettings GetLoggerSettings()
+ {
+ ConfigSection section = Program.Settings.Find("Logging");
+
+ if (section == null)
+ {
+ section = Program.Settings.Append(new ConfigSection("Logging"));
+ section.Append(new ConfigValue("LogLevel", LogLevel.Trace.ToString()));
+ }
+
+ Object logLevel = LogLevel.Trace;
+ if (!ValueConverter.TryConvert(section["LogLevel"].Value, typeof(LogLevel), out logLevel))
+ {
+ logLevel = LogLevel.Trace;
+ }
+
+ return new PersistentLoggerSettings()
+ {
+ ShowTime = true,
+ LogTime = LogTime.Utc,
+ LogType = LogType.Raw,
+ LogLevel = (LogLevel)logLevel,
+ Filename = Program.GetLoggingFilename(),
+ IsQueuing = false,
+ IsRolling = false,
+ Threshold = 0,
+ };
+ }
+
+ private static void OnUnhandledThreadException(Object sender, ThreadExceptionEventArgs args)
+ {
+ try
+ {
+ Exception error = args.Exception;
+ Program.Logger.Critical("Unhandled Thread Exception", error);
+ MessageBox.Show(error.Message, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ catch (Exception exception)
+ {
+ MessageBox.Show(exception.ToString(), "Extreme Trouble", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ Application.Exit();
+ }
+
+ private static void OnUnhandledDomainException(Object sender, UnhandledExceptionEventArgs args)
+ {
+ try
+ {
+ Exception error = args.ExceptionObject as Exception;
+ Program.Logger.Critical("Unhandled Domain Exception", error);
+ MessageBox.Show(error.Message, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ catch (Exception exception)
+ {
+ MessageBox.Show(exception.ToString(), "Extreme Trouble", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ Application.Exit();
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Properties/AssemblyInfo.cs b/code/src/EnvironmentManager/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3e38bb5
--- /dev/null
+++ b/code/src/EnvironmentManager/Properties/AssemblyInfo.cs
@@ -0,0 +1,60 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+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("EnvironmentManager")]
+[assembly: AssemblyDescription("This program allows to manage environment variables. Mainly, the program is designed to switch environment variables using the tray icon menu.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("plexdata.de")]
+[assembly: AssemblyProduct("EnvironmentManager")]
+[assembly: AssemblyCopyright("Copyright © 2019 - plexdata.de")]
+[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("c1467758-5e28-4ba1-b416-d8e4685a5609")]
+
+// 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/code/src/EnvironmentManager/Properties/Resources.Designer.cs b/code/src/EnvironmentManager/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..3f7eea7
--- /dev/null
+++ b/code/src/EnvironmentManager/Properties/Resources.Designer.cs
@@ -0,0 +1,163 @@
+//------------------------------------------------------------------------------
+//
+// 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 Plexdata.EnvironmentManager.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", "15.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("Plexdata.EnvironmentManager.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;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap CreateLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("CreateLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap DeleteLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("DeleteLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap DumpLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("DumpLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap ExitLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("ExitLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap FollowLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("FollowLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ internal static System.Drawing.Icon MainIcon {
+ get {
+ object obj = ResourceManager.GetObject("MainIcon", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap ModifyLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("ModifyLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap SaveLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("SaveLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap SettingsLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("SettingsLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap ShieldLargeIcon {
+ get {
+ object obj = ResourceManager.GetObject("ShieldLargeIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/Properties/Resources.resx b/code/src/EnvironmentManager/Properties/Resources.resx
new file mode 100644
index 0000000..0e31ba0
--- /dev/null
+++ b/code/src/EnvironmentManager/Properties/Resources.resx
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Images\create_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\delete_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\dump_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\exit_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\follow_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\MainIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\modify_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\save_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\settings_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Images\shield_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/Properties/Settings.Designer.cs b/code/src/EnvironmentManager/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..5e5cb62
--- /dev/null
+++ b/code/src/EnvironmentManager/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 Plexdata.EnvironmentManager.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.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/code/src/EnvironmentManager/Properties/Settings.settings b/code/src/EnvironmentManager/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/code/src/EnvironmentManager/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/code/src/EnvironmentManager/Serializers/EnvironmentSerializer.cs b/code/src/EnvironmentManager/Serializers/EnvironmentSerializer.cs
new file mode 100644
index 0000000..0632cd6
--- /dev/null
+++ b/code/src/EnvironmentManager/Serializers/EnvironmentSerializer.cs
@@ -0,0 +1,136 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 plexdata.de
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using Plexdata.EnvironmentManager.Exceptions;
+using Plexdata.EnvironmentManager.Models;
+using Plexdata.LogWriter.Extensions;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Plexdata.EnvironmentManager.Serializers
+{
+ public static class EnvironmentSerializer
+ {
+ public static IEnumerable Load(EnvironmentVariableTarget target)
+ {
+ foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables(target))
+ {
+ yield return EnvironmentSerializer.LoadSettings(new EnvironmentVariable(target, entry));
+ }
+ }
+
+ public static Boolean Save(IEnumerable variables, ref IList exceptions)
+ {
+ Int32 occurrences = 0;
+
+ foreach (EnvironmentVariable variable in variables)
+ {
+ try
+ {
+ if (variable == null || !variable.IsModified)
+ {
+ continue;
+ }
+
+ if (variable.IsReadonly)
+ {
+ throw new InvalidOperationException("Unable to process read-only variables.");
+ }
+
+ EnvironmentSerializer.HandleCreated(variable);
+
+ EnvironmentSerializer.HandleChanged(variable);
+
+ EnvironmentSerializer.HandleDeleted(variable);
+ }
+ catch (Exception exception)
+ {
+ occurrences++;
+
+ if (exceptions != null)
+ {
+ exceptions.Add(new EnvironmentException(variable, exception));
+ }
+
+ Program.Logger.Error("Error while saving environment variables.", exception);
+ }
+ }
+
+ return occurrences == 0;
+ }
+
+ private static void HandleCreated(EnvironmentVariable variable)
+ {
+ if (variable.IsCreated)
+ {
+ // Try creating this new environment variable.
+ Environment.SetEnvironmentVariable(variable.Label, variable.Value, variable.Scope);
+ variable.SaveSettings(Program.Settings);
+ }
+ }
+
+ private static void HandleChanged(EnvironmentVariable variable)
+ {
+ if (variable.IsChanged)
+ {
+ if (variable.IsRenamed)
+ {
+ // Try discarding original environment variable.
+ Environment.SetEnvironmentVariable(variable.Start, String.Empty, variable.Scope);
+ variable.FreeSettings(Program.Settings);
+ }
+
+ // Try creating this new environment variable.
+ Environment.SetEnvironmentVariable(variable.Label, variable.Value, variable.Scope);
+ variable.SaveSettings(Program.Settings);
+ }
+ }
+
+ private static void HandleDeleted(EnvironmentVariable variable)
+ {
+ if (variable.IsDeleted)
+ {
+ if (variable.IsRenamed)
+ {
+ // Try discarding original environment variable.
+ Environment.SetEnvironmentVariable(variable.Start, String.Empty, variable.Scope);
+ }
+ else
+ {
+ // Try discarding current environment variable.
+ Environment.SetEnvironmentVariable(variable.Label, String.Empty, variable.Scope);
+ }
+
+ variable.FreeSettings(Program.Settings);
+ }
+ }
+
+ private static EnvironmentVariable LoadSettings(EnvironmentVariable variable)
+ {
+ variable.LoadSettings(Program.Settings);
+ return variable;
+ }
+ }
+}
diff --git a/code/src/EnvironmentManager/app.config b/code/src/EnvironmentManager/app.config
new file mode 100644
index 0000000..56efbc7
--- /dev/null
+++ b/code/src/EnvironmentManager/app.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/EnvironmentManager/packages.config b/code/src/EnvironmentManager/packages.config
new file mode 100644
index 0000000..5628120
--- /dev/null
+++ b/code/src/EnvironmentManager/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file