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