diff --git a/Vrh.Log4Pro.MaintenanceConsole.sln b/Vrh.Log4Pro.MaintenanceConsole.sln new file mode 100644 index 0000000..40c589b --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vrh.Log4Pro.MaintenanceConsole", "Vrh.Log4Pro.MaintenanceConsole\Vrh.Log4Pro.MaintenanceConsole.csproj", "{2AF86207-84E8-479F-A466-CA65C606B009}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2AF86207-84E8-479F-A466-CA65C606B009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AF86207-84E8-479F-A466-CA65C606B009}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AF86207-84E8-479F-A466-CA65C606B009}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AF86207-84E8-479F-A466-CA65C606B009}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {63445CF0-9B46-4509-A9DA-8F125390FDC2} + EndGlobalSection +EndGlobal diff --git a/Vrh.Log4Pro.MaintenanceConsole/App.config b/Vrh.Log4Pro.MaintenanceConsole/App.config new file mode 100644 index 0000000..507f944 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/App.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Vrh.Log4Pro.MaintenanceConsole/Config.xml b/Vrh.Log4Pro.MaintenanceConsole/Config.xml new file mode 100644 index 0000000..1e83aec --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/Config.xml @@ -0,0 +1,160 @@ + + + + VRH Starter for Log4ProIS + System Starter Service for Log4ProIS System + VRH Terminator for Log4ProIS + System Terminator Service for Log4ProIS System + VRH Redis for Log4ProIS + Redis Monitor DataProvider Service for Log4ProIS System + VRH iLogger for Log4ProIS + iLogger Service for Log4ProIS System + VRH ASEMW for CP + ASE Middleware Service for Log4Pro CP System + VRH ASEDC for CP + ASEDC Service for Log4Pro CP System + + + + WebAndon Support Request Monitoring System + WebALM Monitoring System + WebPack Packaging System + Log4ProIS WEB Application + + + C:\Log4ProIS + @DIR_ROOT@\CONFIG + @DIR_ROOT@\SERVICESCRIPTS + @Desktop@\WebPack Folders + @DIR_ROOT@\Live Monitor + + @DIR_ROOT@\VRH.StartStopSystem + @DIR_ROOT@\Redis + @DIR_REDIS@\rdb + @DIR_ROOT@\VRH.iLogger + @DIR_ILOGGER@\Plugins + + @DIR_ROOT@\ASEemu_CP + @DIR_ROOT@\VRH.Log4Pro.ASEMW-DCWF_CP + @DIR_ROOT@\VRH.Log4Pro.ASEDC-DCWF_CP + + @DIR_ROOT@\ASEemu_ALM + @DIR_ROOT@\VRH.Log4Pro.ASEMW-DCWF_ALM + @DIR_ROOT@\VRH.Log4Pro.ASEDC-DCWF_ALM + @DIR_ROOT@\VRH.Log4Pro.ASEDC-ASEMON_ALM + @DIR_ROOT@\VRH.Log4Pro.ACALM + + @DIR_ROOT@\wwwroot_WebPack + @DIR_ROOT@\wwwroot_WebALM + @DIR_ROOT@\wwwroot_WebAndon + @DIR_ROOT@\wwwroot_Log4ProIS + + @DIR_ROOT@\DB + + C:\Log4ProISBackups + @DIR_ROOTBAK@\MSMQtests + @DIR_ROOTBAK@\VRH.Logfiles + @ILOG@\RedisLog + @ILOG@\WindowsEventLogs + @ILOG@\WindowsEventLogs + @ILOG@\ServiceScriptLogs + @DIR_ROOTBAK@\SystemBackups + @DIR_ROOTBAK@\BackupDBOnly + @DIR_ROOTBAK@\TEMP + @DIR_ROOTBAK@\ScheduledSavedReports + @DIR_ROOTBAK@\SavedReports + @DIR_ROOTBAK@\ExportDataReports + @DIR_ROOTBAK@\FTPDownloads + + + @DIR_SCRIPTS@\ServiceScriptsConfig.xml + @DIR_ILOGGERPLUGINS@\VRH.Common.iLogger.TextPlugin.dll.config + @DIR_WEBAPPWEBPACK@\WebIdentity.config + @DIR_WEBAPPWEBALM@\WebIdentity.config + @DIR_WEBAPPWEBANDON@\WebIdentity.config + @DIR_WEBAPPLOG4PROIS@\WebIdentity.config + @DIR_REDIS@\redis.windows.conf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - ColorConsole.cs b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - ColorConsole.cs new file mode 100644 index 0000000..f91854d --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - ColorConsole.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + #region ColorConsole + public static class ColorConsole + { + public static string WideString(string txt) + { + if (txt == null) { return null; }; + if (txt == "") { return " "; }; + string widetxt = null; + for (var i = 0; i < txt.Length; i++) { widetxt += txt[i] + " "; }; + return widetxt.Substring(0, widetxt.Length - 1); + } + + public static string ReadLine(string text = null, ConsoleColor? f = null, ConsoleColor? b = null, string bracket = null, string prefix = "") + { + Write(text, f, b,bracket,prefix); + return Console.ReadLine(); + } + public static void WriteLine(string text = null, ConsoleColor? f = null, ConsoleColor? b = null, string bracket = null,string prefix="", string suffix = "") + { + Write(text, f, b, bracket,prefix,suffix); + Console.WriteLine(); + } + public static void Write(string text = null, ConsoleColor? f = null, ConsoleColor? b = null, string bracket = null, string prefix = "",string suffix="") + { + if (!string.IsNullOrEmpty(prefix)) { Write(prefix); } + if (!string.IsNullOrEmpty(bracket) && bracket.Length==1) { bracket = bracket + bracket; } + if (!string.IsNullOrEmpty(bracket)) { Console.Write($"{bracket[0]}"); } + ConsoleColor savecolorF = Console.ForegroundColor; + ConsoleColor savecolorB = Console.BackgroundColor; + if (f != null) { Console.ForegroundColor = (ConsoleColor)f; } + if (b != null) { Console.BackgroundColor = (ConsoleColor)b; } + if (text != null) { Console.Write($"{text}"); } + Console.ForegroundColor = savecolorF; + Console.BackgroundColor = savecolorB; + if (!string.IsNullOrEmpty(bracket)) { Console.Write($"{bracket[1]}"); } + if (!string.IsNullOrEmpty(prefix)) { Write(suffix); } + } + } + #endregion ColorConsole +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - CommandLineParser.cs b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - CommandLineParser.cs new file mode 100644 index 0000000..b0d56ed --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - CommandLineParser.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + #region CommandLineParser + public static class CommandLineParser + { + public static string Quote(this string txt) { return $"\"{txt.EscapeQuotes()}\""; } + public static string QuoteS(this string txt) { return $"'{txt.EscapeQuotes("'")}'"; } + private static string EscapeQuotes(this string txt,string quotestr="\"") + { + string escapedtxt = txt.Replace("\"", "\\\""); + + return escapedtxt; ; + } + + public static IEnumerable SplitArgs(string commandLine) + { + var result = new StringBuilder(); + + var quoted = false; + var escaped = false; + var started = false; + var allowcaret = false; + for (int i = 0; i < commandLine.Length; i++) + { + var chr = commandLine[i]; + + if (chr == '^' && !quoted) + { + if (allowcaret) + { + result.Append(chr); + started = true; + escaped = false; + allowcaret = false; + } + else if (i + 1 < commandLine.Length && commandLine[i + 1] == '^') + { + allowcaret = true; + } + else if (i + 1 == commandLine.Length) + { + result.Append(chr); + started = true; + escaped = false; + } + } + else if (escaped) + { + result.Append(chr); + started = true; + escaped = false; + } + else if (chr == '"') + { + quoted = !quoted; + started = true; + } + else if (chr == '\\' && i + 1 < commandLine.Length && commandLine[i + 1] == '"') + { + escaped = true; + } + else if (chr == ' ' && !quoted) + { + if (started) yield return result.ToString(); + result.Clear(); + started = false; + } + else + { + result.Append(chr); + started = true; + } + } + + if (started) yield return result.ToString(); + } + public static void UnitTest() + { + Test(1,"One", new[] { "One" }); + Test(2,"One ", new[] { "One" }); + Test(3," One", new[] { "One" }); + Test(4," One ", new[] { "One" }); + Test(5,"One Two", new[] { "One", "Two" }); + Test(6,"One Two", new[] { "One", "Two" }); + Test(7,"One Two", new[] { "One", "Two" }); + Test(8,"\"One Two\"", new[] { "One Two" }); + Test(9,"One \"Two Three\"", new[] { "One", "Two Three" }); + Test(10,"One \"Two Three\" Four", new[] { "One", "Two Three", "Four" }); + Test(11,"One=\"Two Three\" Four", new[] { "One=Two Three", "Four" }); + Test(12, "One\"", new[] { "One" }); + Test(13,"One\"Two Three\" Four", new[] { "OneTwo Three", "Four" }); + Test(14,"One\"Two Three Four", new[] { "OneTwo Three Four" }); + Test(15,"\"One Two\"", new[] { "One Two" }); + Test(16,"One\" \"Two", new[] { "One Two" }); + Test(17,"\"One\" \"Two\"", new[] { "One", "Two" }); + Test(18,"One\\\" Two", new[] { "One\"", "Two" }); + Test(19,"\\\"One\\\" Two", new[] { "\"One\"", "Two" }); + Test(20,"\"One", new[] { "One" }); + Test(21,"One \"\"", new[] { "One", "" }); + Test(22,"One \"", new[] { "One", "" }); + Test(23,"1 A=\"B C\"=D 2", new[] { "1", "A=B C=D", "2" }); + Test(24,"1 A=\"B \\\" C\"=D 2", new[] { "1", "A=B \" C=D", "2" }); + Test(25,"1 \\A 2", new[] { "1", "\\A", "2" }); + Test(26,"1 \\\" 2", new[] { "1", "\"", "2" }); + Test(27,"1 \\\\\" 2", new[] { "1", "\\\"", "2" }); + Test(28,"\"", new[] { "" }); + Test(29,"\\\"", new[] { "\"" }); + Test(30,"'A B'", new[] { "'A", "B'" }); + Test(31,"^", new[] { "^" }); + Test(32,"^A", new[] { "A" }); + Test(33,"^^", new[] { "^" }); + Test(34,"\\^^", new[] { "\\^" }); + Test(35,"^\\\\", new[] { "\\\\" }); + Test(36,"^\"A B\"", new[] { "A B" }); + Test(37,@"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo", new[] { @"/src:C:\tmp\Some Folder\Sub Folder", @"/users:abcdefg@hijkl.com", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" }); + Test(38,"", new string[] { }); + Test(39,"a", new[] { "a" }); + Test(40," abc ", new[] { "abc" }); + Test(41,"a b ", new[] { "a", "b" }); + Test(42,"a b \"c d\"", new[] { "a", "b", "c d" }); + Test(43,"this is a test ", new[] { "this", "is", "a", "test" }); + Test(44,"this \"is a\" test", new[] { "this", "is a", "test" }); + Test(45,"\"C:\\Program Files\"", new[] { "C:\\Program Files" }); + Test(46,"\"He whispered to her \\\"I love you\\\".\"", new[] { "He whispered to her \"I love you\"." }); + Console.WriteLine("Press a key to continue...."); + Console.ReadKey(); + } + static void Test(int id,string cmd, string[] r) + { + string scmd="",sexp=""; + try + { + var sr = SplitArgs(cmd).ToArray(); + scmd = "["+String.Join("][", sr)+"]"; + sexp = "[" + String.Join("][", r) + "]"; + if (r.Length != sr.Length) + { + + Console.WriteLine($"ERROR:{id}"); + Console.WriteLine($" cmdline : {cmd}"); + Console.WriteLine($" splitted: {scmd}"); + Console.WriteLine($" expected: {sexp}"); + return; + } + for (int i = 0; i < r.Length; i++) + if (r[i] != sr[i]) + { + Console.WriteLine($"ERROR:{id}"); + Console.WriteLine($" cmdline : {cmd}"); + Console.WriteLine($" splitted: {scmd}"); + Console.WriteLine($" expected: {sexp}"); + return; + } + } + catch (Exception ex) + { + Console.WriteLine($"EXCEP:{id}"); + Console.WriteLine($" cmdline : {cmd}"); + Console.WriteLine($" splitted: {scmd}"); + Console.WriteLine($" expected: {sexp}"); + return; + } + } + } + #endregion CommandLineParser +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - Menu.cs b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - Menu.cs new file mode 100644 index 0000000..d6bd869 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - Menu.cs @@ -0,0 +1,289 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + public class Menu + { + public delegate Object MenuItemExecutorFunc(Object p, Object o); + public delegate Object MenuItemDisplayerFunc(Object p, int i); + public Menu(string title, string selectionprompt = null) { Title = title; if (!string.IsNullOrWhiteSpace(selectionprompt)) { SelectionPrompt = selectionprompt; } } + #region private fields + private string title; + private string Title { get { return ColorConsole.WideString(title); } set { title = value; } } + private MenuItemDisplayerFunc MenuItemDisplayer; + private SelectionMode selectionMode = Menu.SelectionMode.Multi; + private int HeaderWidth = 9; + private string SelectionPrompt = "Make Your selection!"; + #endregion private fields + + #region public properties + public List MenuItemList { get; private set; } = new List(); + List MenuKeyList { get { return MenuItemList.Select(x => x.Key).ToList(); } } + #endregion public properties + + #region public tools + public Menu SetMenuItemDisplayer(MenuItemDisplayerFunc displayer) + { + MenuItemDisplayer = displayer; + return this; + } + public Menu SetSelectionMode(SelectionMode sm) { selectionMode = sm; return this; } + public Menu SetHeaderWidth(int hw) { HeaderWidth= hw; return this; } + public Menu AddMenuItem(Item mi) + { + MenuItemList.Add(mi); + if (string.IsNullOrWhiteSpace(mi.Key)) { mi.Key = $"#$KEY{MenuItemList.IndexOf(mi)}"; } + return this; + } + public void ClearMenuItemList() { MenuItemList.Clear(); } + public void ExecuteMenu() + { + try + { + while (true) + { + DisplayTitle(); + DisplayItems(); + var sr = Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return; } + else if (sr.Result == Menu.SelectionResult.None) { continue; } + else if (sr.Result == Menu.SelectionResult.Error) { continue; } + Execute(sr.SelectedKeyList); + } + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message,ConsoleColor.Red); + if (ex.InnerException != null) { ColorConsole.WriteLine(ex.InnerException.Message, ConsoleColor.Red); } + ColorConsole.WriteLine("Press any key to continue..."); + } + } + public void DisplayTitle() + { + Console.Clear(); + ColorConsole.WriteLine(Title, ConsoleColor.White); + ColorConsole.WriteLine(new string('-', Title.Length)); + } + public void DisplayItems(int columns = 1, int columnlength = 0) + { + int columncounter = 0; + ColorConsole.WriteLine(); + var i = 1; + Console.SetCursorPosition(0, Console.CursorTop); + foreach (var menuitem in MenuItemList) + { + ColorConsole.Write($"["); + ColorConsole.Write($"{i}"); + if (!string.IsNullOrEmpty(menuitem.Key) && !menuitem.Key.StartsWith("#$KEY")) + { + ColorConsole.Write($":"); + ColorConsole.Write(menuitem.Key, ConsoleColor.Yellow); + } + ColorConsole.Write($"]"); + Console.SetCursorPosition(columnlength * columncounter+HeaderWidth, Console.CursorTop); + + if (!string.IsNullOrEmpty(menuitem.Text)) + { + ColorConsole.Write(menuitem.Text); + } + if (columns == 1) + { + if (!string.IsNullOrEmpty(menuitem.Text)) { ColorConsole.WriteLine(); } + if (MenuItemDisplayer != null) + { + for (var li = 0; li < 100; li++) + { + if (li>0) { ColorConsole.Write(new string(' ',HeaderWidth)); } + var str = (string)MenuItemDisplayer(menuitem.Parameters, li); + if (string.IsNullOrEmpty(str)) { Console.SetCursorPosition(0, Console.CursorTop); } + if (str == null) { break; } + } + } + } + else + { + columncounter++; + if (columncounter == columns) { ColorConsole.WriteLine(); columncounter = 0; } + else + { + Console.SetCursorPosition(columnlength * columncounter - 2, Console.CursorTop); + ColorConsole.Write(" "); + } + } + i++; + } + if (columns != 1) { ColorConsole.WriteLine(); } + if (selectionMode==SelectionMode.Multi) + { + ColorConsole.Write("*", ConsoleColor.Red,bracket:"()");ColorConsole.WriteLine(" All"); + } + ColorConsole.Write("EX", ConsoleColor.Red,bracket:"()");ColorConsole.WriteLine(" Exit"); + } + public class Selection + { + public List SelectedKeyList = null; + public SelectionResult Result; + public List SelectedParameterList; + } + public enum SelectionResult { Exit, None, Error, Ok, } + public enum SelectionMode { Single, Multi, } + public Selection Select() + { + List selectionKeyList=null; + List selectionParList = null; + var result = new Selection(); + string selectionliststr; + ColorConsole.Write(SelectionPrompt,ConsoleColor.Yellow); + ColorConsole.Write(" --> ", ConsoleColor.White); + var remembercursorleft = Console.CursorLeft; + var remembercursortop = Console.CursorTop; + if (selectionMode == SelectionMode.Multi) + { + ColorConsole.WriteLine(); + ColorConsole.WriteLine("Multiple selections are separated with comma(,), enter asterisk(*) to select all)!"); + } + var remembercursorleft2 = Console.CursorLeft; + var remembercursortop2 = Console.CursorTop; + Console.SetCursorPosition(remembercursorleft, remembercursortop); + selectionliststr = Console.ReadLine().ToUpper(); + if (selectionMode == SelectionMode.Multi) + { + Console.SetCursorPosition(remembercursorleft2, remembercursortop2); + } + if (selectionliststr == "EX") + { + result.Result = SelectionResult.Exit; + result.SelectedKeyList = null; + return result; + } + else if (selectionliststr == "") + { + result.Result = SelectionResult.None; + result.SelectedKeyList = null; + return result; + } + else if (selectionMode == SelectionMode.Multi && selectionliststr == "*") + { + selectionKeyList = new List(); + selectionParList = new List(); + var i = 1; + foreach (var mi in MenuItemList) + { + selectionKeyList.Add(mi.Key); + selectionParList.Add(mi.Parameters); + } + result.Result = SelectionResult.Ok; + result.SelectedKeyList = selectionKeyList; + result.SelectedParameterList= selectionParList; + return result; + } + selectionKeyList = new List(); + selectionParList = new List(); + foreach (var selection in selectionliststr.Split(',').ToList()) + { + string _selection = selection; + object _parameters=null; + if (!MenuKeyList.Contains(_selection)) + { + if (!int.TryParse(_selection, out int indexselection) || indexselection < 1 || indexselection > MenuItemList.Count()) + { + IncorrectSelection($"Selected item {_selection} is not valid!"); + result.Result = SelectionResult.Error; + result.SelectedKeyList = null; + return result; + } + _selection = GetItemKey(indexselection-1); + _parameters = GetItem(indexselection - 1).Parameters; + } + selectionKeyList.Add(_selection); + selectionParList.Add(_parameters); + } + result.Result = SelectionResult.Ok; + result.SelectedKeyList = selectionKeyList; + result.SelectedParameterList = selectionParList; + return result; + } + public static void IncorrectSelection(string partxt="") + { + ColorConsole.WriteLine(); + ColorConsole.WriteLine($"Incorrect selection! {partxt} Make a new selection!",ConsoleColor.Red); + Console.WriteLine("Press a key to continue..."); + Console.ReadKey(); + } + + public void Execute(List selectedkeylist) + { + object o = null; + foreach (var mi in MenuItemList) + { + if (selectedkeylist.Contains(mi.Key)) + { + o = mi.Executor(mi.Parameters, o); + } + } + ColorConsole.WriteLine("Press any key to continue...", ConsoleColor.Yellow); Console.ReadKey(); + } + public MenuItemExecutorFunc GetExecutor(string key) + { + return MenuItemList.ElementAt(GetItemIx(key)).Executor; + } + #endregion public tools + + #region private tools + private Menu.Item GetItem(string itemkey) + { + return MenuItemList.FirstOrDefault(x => x.Key == itemkey); + } + private Menu.Item GetItem(int ix) + { + return MenuItemList.ElementAt(ix); + } + private int GetItemIx(string itemkey) + { + return MenuKeyList.IndexOf(itemkey); + } + private string GetItemKey(int ix) + { + return MenuKeyList.ElementAt(ix); + } + public void SetParameters(Menu.Selection r, object p=null) + { + foreach (var mi in this.MenuItemList) + { + var keyindex = r.SelectedKeyList.IndexOf(mi.Key); + if (keyindex != -1) + { + if (p == null) { mi.Parameters = r.SelectedParameterList.ElementAt(keyindex); } else { mi.Parameters = p; } + } + } + } + #endregion private tools + public class Item + { + public Item(string key, string text, MenuItemExecutorFunc executor = null, object parameters = null) + { + Key = key; + Text = text; + Executor = executor; + Parameters = parameters; + } + public string Key; + public string Text; + public MenuItemExecutorFunc Executor; + public object Parameters; + } + } +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/Program.cs b/Vrh.Log4Pro.MaintenanceConsole/Program.cs new file mode 100644 index 0000000..a7fdf3a --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/Program.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + class Program + { + static void Main(string[] args) + { + try { Console.SetWindowSize(120, 64); } + catch (Exception ex) + { + ColorConsole.WriteLine("Change the size of the console fonts smaller!"); + Console.ReadKey(); + return; + } + + var mm = new Menu("Log4ProIS Maintenance Console") + .AddMenuItem(new Menu.Item("WAM", "Web Application Manager", WebApplicationManager.Execute)) + .AddMenuItem(new Menu.Item("WSM", "Windows Service Manager", WindowsServiceManager.Execute)) + .SetSelectionMode(Menu.SelectionMode.Single); + + mm.ExecuteMenu(); + ColorConsole.WriteLine("Press any key to exit..."); Console.ReadKey(); + } + } +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/Properties/AssemblyInfo.cs b/Vrh.Log4Pro.MaintenanceConsole/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1a3cea1 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Vrh.Log4Pro.MaintenanceConsole")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Vrh.Log4Pro.MaintenanceConsole")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2af86207-84e8-479f-a466-ca65c606b009")] + +// 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/Vrh.Log4Pro.MaintenanceConsole/Vrh.Log4Pro.MaintenanceConsole.csproj b/Vrh.Log4Pro.MaintenanceConsole/Vrh.Log4Pro.MaintenanceConsole.csproj new file mode 100644 index 0000000..e26e386 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/Vrh.Log4Pro.MaintenanceConsole.csproj @@ -0,0 +1,167 @@ + + + + + Debug + AnyCPU + {2AF86207-84E8-479F-A466-CA65C606B009} + Exe + Vrh.Log4Pro.MaintenanceConsole + Vrh.Log4Pro.MaintenanceConsole + v4.6.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Web.Administration.11.1.0\lib\netstandard1.5\Microsoft.Web.Administration.dll + + + ..\packages\Microsoft.Win32.Primitives.4.0.1\lib\net46\Microsoft.Win32.Primitives.dll + + + ..\packages\Microsoft.Win32.Registry.4.0.0\lib\net46\Microsoft.Win32.Registry.dll + + + + ..\packages\System.AppContext.4.1.0\lib\net46\System.AppContext.dll + + + + + ..\packages\System.Console.4.0.0\lib\net46\System.Console.dll + + + + ..\packages\System.Diagnostics.DiagnosticSource.4.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Diagnostics.TraceSource.4.0.0\lib\net46\System.Diagnostics.TraceSource.dll + + + ..\packages\System.Diagnostics.Tracing.4.1.0\lib\net462\System.Diagnostics.Tracing.dll + + + ..\packages\System.Globalization.Calendars.4.0.1\lib\net46\System.Globalization.Calendars.dll + + + ..\packages\System.IO.4.1.0\lib\net462\System.IO.dll + + + ..\packages\System.IO.Compression.4.1.0\lib\net46\System.IO.Compression.dll + + + + ..\packages\System.IO.Compression.ZipFile.4.0.1\lib\net46\System.IO.Compression.ZipFile.dll + + + ..\packages\System.IO.FileSystem.4.0.1\lib\net46\System.IO.FileSystem.dll + + + ..\packages\System.IO.FileSystem.Primitives.4.0.1\lib\net46\System.IO.FileSystem.Primitives.dll + + + + ..\packages\System.Net.Http.4.1.0\lib\net46\System.Net.Http.dll + + + ..\packages\System.Net.Sockets.4.1.0\lib\net46\System.Net.Sockets.dll + + + + ..\packages\System.Reflection.4.1.0\lib\net462\System.Reflection.dll + + + ..\packages\System.Reflection.TypeExtensions.4.4.0\lib\net461\System.Reflection.TypeExtensions.dll + + + ..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll + + + ..\packages\System.Runtime.Extensions.4.1.0\lib\net462\System.Runtime.Extensions.dll + + + ..\packages\System.Runtime.InteropServices.4.1.0\lib\net462\System.Runtime.InteropServices.dll + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + + + ..\packages\System.Security.Claims.4.0.1\lib\net46\System.Security.Claims.dll + + + ..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net461\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.0.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.0.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.1.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + + ..\packages\System.Security.Principal.Windows.4.0.0\lib\net46\System.Security.Principal.Windows.dll + + + + ..\packages\System.ServiceProcess.ServiceController.4.1.0\lib\net461\System.ServiceProcess.ServiceController.dll + + + + + + + + + + ..\packages\Vrh.XmlProcessing.1.23.0\lib\net45\Vrh.XmlProcessing.dll + + + + + + + + + + + + + + Always + + + + + + + Always + + + Always + + + + \ No newline at end of file diff --git a/Vrh.Log4Pro.MaintenanceConsole/Vrh.NugetModuls.Documentations/Vrh.XmlProcessing/ReadMe.md b/Vrh.Log4Pro.MaintenanceConsole/Vrh.NugetModuls.Documentations/Vrh.XmlProcessing/ReadMe.md new file mode 100644 index 0000000..3a89a6f --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/Vrh.NugetModuls.Documentations/Vrh.XmlProcessing/ReadMe.md @@ -0,0 +1,958 @@ +# Vrh.XmlProcessing +VRH-s XML paraméter fájlok feldolgozását támogató komponens. +Hasznos függvényekkel, osztályokkal és rendszeresen előforduló szerkezetek leképezésével. + +> Fejlesztve és tesztelve **4.5** .NET framework alatt. +> Teljes funkcionalitás és hatékonyság kihasználásához szükséges legalacsonyabb framework verzió: **4.5** + +## Főbb összetevők + > * [XmlLinqBase osztály](##XmlLinqBase-osztaly) Xml feldogozásokhoz készülő osztályok absztrakt alaposztálya. + > * [VariableDictionary](##VariableDictionary) Behelyettesítendő változók összegyűjtése és a behelyettesítés végrehajtása. + > * [XmlCondition osztály](##XmlCondition-osztaly) A `````` elem feldogozását segítő osztály. + > * [XmlVariable osztály](##XmlVariable-osztaly) Az `````` vagy bármely Name, LCID attribútummal és értékkel rendelkező elem feldogozását segítő osztály. + > * [XmlConnection osztály](##XmlConnection-osztaly) XmlParser kapcsolati string feldogozása és kifejtése. + > * [XmlParser osztály](##XmlParser-osztaly) Az `````` elem feldogozását elvégző osztály. + + > * [ConnectionStringStore osztály](##ConnectionStringStore-osztaly) Kapcsolati sztringeket szolgáltató static osztály. + +## XmlLinqBase osztály +Egy absztrakt alaposztály, mely minden specialitás nélkül alkalmas egy XML állomány +elemeinek és attribútumainak beolvasására és a típusos értékek előállítására. Az osztály +a System.Xml.Linq névtér beli xml kezeléshez nyújt segédeszközöket. Az osztályban minden +tulajdonság és metódus protected hatáskörű. + +Felhasználási minta: +```javascript +public class MyClass : XmlLinqBase { ... } +``` + +Tulajdonságok|Leírás +:----|:---- +CurrentFileName|A Load metódus által legutóbb sikeresen betöltött xml fájl neve. Csak olvasható. Értéke akkor íródik felül, ha a Load minden tekintetben sikeres. +RootElement|GetXElement metódusban megadott útvonal ettől az elemtől számítódik. A Load metódus beállítja (felülírja) ezt az elemet. +EnableTrim|A GetValue metódusok számára engedélyezi a whitespace karakterek eltávolítását a megtalált érték elejéről és végéről. Alapértelmezett értéke: true. + +Metódusok|Leírás +:----|:---- +```void Load(string xmlFileName)```|A megadott fájlból betölti az XML struktúrát. Beállítja a CurrentFileName tulajdonságot. +```XElement GetXElement(params string[] elementPath)```|A RootElement-ben lévő elemtől a sorban megadott elemeken keresztül elérhető elemet adja vissza. +```XElement GetXElement(XElement root, params string[] elementPath)```|A root parméterben megadott elemtől a sorban megadott elemeken keresztül elérhető elemet adja vissza. +```XElement GetXElement(string elementPath, char separator = '/')```|A RootElement-ben lévő elemtől az elementhPath paraméterben megadott útvonalon keresztül elérhető elemet adja vissza. +```XElement GetXElement(string elementPath, bool isRequired, char separator = '/')```|Mint az előző, de az elem hiánya esetén és isRequired igaz értéke esetén hibát dob. +```XElement GetXElement(XElement root, string elementPath, char separator = '/')```|A root paraméterben megadott elemtől az elementhPath paraméterben megadott útvonalon keresztül elérhető elemet adja vissza. +```XElement GetXElement(XElement root, string elementPath, bool isRequired, char separator = '/')```|Mint az előző, de az elem hiánya esetén és isRequired igaz értéke esetén hibát dob. +```T GetValue(XElement xelement, T defaultValue, bool isThrowException = false, bool isRequired = false)```|Visszad egy element értéket a defaultValue típusának megfelelően. +```T GetValue(string attributeName, XElement xelement, T defaultValue, bool isThrowException = false, bool isRequired = false)```|Visszadja az xelement elem alatti, megnevezett attribútum értékét. +```T GetValue(System.Xml.Linq.XAttribute xattribute, T defaultValue, bool isThrowException = false, bool isRequired = false)```|Visszadja az attribútum értékét a kért típusban. +```T GetValue(string stringValue, T defultValue)```|A megadott típusra konvertál egy stringet, ha a konverzió lehetséges. +```TEnum GetEnumValue(XAttribute xattribute, TEnum defaultValue, bool ignoreCase = true)```|Attribútum értéket enum típus értékké konvertálja. +```TEnum GetEnumValue(XElement xelement, TEnum defaultValue, bool ignoreCase = true)```|Elem értéket enum típus értékké konvertálja. +```string GetXmlPath(XAttribute xattribute)```|Xattribute útvonala '/Element0/Element1/Element2.Attribute' formában. +```string GetXmlPath(XElement xelement)```|XElement útvonala 'Element0/Element1/Element2' formában. +```void ThrowEx(string mess, params object[] args)```|System.ApplicationException dobása a megadott formázott üzenettel. + +### Beépített kiterjeszthető osztályok + +#### XmlLinqBase.ElementNames osztály +Általában használatos elemnevek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +VALUE|"Value"|XML tagokban lehetséges 'Value' elem eléréséhez hasznos állandó. + +#### XmlLinqBase.Messages osztály +Általában használatos üzenetek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +ERR_FILENOTEXIST|"File does not exist! File = {0}"|Nem létező fájl üzenete 1 behelyttesítéssel. +ERR_XMLROOT|"The root element is missing or corrupt! File = {0}"|'Root' elem hiányzik vagy hibás XML üzenet 1 behelyettesítéssel. +ERR_MISSINGELEMENT|"The '{0}' element is missing !"|Az elem hiányzik üzenet 1 behelyettesítéssel. +ERR_MISSINGATTRIBUTE|"The '{0}' attribute is missing in the '{1}' element!"|Az attribútum hiányzik üzenet 2 behelyettesítéssel. +ERR_REQUIREDELEMENT|"Value of the '{0}' element is null or empty!"|Szükséges elem null vagy üres üzenet 1 behelyettesítéssel. +ERR_REQUIREDATTRIBUTE|"Value of the '{0}' attribute is null or empty in the '{1}' element!"|Szükséges attribútum null vagy üres üzenet 2 behelyettesítéssel. +ERR_PARSETOTYPE|"The '{0}' string is not {1} type! Place='{2}'"|Típus konvertálása sikertelen üzenet 3 behelyettesítéssel. + + +## VariableDictionary +Egy ```System.Collections.Generic.Dictionary``` alapú osztály, mely kiterjesztésre került abból a célból, hogy alkalmas legyen Név és Érték +kulcspárok tárolására, és azok behelyettesítésére egy megadott cél szövegben. Az osztály létrehozható úgy , hogy tartalmazza a rendszerváltozókat. +A [rendszerváltozók](####A-rendszervaltozok) nevei a ```SystemVariableNames``` statikus osztályban érhetőek el. Ha engedélyezve van, akkor +példányosításkor egyből létrejönnek a rendszer változók is a ...BACK változók kivételével, azok ugyanis behelyettesítéskor értékelődnek ki, +nem is szerepelnek a szótárban. A ...BACK változók kiértékelése csak akkor következik be behelyettesítéskor, ha a rendszerváltozók engedélyezve vannak. +Behelyettesítéskor a változókat az osztály NameSeparator tulajdonság közé illesztve keresi. A NameSeparator tulajdonság +alapértelmezett értéke "@@", de átállítható, ha a környezetben más használatos. + +#### Konstruktorok +##### ```new VariableDictionary()``` +E konstruktor hatására egy olyan példány jön létre, mely a változónevek tekintetében betűérzékeny, nem tartalmazza a rendszerváltozókat, és az alapértelmezett +("@@") névelválasztó állítódik be. + +##### ```new VariableDictionary(string lcid, string userName)``` +E konstruktor hatására egy olyan példány jön létre, mely a változónevek tekintetében betűérzékeny, tartalmazza a rendszerváltozókat, és az alapértelmezett +("@@") névelválasztó állítódik be. A két paraméter az LCID és USERNAME rendszerváltozók értékét állítja be. Az ```lcid``` paraméter kötelező, +és érvényes nyelvi kódnak kell lennie. + +##### ```new VariableDictionary(VariableDictionary.Configuration configuration)``` +E konstruktor esetén a ```configuration``` paraméterben található tulajdonságok határozzák meg az osztály illetve a konstruktor viselkedését. +###### Variable.Configuration osztály +Tulajdonság|Típus|Leírás +:----|:----|:---- +IsCaseSensitive|```bool```|Ha true, akkor a kis és nagy betűk különbözőnek számítanak a változóneveknél. Alapértelmezett érték: true. +IsIncludeSystemVariables|```bool```|Ha true, akkor a példányosításkor automatikusan létrejönnek a rendszerváltozók. Alapértelmezett érték: false. +LCID|```string```|Ha ebben az osztályban az IsIncludeSystemVariables értéke igaz, akkor ez az érték inicializálja az "LCID" rendszerváltozót. Alapértelmezett érték: null. +NameSeparator|```string```|A változónevek elválasztó jelei, mely felülírja az alapértelmezett "@@" értéket. Alapértelmezett értéke: "@@" +UserName|```string```|Ha ebben az osztályban az IsIncludeSystemVariables értéke igaz, akkor ez az érték inicializálja a "USERNAME" rendszerváltozót. + +Ha a rendszer változók létrehozása engedélyezett, akkor az LCID értékének érvényes nyelvi kódnak kell lennie, egyébként hiba keletkezik. + +#### Tulajdonságok és metódusok + +> A táblázatokban csak a ```System.Collections.Generic.Dictionary``` +osztályt kiterjesztő elemeket mutatjuk be. + +Tulajdonságok|Leírás +:----|:---- +CurentCultureInfo|Az LCID változó beállításakor kap értéket. Csak olvasható. +NameSeparator|A változó neveket e separátorok közé illesztve keresi a szövegben. +IsCaseSensitive|A változónevek kis/nagybetű érzékenyek, vagy sem.Csak konstruktoron keresztül állítható, egyébként olvasható. +IncludeSystemVariables|Felismeri-e a rendszer változók neveit, vagy sem.Csak konstruktoron keresztül állítható, egyébként olvasható. + +Ha a NameSeparator hossza 1, akkor a változót keretező kezdő és befejező karakter azonos, egyébként a +2. karakter lesz a befejező jel. Alapértelmezett értéke "@@". Amennyiben szükség van egy alternatív +elválasztó párra, akkor párosával növelhető az elválasztó párok száma. Példa: "@@##". +Ilyenkor elsőként a "@" jelek közé zárt neveket keres, ha ilyet nem talál, akkor kísérletet tesz a "#" +jelek közé zárt változó név keresésre. Ha a Name separator nem 1, 2 vagy páros hoszzúságú, akkor hiba +keletkezik. + +Metódusok|Leírás +:----|:---- +```void Add(string name, string value)```|Egy darab név-érték pár hozzáadása a gyűjteményhez. +```void Add((NameValueCollection collection, bool isOverwrite = false)```|Név-érték párok hozzáadása egy létező ```NameValueCollection```-ból. Ha ```isOverwrite``` igaz, akkor az azonos kulcs értékű elemet felülírja. +```void Add((Dictionary dictionary, bool isOverwrite = false)```|Név-érték párok hozzáadása egy létező ```Dictionary```-ból. Ha ```isOverwrite``` igaz, akkor az azonos kulcs értékű elemet felülírja. +```void Set(string name, string value)```|Megadott nevű változó értékének módosítása. +```bool ContainsVariable(string name)```|Igaz értékkel jelzi, ha a név már szerepel a gyűjteményben. +```bool IsValidName(string name)```|Változónév ellenőrzése. A névnek meg kell felelnie az "[a-zA-Z_]\w*" reguláris kifejezésnek. +```void ResetSystemVariables()```|Rendszerváltozók értékének (újra)beállítása az LCID változó kivételével. +```string Substitution(string text)```|A szövegbe behelyettesíti a gyűjteményben található változók értékét. +```string Substitution(string text, string name)```|A szövegbe behelyettesíti a gyűjteményben a ```name``` változó értékét. +```public List FindVariables(string text)```|A függvény megnézi, hogy a megadott paraméterben léteznek-e a gyűjteményben szereplő változók. A megtalált elemek kulcsaival egy listát képez. + +#### A rendszerváltozók +A rendszerváltozók neve egy statikus SystemVariableNames nevű osztályban érhetőek el. +> Az XML fájban való hivatkozás bemutatásánál az alapértelmezett név elválasztót használtuk. + +Név|XML hivatkozás|Leírás +:----|:----|:------ +LCID|@LCID@|A nyelvi kódot tartalmazó rendszer változó neve. +USERNAME|@USERNAME@|Egy felhasználó név, melyet példányosításkor vagy később is be lehet állítani. +TODAY|@TODAY@|Mai nap rendszer változó neve. +YESTERDAY|@YESTERDAY@|Tegnap rendszer változó neve. +NOW|@NOW@|Most rendszerváltozó neve. +THISWEEKMONDAY|@THISWEEKMONDAY@|E hét hétfő rendszer változó neve. +THISWEEKFRIDAY|@THISWEEKFRIDAY@|E hét péntek rendszer változó neve. +LASTWEEKMONDAY|@LASTWEEKMONDAY@|Múlt hét hétfő rendszer változó neve. +LASTWEEKFRIDAY|@LASTWEEKFRIDAY@|Múlt hét péntek rendszer változó neve +THISMONTH1STDAY|@THISMONTH1STDAY@|E hónap első napja rendszer változó neve. +THISMONTHLASTDAY|@THISMONTHLASTDAY@|E hónap utolsó napja rendszer változó neve. +LASTMONTH1STDAY|@LASTMONTH1STDAY@|Múlt hónap első napja rendszer változó neve. +LASTMONTHLASTDAY|@LASTMONTHLASTDAY@|Múlt hónap utolsó napja rendszer változó neve. +MINUTESBACK|@MINUTESBACK#@|Valahány(#) perccel korábbi időpont. +HOURSBACK|@HOURSBACK#@|Valahány(#) órával korábbi időpont. +DAYSBACK|@DAYSBACK#@|Valahány(#) nappal korábbi nap. +WEEKSBACK|@WEEKSBACK#@|Valahány(#) héttel (1hét=7nap) korábbi nap. +MONTHSBACK|@MONTHSBACK#@|Valahány(#) hónappal korábbi nap. + +A MINUTESBACK, a HOURSBACK és a NOW formátuma "ÉÉÉÉHHNNÓÓPPMM", míg a többi dátum típusú változónak "ÉÉÉÉHHNN". + +Rövidítés|jelentése +:----|:---- +É| év száma 4 számjeggyel +H| a hónap száma 2 számjegyen vezető zéróval +N| a nap száma 2 számjegyen vezető zéróval +Ó| az óra 2 számjegyen vezető zéróval +P| a perc 2 számjegyen vezető zéróval +M| a másodperc 2 számjegyen vezető zéróval + + +#### Osztály használatára egy minta +```javascript +/// +/// Minta a VariableDictionary osztály használatára. +/// +private static void VariableDictionaryTest() +{ + try + { + // Az osztály példányosítása. Nyelvi kód paraméter kötelező. + VariableDictionary vd = new VariableDictionary("hu-HU", User.Identity.Name); + Show(vd); + + System.Threading.Thread.Sleep(1000); + + vd.ResetSystemVariables(); //rendszerváltozók újra beállítása + Show(vd); + + vd.Add("VLTZ", "vltz értéke"); //egy változó hozzáadása + vd["TODAY"] = "ma"; //egy változó módosítása + vd.Remove("NOW"); //egy változó törlése + Show(vd); + + string text = String.Concat( + "aaaaa@YESTERDAY@bbbbbbb@TODAY@cccccccc", + "@LASTMONTHLASTDAY@ddddddddddd\neee@DAYSBACK3@ff", + "ff@WEEKSBACK4@gggg@MONTHSBACK10@hhhh" + ); + string result = vd.Substitution(text); //szövegben lévő hivatkozások behelyettesítése + Console.WriteLine(); + Console.WriteLine($"text= {result}"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } +} + +private static void Show(VariableDictionary vd) +{ + Console.WriteLine(); + foreach (KeyValuePair s in vd) + Console.WriteLine($"Name: {s.Key,-25}Value: {s.Value}"); +} +``` + + + +## XmlCondition osztály +Az alábbi xml struktúra feldolgozását segítő osztály: +```xml + + four + + xxx + five + + +``` +A 'Condition' XML elem megszerzése után egy példányosítással előáll egy XmlCondition +típus. +```javascript +XmlCondition condition = new XmlCondition(XML.Element("Condition")); +``` +Tulajdonság|Típus|Leírás +:----|:----|:---- +Type|```string```|A feltétel típusa. Types enum értékek valamelyike. Alapértelmezett érték: Types.Equal. +Test|```string```|A egyik tesztelendő karakterlánc. Lehet benne XML változó. Types.Match esetén a kiértékelendő karakterléncot tartalmazza. +With|```string```|A másik tesztelendő karakterlánc. Lehet benne XML változó. Types.Match esetén a kiértékelő reguláris kifejezést tartalmazza. +Value|```string```|Igaz értékű feltétel esetén, ez lesz a releváns érték. +Conditions|```List```|XmlCondition típusú elemek listája. Ha a releváns érték más feltétel(ek)től is függ. + +Metódus|Leírás +:----|:---- +bool Evaluation(VariableDictionary xmlVars)|A feltétel kiértékelése. Mivel létezhetnek hivatkozások, ezért meg kell adni a változók gyűjteményét. + +### XmlCondition.Types enum +A feltételekben a "Type" attribútumban megadható típusok. + +Név|Leírás +:----|:---- +```XmlCondition.Types.Else```|Mindig igaz feltétel típus. +```XmlCondition.Types.Equal```|Egyenlő. Ez az alapértelmezés. +```XmlCondition.Types.NotEqual```|Nem egyenlő. +```XmlCondition.Types.Match```|Regex kifejezés. + +### Beépített kiterjeszthető osztályok + +#### XmlCondition.ElementNames osztály +Általában használatos elemnevek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +CONDITIONS|"Conditions"|XML tagokban lehetséges 'Conditions' elem eléréséhez hasznos állandó. +CONDITION|"Condition"|XML tagokban lehetséges 'Condition' elem eléréséhez hasznos állandó. + +#### XmlCondition.AttributeNames osztály +Általában használatos attribútumnevek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +TYPE|"Type"|XML tagokban lehetséges 'Type' attribútum eléréséhez hasznos állandó. +TEST|"Test"|XML tagokban lehetséges 'Test' attribútum eléréséhez hasznos állandó. +WITH|"With"|XML tagokban lehetséges 'With' attribútum eléréséhez hasznos állandó. + + + +## XmlVariable osztály +Az alábbi struktúra feldolgozását segítő osztály: +```xml + + + + four + + xxx + five + + + + three + + +VAGY + +érték +``` +Xml változókat leképező osztály. Tulajdonképpen minden elem, melynek 'Name', 'LCID' attribútuma lehetséges, +és van 'Value' tulajdonsága, és lehetnek benne feltétek (Conditions). +Az 'XmlVar' XML elem megszerzése után egy példányosítással előáll egy XmlVariable típus. +```javascript +XmlVariable variable = new XmlVariable(XML.Element("XmlVar")); +``` + +Tulajdonság|Típus|Leírás +:----|:----|:---- +Name|```string```|A változó neve. Csak olyan változó jön létre a példányosításkor, amelynek létezik a megnevezése. +LCID|```string```|A változó értékét, melyik nyelvi környezet esetén lehet felhasználni. Ha nincs vagy üres, akkor mindegyikben. +Value|```string```| A változó értéke. +Conditions|```List```|A változó végleges értékét befolyásoló feltételek listája. Az első igaz érték lesz a végleges érték. + +Metódus|Leírás +:----|:---- +bool Evaluation(VariableDictionary xmlVars)|A változó kiértékelése. Mivel létezhetnek hivatkozások a feltételekben, ezért meg kell adni a változók gyűjteményét. + +### Beépített kiterjeszthető osztályok + +#### XmlVariable.ElementNames osztály +Általában használatos elemnevek kiterjeszthető osztálya. Az XmlLinqBase hasonló osztályát terjeszti ki. + +Állandó|Értéke|Leírás +:----|:----|:---- +XMLVAR|"XmlVar"|XML tagokban lehetséges 'XmlVar' elem eléréséhez hasznos állandó. +CONNECTIONSTRING|"ConnectionString"|XML tagokban lehetséges 'ConnectionString' elem eléréséhez hasznos állandó. + +#### XmlVariable.AttributeNames osztály +Általában használatos attribútumnevek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +NAME|"Name"|XML tagokban lehetséges 'Name' attribútum eléréséhez hasznos állandó. +LCID|"LCID"|XML tagokban lehetséges 'LCID' attribútum eléréséhez hasznos állandó. + + + +## XmlConnection osztály +Az XmlPaser példányosításához egy kapcsolati sztring szükséges, amelyet ez az osztály ellenőriz és +kifejt. Az alábbi táblázat "Tulajdonság" oszlopában zárójelben az látható, hogy az adott összetevőnek +milyen néven kell szerepelnie a kapcsolati stringben. + +Tulajdonság|Típus|Leírás +:----|:----|:---- +Root (root)|```string```|A gyökér XML fájl az elérési útvonalával együtt, tartalmazhat relatív útvonalat is. +ConfigurationName (config)|```string```|A konfiguráció neve, amit keresünk a gyökér XmlParser fájlban. +File (file)|```string```|Ha ez van megadva a connection stringben, akkor itt van a komponens xml paraméter fájlja. +Element (element)|```string```|Ha ez van megadva a connection stringben, akkor a komponens xml paraméter fájljában ezen elem alatt található a struktúra. + +Metódus|Leírás +:----|:---- +```void SetPath(string appPath)```|A 'Root' és 'File' tulajdonságok relatív értékének feloldása a megadott útvonallal. + +#### Alapértelmezettől eltérő konstruktorok +Konstruktor|Leírás +:----|:---- +```XmlConnection(string xmlConnectionString, bool isRequired = true)```|XmlConnection példányosítása egy kapcsolati sztring megadásával, melynek során a sztring ellenőrzésre és feldolgozásra kerül. +```XmlConnection(string xmlConnectionString, string defaultValue, DefaultTypes defaultType = DefaultTypes.File)```|XmlConnection példányosítása egy kapcsolati sztring megadásával, melynek során a sztring ellenőrzésre és feldolgozásra kerül. Alapértelmezett értéket lehet megadni a "file" vagy "config" elemnek, ha azok üresek egyébként. + +#### Nyilvános állandók +Elérésükre egy minta: +```javascript +XmlConnection.CSNAME_ROOT +``` +Állandó|Értéke|Leírás +:----|:----|:---- +CSNAME_ROOT|"root"|A kapcsolati sztring "root" elem megnevezésének állandója. +CSNAME_CONFIG|"config"|A kapcsolati sztring "config" elem megnevezésének állandója. +CSNAME_FILE|"file"|A kapcsolati sztring "file" elem megnevezésének állandója. +CSNAME_ELEMENT|"element"|A kapcsolati sztring "element" elem megnevezésének állandója. +SEPARATOR_NAMEVALUE|"="|Egy elemben a név és értéket elválasztó jel. +SEPARATOR_ITEM|";"|A kapcsolati sztringben az egyes elemeket elválasztó jel. + +#### Kapcsolati sztring felépítése: +A minimum igény, hogy a 'config' vagy a 'file' tagnak szerepelnie kell. +A config az erősebb, ha mindkettő szerepel. Pár minta: +* "root=D:\SandBox\XmlParser\XmlParser.xml;config=FileManager" vagy +* "root=D:\SandBox\XmlParser\XmlParser.xml;file=D:\aaa\bbb\FileManager.xml;element=RootAlattiElemNév" +* Ha 'root' nem szerepel, akkor a gyökér fájl (1) elsődleges alapértelmezését az alkalmazás appconfig file-jában levő +"Vrh.XmlParser:root" nevű elem tartalmazza; ha ez nem létezik, vagy értéke üres, akkor (2) a másodlagos alapértelmezés +"~/App_Data/XmlParser/XmlParser.xml". +* A felhasználó komponensek fogadhatnak üres kapcsolati stringet, ha számukra +van érvényes alapértelmezett konfiguráció név. Például a FileManager meghívható kapcsolati sztring nélkül, +akkor a FilManager a következő sztringet generálja: "config:FileManager", és ezzel inicializálja az XmlParser-t. + + +## XmlParser osztály +Az 'XmlParser' XML elem feldolgozását elvégző absztrakt osztály. A VRH paraméterező XML +állományainak egységes szerkezetű eleme, mely definiálja az állomány részére a változókat, és kapcsolatokat. +Valamint meghatározza a paraméterezés nyelvi környezetét, ha azt nem adják meg a programban. +Az XmlParser paraméterfájljainak általános felépítése és logikája a következő: +```xml + + + + ...VALUE... + ...VALUE... + ...VALUE... + ...VALUE... + [ + ...VALUE... + [...] + + ] + + + + + + + + + [...] + . + . + . + + +``` +Sample1File: +```xml + + [...] + + + [...] + . + . + . + + + +``` +Sample3File: +```xml + + [...] + . + . + . + +``` +Az osztály a lenti sorrendben és helyeken elvégzi az XmlParser elemek feldolgozását: +* Az XmlParser.xml fájl gyökerében elhelyezett XmlParser elem +* A konfiguráció által meghatározott fájl gyökér eleme alatti XmlParser elem +* A konfiguráció által meghatározott fájl és egy ottani elem alatti XmlParser elem +Az XmlVar elemek (a továbbiakban változók) egy későbbi XmlParser-ban felülírhatóak. +Másképpen fogalmazva: ha a feldolgozás során olyan változót talál, mely már egy korábbi +XmlParser-ban szerepelt, akkor annak értéke felülíródik a későbben megtalált ilyen nevű +változó értékével. Az XmlParser elemek feldolgozása után az előfeldolgozó a paraméterező XML +állományban elvégzi a változókra való hivatkozások feloldását, azaz behelyettesíti a hivatkozások +helyére a változók értékét. Utána a saját xml feldolgozó részére megtartja a tartalmat. +Az XmlParser.RootElement (XmlLinqBase.RootElement) tulajdonságában már egy olyan XML struktúra van, +amelyben a behelyettesítések el lettek végezve. Az XmlParser (XmlLinqBase) által szolgáltatott +metódusok már mind ezen az elemen dolgoznak. + +Az osztály egy absztrakt osztály, felhasználása a következő módon lehetséges: +```javascript +public class MyXmlProcessor : XmlParser +{ + public MyXmlProcessor() : base(xmlConnection, appPath, lcid, otherVars) +} +``` +* **xmlConnection** Az XmlParser kapcsolati objektum. Lásd: [XmlConnection osztály](##XmlConnection-osztaly) +* **appPath** A felhasználó alkalmazásban érvényes alkalmazás mappa. (A '~' jel értéke.) +* **lcid** A nyelvi környezetet meghatározó nyelvi kód. Ha üres, akkor az XmlParser.xml-ben megadott nyelv kód lesz alkalmazva. Ha ott sincs, akkor "en-US". +* **otherVars** Egy szótár, mely név érték párokat tartalmaznak, melyek bekerülnek az XmlVars-ok közé. + +Tulajdonság|Típus|Leírás +:----|:----|:---- +XmlVars|```VariableCollection```|Változók gyűjteménye, mely tartalmazza az összes változót az értékével együtt. +ConnectionStrings|```VariableCollection```|Kapcsolatok gyűjteménye, mely tartalmazza a struktúra összes különböző nevű kapcsolatát. +CurrentFileName|```string```|Az épp feldolgozás alatt álló Xml fájl a teljes fizikai elérési útvonalával. +Configuration|```ConfigurationType```|A megadott nevű komponens Configuration elemének értékei. + +### Beépített static osztályok + +#### XmlParser.Defaults osztály + +Állandó|Értéke|Leírás +:----|:----|:---- +XMLFILE|@"~\App_Data\XmlParser\XmlParser.xml"|Az XMLParser.xml fájl meghatározott helyének állandója. + +A controllerben a következő utasítással feloldható: ```Server.MapPath(XmlParser.Defaults.XMLFILE);``` + +### Beépített osztályok +Ha szükséges vagy hasznos, akkor a felhasználó osztály kiterjesztheti ezeket. + +#### XmlParser.ElementNames osztály +Általában használatos elemnevek kiterjeszthető osztálya. Az XmlLinqBase hasonló osztályát terjeszti ki. + +Állandó|Értéke|Leírás +:----|:----|:---- +XMLPARSER|"XmlParser"|XML tagokban lehetséges 'XmlVar' elem eléréséhez hasznos állandó. +XMLVAR|"XmlVar"|XML tagokban lehetséges 'XmlVar' elem eléréséhez hasznos állandó. +CONNECTIONSTRING|"ConnectionString"|XML tagokban lehetséges 'ConnectionString' elem eléréséhez hasznos állandó. +CONFIGURATIONS|"Configurations"|XML tagokban lehetséges 'Configurations' elem eléréséhez hasznos állandó. +CONFIGURATION|"Configuration"|XML tagokban lehetséges 'Configuration' elem eléréséhez hasznos állandó. + +#### XmlParser.AttributeNames osztály +Általában használatos attribútumnevek kiterjeszthető osztálya. + +Állandó|Értéke|Leírás +:----|:----|:---- +NAME|"Name"|XML tagokban lehetséges 'Name' attribútum eléréséhez hasznos állandó. +LCID|"LCID"|XML tagokban lehetséges 'LCID' attribútum eléréséhez hasznos állandó. +NAMESEPARATOR|"NameSeparator"|XML tagokban lehetséges 'NameSeparator' attribútum eléréséhez hasznos állandó. +FILE|"File"|XML tagokban lehetséges 'File' attribútum eléréséhez hasznos állandó. +ELEMENT|"Element"|XML tagokban lehetséges 'Element' attribútum eléréséhez hasznos állandó. + + + +## ConnectionStringStore osztály +Kapcsolati sztringek tárolójának kezelését és abban való keresést támogató osztály +az egységes használat érdekében. + +Használati minta: +```javascript +// SQL esetén +string csstring = ConnectionStringStore.GetSQL(nameOrRef); +// vagy +string csstring = ConnectionStringStore.Get(nameOrRef); + +// EF DBContext-nél használható így, ha jó az alapértelmezett kapcsolat +public ApplicationDbContext() : base(ConnectionStringStore.Get()) { } + +// Redis esetén +string csstring = ConnectionStringStore.GetRedis(nameOrRef); +// vagy +string csstring = ConnectionStringStore.Get(nameOrRef, ConnectionStringType.Redis); +``` + +Ha a "nameOrRef" paraméter null vagy üres, akkor a "VRH.ConnectionStringStore:[CSTYPE]_connectionString" +nevű kapcsolati sztringet keresi. [CSTYPE] a keresett típus szerinti érték (jelenleg SQL v. Redis). +Ha a feloldás nem jár sikerrel, akkor hiba keletkezik. + +### A keresés algoritmusa: +Az alkalmazás konfigurációs fájljában (App.config vagy Web.config) indul a keresés. + +#### 1. Konfigurációs fájl `````` elem +A nevet megkísérli az `````` elemben megkeresni. Egy minta: +```xml + + + +``` +Ha van olyan `````` bejegyzés, ahol a "name" attribútum egyezik a keresett névvel, +akkor a "connectionString" attribútum értéke lesz a kapcsolati sztring. + +#### 2. Konfigurációs fájl `````` elem +Ha a konfigurációs fájl `````` elemében nem találta a nevet, akkor az `````` elemben keres tovább. +```xml + + + + + +``` +Ha van olyan `````` bejegyzés, ahol a "key" attribútum egyezik a keresett névvel, akkor a "value" attribútum értékét egy +ConnectionStringStore referenciának tekinti. Ha nem talál ilyet, akkor a nevet tekinti egy ConnectionStringStore referenciának. +A referencia megmutatja, hol található a kapcsolati sztringeket tartalmazó fájl, és azon belül az az elem, ahol meghatározott +szerkezetű kapcsolati sztring leírók megtalálhatóak. A referencia által megjelölt helyen, és az `````` elem +`````` elemeiben megadott sztringek egy gyűjteményt alkotnak, melyben a megadot nevet lehet keresni, +A gyűjteményben nem lehetnek egyforma nevek. Hiba akkor jelentkezik, ha a keresett név nem található, vagy a referencia hibás. + +#### ConnectionStringStore referencia formátuma +Teljes formátum: **[fájl az elérési útvonal] / [XMLPath] | [cselem név]** vagy rövid formátum: **[cselem név]** ahol +- **[file elérési útvonal]**: Az xml fájlhoz vezető relatív, vagy abszolút útvonal. +Ha az első karakter '@', akkor az útvonal abszolút, egyébként az exe könyvtárára relatív. +- **[XMLPath]**: A kijelölt xml fájlban a connection string store root eleméig (``````) vezető xml elem hierarchia. +A tagok "/" jellel vannak elválasztva. Tulajdonképpen az XmlParser-ból ismerős XmlConnection.Element értéke, +ezért a gyökér elem nevét nem kell megadni. +- **[cselem név]**: egy connectionString elem neve. A "name" attributum értéke. + +Abban az esetben, ha a ConnectionStringStore referencia „rövid” formátumú, akkor a "[file elérési útvonal] / [XMLpath]" karaktersorozatot a ConnectionStringStore +az App.config file `````` blokkjából a "VRH.ConnectionStringStore" nevű elemből olvassa ki. Ha ilyenkor nincs ilyen, akkor az hiba. + +### Javasolt módszer +A konfigurációs fájlban létre kell hozni a kapcsolati sztring gyűjteményre mutató bejegyzést "VRH.ConnectionStringStore" kulcs értékkel. +```xml + + + +``` +- Az konfigurációs fájl `````` és `````` elemeiben egyéb hivatkozás szükségtelen. +- A ```Get()```, ```GetSQL()``` és ```GetRedis()``` metódusok paraméterében rövid formátumú referenciát kell megadni. +Értsd: csak a kapcsolati sztring nevét. +- Alapértelmezések használata kerülendő. + + + +## LinqXMLProcessorBase osztály +Az xml paraméterfájlok feldolgozását támogató osztály, mely eseménykezeléssel és az xml paraméterfájl változásának figyelésével is fel van vértezve. +Ha nincs elérhető XmlParser gyökérfájl, ezzel akkor is megvalósítható a feldolgozás, de akkor a behelyettesítéseket a feldolgozó osztálynak +kell elvégeznie. Elérhető XmlParser gyökérfájl esetén az xml paraméterfájl átesik az XmlParser előfeldolgozásán, vagyis az XMlParser változók +automatikusan behelyettesítve állnak rendelkezésre a feldolgozás során. + +**Feldolgozó osztály számára látható tagok** + +Tulajdonság|Típus|Leírás +:----|:----|:---- +_AppPath|```string```|A relatív útvonalak feloldó mappája. +_throwException|```bool```|Igaz értéke esetén hibák esetén exception keletkezik. Hamis érték esetén alapértelmezett értékeket eredményez a hibák elfedésére. Alapértelmezett értéke az "appSettings" "Vrh.LinqXMLProcessor.Base:ThrowExceptions" kulcs értéke. Ha ott nincs érték, akkor false. +_xmlFileDefinition|```string```|Az xml paraméter fájl fizikai elérése kiegészítve a fájlon belüli elemre hivatkozással. +_xmlNameSpace|```string```|Az xml namespace tárolására szolgáló mező. +_XmlParser_Root|```string```|XmlParser számára szükséges root fájl helyének megadása. Ha üresen hagyjuk, akkor az alapértelmezés szerinti helyen keresi az XmlParser. Ha relatív (vagyis "~" jellel kezdődik, akkor érdemes kitölteni az _AppPath tulajdonságot is. + +Metódus|Leírás +:----|:---- +```T GetAttribute(XElement element, string attributeName, T defaultValue)```|Visszadja egy XElement típusban található XAttribute típus értékét az elvárt típusban. +```T GetElementValue(XElement element, T defaultValue)```|Visszadja egy XElement típus értékét az elvárt típusban. +```XElement GetRootElement()```|Az xml paraméterfájl gyökér elemének XElement objektumát adja vissza ez a függvény. +```IEnumerable GetAllXElements(params string[] elementPath)```|Visszaadja a paraméterek által kijelölt útvonal alatt lévő összes, az utolsó paraméterben megnevezett TAG-et +```XElement GetXElement(params string[] elementPath)```|Visszadja a gyökér elemtől megadott elemeken át elérhető XElement objektumot. +```T GetValue(string stringValue, T defultValue)```|A megadott típusra konvertál egy sztringet, ha a konverzió lehetséges. + +> A metódusok felsorolása nem teljes!!!. + + +### A korábbi dokumentáció +Ez a leírás a komponens **v1.2.1** kiadásáig bezáróan naprakész. +Igényelt minimális framework verzió: **4.0** +Teljes funkcionalítás és hatékonyság kihasználásához szükséges legalacsonyabb framework verzió: **4.0** +#### Ez a komponens arra szolgál, hogy az XML-ben tárolt beállítások, paraméterek feldolgozása egységes legyen, és a következő elveket kényszerítse: +* ##### A paraméter feldolgozás tisztán OOP-elvű legyen +* ##### Kerüljön kiemelésre egy külön osztályba, amelynek ez a scopja. Így a megvalósítás még véletlenül se legyen összeépítve a felhasználási hely feladataival, megsértve ezzel a Single Responsibility elvet. +* ##### A használt megoldás semmilyen módon ne legyen változás érzékeny, hibatürő legyen. (Ne serializációra épüljön.) +* ##### Kevés implementáció függő kód írásával adjon eredményt. + +Használatára ez a kód minta (a kód részlet copyzható az üjabb paraméter fájlfeldolgoztó kiindulási mintajéként): + +```javascript +using System.Collections.Generic; +using System.Xml.Linq; +using Vrh.LinqXMLProcessor.Base; + +/// TODO: Változtasd meg a felhasználási helynek megfelelő névtérre +namespace YourNameSpace +{ + // TODO: Töröld az instrukciós megjegyzéseket (TODO) a production-level kódból, miután elvégezted a bennük foglaltakat! + + /// + /// TODO: Mindig nevezd át ezt az osztályt!!! + /// Használd az osztályra vonatkozó elnevezési konvenciókat, beszédes neveket használj a DDD (Domain Driven Development) alapelveinek megfelelően! + /// Naming pattern: {YourClassName}XmlProcessor + /// Mindig használd az XmlProcessor suffix-et! + /// + public class YourClassNameXmlConfigProcessor : LinqXMLProcessorBaseClass + { + /// + /// Constructor + /// TODO: Nevezd át az osztály nevére! + /// + /// XML fájl aminek a feldolgozására az osztály készül + public YourClassNameXmlConfigProcessor(string parameterFile) + { + _xmlFileDefinition = parameterFile; + } + + #region Retrive all information from XML + + // TODO: Írd át vagy töröld ezeket a meglévő példa implemenmtációkat! Az alapelveket bent hagyhatod, hogy később is szem előtt legyenek! + // Alapelvek: + // - Mindig csak olvasható property-ket használj getter megvalósítással, ha az adat visszanyeréséhez nem szükséges paraméter átadása! + // - Csak akkor használj függvényeket, ha a paraméterek átadására van szükség az információ visszanyeréséhez! + // - Mindig légy típusos! Az alaposztály jelenlegi implementációja (v1.1.X) az alábbi típusokat kezeli: int, string, bool, Enumerátor (generikusan)! Ha típus bővítésre lenne szükséged, kérj fejlesztést rá (change request)! + // - Bonyolultabb típusokat elemi feldolgozással építs! Soha ne használj XML alapú szérializációt, amit depcreatednek tekintünk a fejlesztési környezeteinkben! + // - A bonyolultabb típusok kódját ne helyezd el ebben a fájlban, hanem külső definíciókat használj! + // - Ismétlődő információk visszanyerésére (listák, felsorolások), generikus kollekciókat használj (Lis, Dictonary, IEnumerable, stb...) + + /// + /// TODO: Írd felül, vagy töröld ezt a példát! + /// Egyszerű boolean érték visszanyerése adott element értékéből + /// + public bool Property1 + { + get + { + return GetExtendedBoolElementValue(GetXElement(PROPERTY1_ELEMENT_NAME), true); + } + } + + /// + /// TODO: Írd felül, vagy töröld ezt a példát! + /// Egyszerű boolean érték visszanyerése adott element alatti atribute értékéből + /// + public bool Property2 + { + get + { + return GetExtendedBoolAttribute(GetXElement(PROPERTY1_ELEMENT_NAME), ATTRIBUTE1_ATTRIBUTE_IN_PROPERTY1_ELEMENT, false, "1", "yes"); + } + } + + /// + /// TODO: Írd felül, vagy töröld ezt a példát! + /// Lista visszaadása ismétlődő XML elemekből. + /// + public List AllStrings + { + get + { + List returnList = new List(); + foreach (var item in GetAllXElements(STRINGS_ELEMENT_NAME)) + { + returnList.Add(GetStringElementValue(GetXElement(STRING_ELEMENT_NAME))); + } + return returnList; + } + } + + /// + /// TODO: Írd felül, vagy töröld ezt a példát! + /// Arra példa, hogy mikor használjunk metódust, property helyett: ha az adott információ visszanyeréséhez valamilyen paramétert akarunk felhasználni. + /// Használjunk Get prefixet ezen metódusok nevében! Adjunk DDD leveknek megfelelő beszédes neveket! + /// + /// + /// + public List GetStringsUnderGroup(string group) + { + List returnList = new List(); + foreach (var item in GetAllXElements(STRINGS_ELEMENT_NAME)) + { + if (GetStringAttribute(item, GROUP_ATTRIBUTE_IN_STRING_ELEMENT) == group) + { + returnList.Add(GetStringElementValue(GetXElement(STRING_ELEMENT_NAME))); + } + } + return returnList; + } + + #endregion + + #region Defination of namming rules in XML + // A szabályok: + // - Mindig konstansokat használj, hogy az element és az attribútum neveket azon át hivatkozd! + // - Az elnevezések feleljenek meg a konstansokra vonatkozó elnevetési szabályoknak! + // - Az Attribútumok neveiben mindig jelöld, mely elem alatt fordul elő. + // - Az elemekre ne használj, ilyet, mert az elnevezések a hierarchia mélyén túl összetetté (hosszúvá) válnának! Ez alól kivétel, ha nem egyértelmű az elnevezés e nélkül. + // + + //TODO: Töröld, vagy írd felül ezeket a példa konstansokat! + private const string PROPERTY1_ELEMENT_NAME = "Property1"; + private const string ATTRIBUTE1_ATTRIBUTE_IN_PROPERTY1_ELEMENT = "Attribute1"; + private const string STRINGS_ELEMENT_NAME = "Strings"; + private const string STRING_ELEMENT_NAME = "String"; + private const string GROUP_ATTRIBUTE_IN_STRING_ELEMENT = "Group"; + + #endregion + } +} +``` +Felhasználáskor: +* Töröld a TODO megjegyzéseket, miután végrehajtottad őket! +* Csak valós kódot hagyj az osztályban, a példákat töröld, vagy írd át valós elemekre! +* A szabályokra vonatkozó instrukciókat célszerűen hagyd meg a kommentekben, hogyha bárki a jövőben bővíti a feldolgozó osztályt, akkor a szeme előtt legyenek a szabályok. +* Mindig hazsnálj XML commenteket! (Ahogy minden production Level code-ban! Ez egy erős kóddal kapcsolkatos "DONE" feltétel nálunk!) + +#### Hibaérzékenység bekapcsolása: +A komponens alapértelmezésben hibatűrő müködésű, ha a konfigurációs fájlban hiányoznak a keresett információk, akkor defultokat ad. +Azonban bekapcsolhatjuk, hogy az adott környezetben kivételerket dobjon, mikor a konfiguráció oldalon nem talál meg valamit. +Ezt vagy ugy érjük el, ha a környezet konfigurációjában (App.config, Web.config) elérhető egy Vrh.LinqXMLProcessor.Base:ThrowExceptions app settings kulcs, amelynek értéke true. +Vagy a leszármazott osztályban true-ra állkítjuk a _throwException protected property értékét. Figyelem, a property értékének beállítása után nem veszi többé figyelembe a fent írt app settings kulcs értékét. + +Ha hibaérzékenység nem, de például logolás szükséges, akkor ezt a ConfigProcessorEvent eseményre építve lehet felépíteni. A kompones ezen az eseméynen át minden esetben jelzéstz küld a fellépő konfigurációs hibákról, amik miatt defult értékeket használ az adott konfigurációs beállításra. + + + +*** +## Version History: +### 1.18.4 (2020.10.26) Patch: +- Az XmlConnection példányt nem írhatjuk felül büntetlenül, hiszen bizonyos alkalmazások meg pont arra számítanak, hogy ott az általuk ismert érték marad benne. +Az XmlParser.Configuration protected tulajdonság pont arra való, hogy az XmlParser származtatott osztályaiban a feloldott értékeket el lehessen érni. +- Bevezetésre került az XmlParser.Configuration-ben a "Root" tulajdonság, így minden XmlConnection-ban is megadható tulajdonság feloldott értéke elérhető a típusban. + +### 1.18.3 (2020.04.30) Patch: +- Annak a hibának a javítása, hogy a config=xxxx; típusú connectionstring-ek esetén az XmlConnection példány File és Element mezői nem kerültek +kitöltésre, és ez a LinqXMLProcessingBase osztályon belül hibát okozott; a hiba javítása csak a megtapasztalt Exception elhárítására szorítkozik, +de szerintem a teljes javítás igényelné, hogy az XmlConnection példány konstruktorai a connectionstring-et teljesen feloldják és ezeket a mezőket is kitöltsék! + +### 1.18.2 (2020.03.30) Patch: +- Apró javítás a "nuspec" és az "AddToTestNuGetPackages.ps1" fájlban. + +### 1.18.1 (2020.03.30) Patch: +- NameSpace használatának kivétele az XmlLinqBase osztályból. + +### 1.18.0 (2020.03.28) Compatible changes: +- XmlParser osztály kiegészült a publikus "RootElementName" tulajdonsággal, mely a megnyitott fájl gyökér elemének nevét tartalmazza. +Egy XPath kifejezés összeállításához használható, mert ott szükséges a gyökér nevét is megadni egy elem vagy attribútum eléréséhez. +- Most már kap értéket a publikus "CurrentXmlConnectionString" tulajdonság abban az esetben is, ha XmlConnection típussal példányosítottak. +- Frissítés az MSTest.TestAdapter 2.1.0 változatára. (Ez csak a Test projektet érinti!) +- Frissítés az MSTest.TestFramework 2.1.0 változatára. (Ez csak a Test projektet érinti!) +- NuGet csomag módosítása úgy, hogy a modul ReadMe.md "Build Action" tulajdonsága "None" legyen a telepítés után. Install.ps1 hozzáadása. + +### 1.17.0 (2020.02.14) Compatible change: +- XmlParser osztály kiegészült a publikus CurrentXmlConnectionString-gel, ami a konstruktornak átadott xml connectionstring-et tartalmazza. + +### 1.16.0 (2020.02.10) Compatible change: +- Az eddig m_BaseFolder privát field átalakítása BaseFolder public attributummá. + +### 1.15.4 (2020.02.04) Patches: +- A ConnectionStringStore exception-ök sztenderdizálása, plusz info beillesztése a szövegekbe + +### 1.15.4 (2020.01.31) Patches: +Ha nincs megadva az AppPath paraméter, akkor alapértelmezést használt, ami azonban bizonyos esetekben nem működött, +mert a "System.Reflection.Assembly.GetEntryAssembly()" metódus null értékkel tért vissza (pl. amikor a WebALM-et hívtuk a LearALMNew-ból). +Ez a kód volt: +```javascript +if (string.IsNullOrEmpty(xmlparserapppath)) +{ + this._AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly()?.Location); +} +else +{ + this._AppPath = xmlparserapppath; +} +``` +És ez kellett helyette (nem biztos, hogy ez a tökéletes megoldás, lehet, hogy egyes esetekben még így is problémás, +ha pl. ha nem webalkalmazások esetén fordul elő a fenti null értékes hívás!!!!!!!!!) +```javascript +if (string.IsNullOrEmpty(xmlparserapppath)) +{ + this._AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly()?.Location); + if (this._AppPath == null) this._AppPath = HttpContext.Current.Server.MapPath("~"); +} +else +{ + this._AppPath = xmlparserapppath; +} +``` + +### 1.15.3 (2020.01.31) Patches: +- LinqXMLProcessorBaseCommonOrInstance osztályban a GetXElement és GetAllXElement metódusokban hiba javítása. + +### 1.15.3 (2020.01.23) Patches: +- LinqXMLProcessorBaseCommonOrInstance osztályban a GetXElement és GetAllXElement metódusokból a felesleges generikus kivétele + +### 1.15.1 (2020.01.22) Patches: +- Vissza lett téve az XmlLinqBase osztályba a GetXmlPath metódus. + +### 1.15.0 (2020.01.20) +#### ConnectionStringStore: +- Több connectionString xml elem lehet, mindegyikben több connectionString elemmel; funkciója mindössze annyi, hogy a ConnectionStringStore +Xml file-ban a connection string-ek ezáltal csoportosítva írhatók be, a blokkok-ban levő connections string-ek végül is egy közös listába kerülnek, ahogy eddig is. +- Az appconfig file-ok másolása funkció kibővült azzal, hogy meg lehet adni a másolandó file-ok nevét a ConnectionStringStore xml file +appconfigFileDistribution elemében (az elem értéke vesszővel elválasztott név lista, és/vagy az appconfigFileDistribution elemen belüli +File elemek sorozatában megadott nevek (a File elemek mindegyike csak egy file nevet tartalmazhat); eddig ezek a nevek be voltak drótozva, +ezért ez a funkció inkább főverzió-szám emelést igényelne, de spóroljunk a számokkal... +- A másolás csak akkor történik meg, ha az appconfigFileDistribution/Enable= attributuma true értékű. +- A másolás csak az app domain indulása utáni első ConnectionStringStore híváskor történik meg (ez web-es hívások esetén különösen "praktikus"), +kivéve, ha az appconfigFileDistribution/Force= attributum értéke is true, mert ebben az esetben mindenképpen másol, tehát akkor is, ha a +file-ok már ott vannak. Azt, hogy a file-ok ott vannak-e, a file-ok összehasonlításával dönti el, majd az összehasonlítás eredményét egy +appdomain változóban meg is jegyzi. +v1.14.xxx: kimaradt + +### 1.13.1 (2020.01.19) +- hiba javítása +- xml connectionstringet, amiben csak file, vagy file+element tagok vannak le lehet írni "@c:\F1\F2\xmlfile.xml/xmltag1/xmltag2" formában +ami egyenértékű a "file=c:\F1\F2\xmlfile.xml; element=xmltag1/xmltag2;" leírással; +- ezen leírás esetén a root elemet az appconfigból veszi, az egyéb xml parser tagok nem használhatók. + +### 1.13.0 (2020.01.02) +- Az XmlParser alapú xml feldolgozásba a LinqXMLProcessorBase-zel azonos funkcionalitású (de nem teljesen azonos nevű) +újabb metódusok beépítése: GetRootElement, GetAllXelement, GetExtendedStringValue, GetExtendedBoolValue + +### 1.12.0 (2019.11.28) +- wcf uri connectionstring elemben a distributecfgfiles tag bevezetése, alapértelmezés true + +### 1.11.0 (2019.11.20) +- WCF osztály változtatása / Kialakításra került az a funkció, amely a központi helyen (a ConnectionStringStore-t tartalmazó +könyvtárból) a futó alkalmazás aktív appconfig file-ja mellé másolja a WCFServices.config,WCFClients.config,WCFBindings.config +és WCFBehaviors.config file-okat, amelyek a nevüknek megfelelő xml struktúrákat tartalmazzák. Ezeknek a file-oknak a +használatához szükséges, hogy az appconfig file-ban a Service.Model elemen belül a Services, Clients, Bindings és Behaviors +elemek ezekre a külső fileokra hivatkozzanak, +- WCF osztály változtatása / public static bool EnableDistributeWCFConfigFiles property bevezetése. Alalpértelmezése true. +Ennek értékével lehet szabályozni, hogy a WCF inicializáló hívások a központi WCF konfigurációs file-okat bemásolják-e a web +alkalmazás web.config file-ja mellé. + +### 1.6.0 (2019.10.30) Compatible changes: +- VariableDictionary osztályban alapértelmezett (paraméter nélküli) konstruktor bevezetése. Az osztály betűérzékeny lesz, rendszerváltozók nélkül, az alapértelmezett "@@" név szeparátorral. +- VariableDictionary osztályban új konstruktor bevezetése, mely a beépített Configuration osztályt várja paraméternek, mely meghatározza a betűérzékenységet, rendszerváltozók létrejöttét, a név elválasztót, és két rendszerváltozó (LCID,USERNAME) értékét. + +### 1.5.8 (2019.10.25) Patches: +- Lemaradt Install.ps1 script pótlása a Nuget csomagban + +### 1.5.7 (2019.10.25) Patches: +- A nuget csomag telepítéskor elkészíti azt a minimális konfigurációt, ami a telepítés helyén való instant működőképességhez szükséges. (**Minden Nuget csomagnak ez lenne a célja a belézárt komponenst illetően: A telepítés teljes automatizálása, ami azonnali használatbavételt tesz lehetővé a telepítés helyén a komponens mélyebb ismerete (pl. konfigurálás módja) nélkül!**) + +#### 1.5.6 (2019.10.01) Patches: +- Az XmlConnection összefűzős konstruktorán kellett javítani. Az elsődleges sztringből nem vette figyelembe a Root elemet. + +#### 1.5.5 (2019.09.25) Patches: +- A Get(string,string) metódus megszüntetésre került, mert a ConnectionStringType egy publikus enum, ami mindig elérhető. + +#### 1.5.4 (2019.09.15) Patches: +- A relatív útvonal feloldásában talált hiba javítása. + +#### 1.5.3 (2019.09.13) Patches: +- ConnectionStringStore kiegészült az MSMQ,EmailServer és TCPIPsocket típusokkal és az ezekhez tartozó Get függvényekkel. +- Beépítésre került egy Get(string,string) függvény is, amely string formában fogadja az enum értékek string megfelelőit + +#### 1.5.2 (2019.09.11) Patches: +- XmlLinqBase GetXElement metódusa újabb túlterheléseket kapott. Lásd dokumentáció. + +#### 1.5.1 (2019.09.03) Patches: +- XmlParser inicilizálásakor fellépő hiba esetén megmutatja az aktuális kapcsolati sztringet. Az eredeti hiba az InnerException-ben. +- XElement.Load hiba esetén a fájl nevét is meg lehet tudni. Az eredeti hiba az InnerException-ben. + +#### 1.5.0 Compatible changes: +- EmailServer connectionstring hozzáadása +- LinqProcessorBase osztályhoz új konstruktor, amely xmlparser connection stringet fogad el paraméternek + +#### 1.4.2 (2019.08.11) Patches: +- XElement értékének beolvasásakor az xml escape karaktereket most már visszaalakítja. + +#### 1.4.1 (2019.08.08) Patches: +- A ConnectionStringStore xml struktúra helyét eddig csak egy speciális string-gel lehetett kijelölni, most meg XmlParser connection stringgel IS lehet. +Eddig ez volt a forma: c:\A\B\cstorefile.xml/E1/E2/E3|CSname, emellett most ez is lehet:file=c:\A\B\cstorefile.xml;element=E1/E2/E3;|CSname +A CSStoreReference osztály-t egyenlőre nem szüntettem meg, hanem abba beletettem mindkét formátum kezelését. +A CSStoreReference osztály előállít egy XmlConnection struktúrát, amit vagy az egyik fajta, vagy a másik fajta formátum alapján csinál meg. +A formátumot meg felismeri arról, hogy van-e benne "=" jel. + +#### 1.4.0 (2019.08.01) Compatible changes: +- "id" elem bevezetése a kapcsolati sztringben. A hivatkozott elemet "Id" attribútuma alapján keresi. +- "preset" elem bevezetése a kapcsolati sztringben. A "Preset" nélküli és a "preset" elemmel egyező XmlVar-ok kerülnek a változók közé. +- Új konstruktor, mely egy megadott példányhoz fűzi a megadott kapcsolati sztringet. +- Merge() metódus elkészítése, mely a meglevő objektumhoz fűzi a megadott kapcsolati sztringet. +- "ConnectionStrings" elem feldolgozása kikerült, a továbbiakban nincs ConnectionStrings gyűjtemény az XmlParser-ban. +- A ConnectionStrings tulajdonság megmaradt "Obsolete" jelöléssel, a kompatibilitás miatt, de mindig null lesz. +- Tartalmazhat "config" esetén is "file" vagy egyéb elemet a kapcsolati sztring, mely felülírja/kiegészíti a "Configuration"-ban talált értékeket. +- A HOURSBACK és a MINUTESBACK rendszerváltozók hozzáadása. + +#### 1.3.0 (2019.06.19) Compatible changes: +- A ConnectionsStringStore a jövőben nem ad vissza alapértelmezéseket. +- SQL típus esetén a név nélkül meghívott Get metódusok a "VRH.ConnectionStringStore:SQL_connectionString" nevű kapcsolati sztringet keresik. Ha nem található, akkor hiba keletkezik. +- Redis típus esetén a név nélkül meghívott Get metódusok a "VRH.ConnectionStringStore:Redis_connectionString" nevű kapcsolati sztringet keresik. Ha nem található, akkor hiba keletkezik. + +#### 1.1.1 (2019.06.14) Patches: +- Dokumentáció frissítése. + +#### 1.1.0 (2019.06.14) Compatible changes: +- A VRH.ConnectionStringStore névtérből a "VRHConnectionStringStore" osztály áthelyezése a Vrh.XmlProcessing névtér "ConnectionStringStore" nevű osztályba. +- A ConnectionStringStore XML átesik az XmlParser előfeldolgozásán, ha van elérhető XmlParser gyökér fájl. +- A Vrh.LinqXMLProcessor.Base névtérből a "LinqXMLProcessorBaseClass" osztály áthelyezése a Vrh.XmlProcessing névtér "LinqXMLProcessorBase" nevű osztályába. +- Ha van elérehtő XmlParser gyökér fájl, akkor a LinqXMLProcessorBase átesik az XmlParser előfeldolgozásán. +- Dokumentáció frissítése. + +#### 1.0.0 (2019.05.14) Initial version: +- A Vrh.Web.Common.Lib 1.18.1-es változatában lévő változatból készült. A Vrh.Web.Common.Lib 2.0-ás változatában már nem fog szerepelni. +- Ha az XmlParser connection string nem tartalmaz root elemet, akkor az alapértelmezést elsődlegesen az alkalmazás appconfig file-jában levő "VRH.XmlParser:root" nevű elemből veszi ki. Ha ilyen elem nem létezik, vagy a tartalma üres, akkor használja a programba bedrótozott "~\App_Data\XmlParser\XmlParser.xml" értéket +- Dokumentáció frissítése. +- ReadMe.md-ből a "csharp" jelölések cseréje "javascript"-re. + diff --git a/Vrh.Log4Pro.MaintenanceConsole/WebApplicationManager.cs b/Vrh.Log4Pro.MaintenanceConsole/WebApplicationManager.cs new file mode 100644 index 0000000..85105ca --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/WebApplicationManager.cs @@ -0,0 +1,939 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + #region WebApplicationManager + public static class WebApplicationManager + { + #region public method: Execute + public static object Execute(object o1=null,object o2=null) + { + string xmlcs = "file=Config.xml;element=WebApplications;"; + var config = new WebApplicationManagerXmlProcessor(xmlcs, "", "hu-HU"); + + var menufunctions = new Menu("Manage Web Applications","Select the management function!") + .AddMenuItem(new Menu.Item("WAR", "Register WebApplication",Register)) + .AddMenuItem(new Menu.Item("WAU", "Unregister WebApplication",Unregister)) + .AddMenuItem(new Menu.Item("WAI", "Set impersonate identity",SetImpersonateIdentity)) + .AddMenuItem(new Menu.Item("APS", "Start application Pool",PoolStart)) + .AddMenuItem(new Menu.Item("APF", "Stop application Pool",PoolStop)) + .AddMenuItem(new Menu.Item("APR", "Recycle application Pool",PoolRecycle)) + .AddMenuItem(new Menu.Item("WSS", "Start website",SiteStart)) + .AddMenuItem(new Menu.Item("WSF", "Stop website",SiteStop)) + .AddMenuItem(new Menu.Item("APU", "Set pool user account",PoolSetUserAccount)) + .AddMenuItem(new Menu.Item("R", "Restart website and pool",Restart)) + .SetSelectionMode(Menu.SelectionMode.Single); + + var menuapplications = new Menu("Web applications","Select the web application(s) to manage!") + .SetMenuItemDisplayer(DisplayWebAppInfo) + .SetSelectionMode(Menu.SelectionMode.Multi) + .SetHeaderWidth(4); + + while (true) + { + List wadefList = config.GetDefinitionList(); + Menu.Selection sr; + using (ServerManager sm = new ServerManager()) + { + var sites = sm.Sites; + var pools = sm.ApplicationPools; + menufunctions.DisplayTitle(); + + menuapplications.ClearMenuItemList(); + foreach (var wadef in wadefList) + { + var w = WebApplicationManagerCore.CollectWebAppInfo(sites, pools, wadef); + menuapplications.AddMenuItem(new Menu.Item(null, null, null, w)); + } + } + menuapplications.DisplayItems(1); + + menufunctions.DisplayItems(3,35); + sr = menufunctions.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { break; } + else if (sr.Result == Menu.SelectionResult.None) { continue; } + else if (sr.Result == Menu.SelectionResult.Error) { continue; } + var functionexecutor = menufunctions.GetExecutor(sr.SelectedKeyList.First()); + + foreach (var mi in menuapplications.MenuItemList) { mi.Executor=functionexecutor; } + + sr = menuapplications.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { break; } + else if (sr.Result == Menu.SelectionResult.None) { continue; } + else if (sr.Result == Menu.SelectionResult.Error) { continue; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + menuapplications.Execute(sr.SelectedKeyList); + } + return null; + } + #endregion public method: Execute + #region private method: DisplayWebAppInfo + private static object DisplayWebAppInfo(object waobj,int lineix) + { + WebApplication wa = waobj as WebApplication; + if (lineix==0) + { + ColorConsole.WriteLine($"{wa.Xml_Description}", ConsoleColor.Black, ConsoleColor.White); + } + else if (lineix == 1) + { + ColorConsole.Write($"Web app:"); + ColorConsole.Write($"{wa.Xml_AppName}", ConsoleColor.Cyan); + var fc2 = wa.ApplicationState.Contains("Unregistered") ? ConsoleColor.Red : ConsoleColor.Green; + ColorConsole.Write($"{wa.ApplicationState}", fc2, bracket: "()"); + ColorConsole.Write($", Log.path:"); + ColorConsole.Write($"{wa.ApplicationPath}", ConsoleColor.White); + ColorConsole.Write($", Phy.path:"); + ColorConsole.WriteLine($"{wa.Xml_AppPhysicalPath}", ConsoleColor.White); + } + else if (lineix == 2) + { + ColorConsole.Write($"impersonate identity:"); + ColorConsole.WriteLine($"{wa.AppImpersonateIdentity}", ConsoleColor.White); + } + else if (lineix == 3) + { + ColorConsole.Write($"Site:"); + ColorConsole.Write($"{wa.Xml_SiteName}", ConsoleColor.Cyan); + var fc0 = + wa.SiteState.Contains("Started") ? ConsoleColor.Green + : wa.SiteState.Contains("Unregistered") ? ConsoleColor.Red + : ConsoleColor.Yellow; + ColorConsole.Write($"{wa.SiteState}", fc0, bracket: "()"); + ColorConsole.Write($", Ports:"); + ColorConsole.Write($"{wa.SitePortList}", ConsoleColor.White); + ColorConsole.Write($", Phy.path:"); + ColorConsole.WriteLine($"{wa.Xml_SitePhysicalPath}", ConsoleColor.White); + } + else if (lineix == 4) + { + ColorConsole.Write($"Pool:"); + ColorConsole.Write($"{wa.Xml_PoolName}", ConsoleColor.Cyan); + var fc1 = wa.PoolState == null ? ConsoleColor.Red + : wa.PoolState.Contains("Started") ? ConsoleColor.Green + : wa.PoolState.Contains("Unregistered") ? ConsoleColor.Red + : ConsoleColor.Yellow; + string waps = wa.PoolState == null ? "Unregistered/Unknown" : wa.PoolState; + ColorConsole.Write($"{waps}", fc1, bracket: "()"); + ColorConsole.Write($", identity:"); + ColorConsole.WriteLine($"{wa.PoolAccount}", ConsoleColor.White); + } + else if (lineix==5) + { + ColorConsole.WriteLine(); + } + else + { + return null; + } + return ""; + } + #endregion private method: DisplayWebAppInfo + #region + private static object Restart(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + using (ServerManager sm = new ServerManager()) + { + try { WebApplicationManagerCore.GetPool(sm, d.Xml_PoolName)?.Stop(); } catch { Console.WriteLine($"Pool {d.Xml_PoolName} already stopped."); } + try { WebApplicationManagerCore.GetSite(sm, d.Xml_SiteName)?.Stop(); } catch { Console.WriteLine($"Site {d.Xml_SiteName} already stopped."); } + + SiteStart(d, parameters); + PoolStart(d, parameters); + PoolRecycle(d, parameters); + + Console.WriteLine($"Pool {d.Xml_PoolName} and site {d.Xml_SiteName} restarted."); + return parameters; + } + } + private + static object PoolStart(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + using (ServerManager sm = new ServerManager()) + { + bool success = false; + for (var i = 1; i <= 10; i++) + { + var p = WebApplicationManagerCore.GetPool(sm, d.Xml_PoolName); + if (p != null && p.State == ObjectState.Stopped) { p.Start(); success = true; break; } + else + { + Console.WriteLine($"Trying to start pool {d.Xml_PoolName} ... Press key 'X' to exit..."); + if (Console.KeyAvailable) { if (Console.ReadKey().KeyChar.ToString().ToUpper() == "X") { break; } } + + Thread.Sleep(1000); + } + } + var successstr = success ? "started" : "NOT started"; + Console.WriteLine($"{d.Xml_PoolName} {successstr}."); + return parameters; + } + } + private static object PoolRecycle(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + using (ServerManager sm = new ServerManager()) + { + bool success = false; + for (var i = 1; i < 4; i++) + { + var p = WebApplicationManagerCore.GetPool(sm, d.Xml_PoolName); + if (p != null && p.State == ObjectState.Started) { p.Recycle(); success = true; break; } + else + { + Console.WriteLine($"Trying to recycle pool {d.Xml_PoolName} ... Press key 'X' to exit..."); + if (Console.KeyAvailable) { if (Console.ReadKey().KeyChar.ToString().ToUpper() == "X") { break; } } + Thread.Sleep(1000); + } + } + var successstr = success ? "recycled" : "NOT recycled"; + Console.WriteLine($"{d.Xml_PoolName} {successstr}."); + return parameters; + } + } + private static object PoolStop(object dobj, object parameters) + { + using (ServerManager sm = new ServerManager()) + { + WebApplication d = dobj as WebApplication; + bool success = false; + for (var i = 1; i <= 10; i++) + { + var p = WebApplicationManagerCore.GetPool(sm, d.Xml_PoolName); + if (p != null && p.State == ObjectState.Started) { p.Stop(); success = true; break; } + else + { + Console.WriteLine($"Trying to stop pool {d.Xml_PoolName} ... Press key 'X' to exit..."); + if (Console.KeyAvailable) { if (Console.ReadKey().KeyChar.ToString().ToUpper() == "X") { break; } } + + Thread.Sleep(1000); + } + } + var successstr = success ? "stopped" : "NOT stopped"; + Console.WriteLine($"{d.Xml_PoolName} {successstr}."); + return parameters; + } + } + private static object SiteStart(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + using (ServerManager sm = new ServerManager()) + { + bool success = false; + for (var i = 1; i < 4; i++) + { + var s = WebApplicationManagerCore.GetSite(sm, d.Xml_SiteName); + if (s != null && s.State == ObjectState.Stopped) { s.Start(); success = true; break; } + else + { + Console.WriteLine($"Trying to start site {d.Xml_SiteName} ... Press key 'X' to exit..."); + if (Console.KeyAvailable) { if (Console.ReadKey().KeyChar.ToString().ToUpper() == "X") { break; } } + Thread.Sleep(1000); + } + } + var successstr = success ? "started" : "NOT started"; + Console.WriteLine($"{d.Xml_SiteName} {successstr}."); + return parameters; + } + } + private static object SiteStop(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + using (ServerManager sm = new ServerManager()) + { + bool success = false; + for (var i = 1; i < 4; i++) + { + var s = WebApplicationManagerCore.GetSite(sm, d.Xml_SiteName); + if (s != null && s.State == ObjectState.Started) { s.Stop(); success = true; break; } + else + { + Console.WriteLine($"Trying to stop site {d.Xml_SiteName} ... Press key 'X' to exit..."); + if (Console.KeyAvailable) { if (Console.ReadKey().KeyChar.ToString().ToUpper() == "X") { break; } } + Thread.Sleep(1000); + } + } + var successstr = success ? "stopped" : "NOT stopped"; + Console.WriteLine($"{d.Xml_SiteName} {successstr}."); + return parameters; + } + } + private static object Register(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + WebApplicationManagerCore.RegisterWebApplication(d.Xml_SiteName, d.Xml_PoolName, d.Xml_AppName, d.Xml_AppPhysicalPath, d.Xml_SitePhysicalPath, 8080, d.Xml_RecreatePool, d.Xml_RecreateSite, d.Xml_RecreateApp, d.Xml_PoolPipeLineMode); + Console.WriteLine($"{d.Xml_AppName} is registered in site {d.Xml_SiteName} using pool {d.Xml_PoolName}."); + return parameters; + } + private static object Unregister(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + WebApplicationManagerCore.UnRegisterWebApplication(d.Xml_AppName, d.Xml_PoolName, d.Xml_SiteName, d.Xml_ForceRemovePool, d.Xml_ForceRemoveSite); + Console.WriteLine($"{d.Xml_AppName} is unregistered from site {d.Xml_SiteName} and from pool {d.Xml_PoolName}."); + return parameters; + } + private static object SetImpersonateIdentity(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + string username="", password = "",impersonate="FALSE"; + bool parametersarealreadyset = parameters != null; + string olduserinfo = WebApplicationManagerCore.GetImpersonateIdentityInfo(d.Xml_IdentityConfigFile); + if (parametersarealreadyset) + { + impersonate = ((Dictionary)parameters)["impersonate"]; + username = ((Dictionary)parameters)["username"]; + password = ((Dictionary)parameters)["password"]; + } + else + { + while(true) + { + + Console.WriteLine($"Current user info: {olduserinfo}"); + Console.WriteLine("Set impersonate-on status (true/false), then [Enter], EX=exit."); + impersonate = Console.ReadLine().ToUpper(); + if (impersonate == "EX") { return null; } + else if (impersonate == "FALSE") { break; } + else if (impersonate == "TRUE") + { + Console.WriteLine("Enter username, then [Enter]."); + var un = !string.IsNullOrWhiteSpace(d.Xml_PoolUsername) ? $"empty={d.Xml_PoolUsername }," : ""; + Console.WriteLine($"{un}EX=exit"); + username = Console.ReadLine().ToUpper(); + if (impersonate == "EX") { return null; } + Console.WriteLine("Enter password, then [Enter]"); + var pw = !string.IsNullOrWhiteSpace(d.Xml_PoolPassword) ? $"empty={d.Xml_PoolPassword}," : ""; + Console.WriteLine($"{pw}EX=exit"); + password = Console.ReadLine().ToUpper(); + if (impersonate == "EX") { return null; } + break; + //bool valid = System.Web.ApplicationSerices.AuthenticationService.ValidateUser(username, password, mull); + } + else + { + Menu.IncorrectSelection(); + continue; + } + } + parameters = new Dictionary { { "impersonate", impersonate }, { "username", username }, { "password", password }, }; + } + WebApplicationManagerCore.SetImpersonateIdentity(d.Xml_IdentityConfigFile, impersonate, username, password); + Console.WriteLine($"Impersonate identity changed for webapp {d.Xml_AppName}."); + Console.WriteLine($"From '{olduserinfo}' to '{WebApplicationManagerCore.GetImpersonateIdentityInfo(d.Xml_IdentityConfigFile)}'"); + + return parameters; + } + private static object PoolSetUserAccount(object dobj, object parameters) + { + WebApplication d = dobj as WebApplication; + string username, password = ""; + bool parametersarealreadyset = parameters != null; + string olduserinfo = WebApplicationManagerCore.GetUserInfo(d.Xml_PoolName); + if (parametersarealreadyset) + { + username = ((Dictionary)parameters)["username"]; + password = ((Dictionary)parameters)["password"]; + } + else + { + Console.WriteLine($"Current user info: {olduserinfo}"); + Console.WriteLine("Enter username, then [Enter]."); + Console.WriteLine("Special usernames are: (Empty=API) "); + Console.WriteLine($" (API) {nameof(ProcessModelIdentityType.ApplicationPoolIdentity)}"); + Console.WriteLine($" (LSE) {nameof(ProcessModelIdentityType.LocalService)}"); + Console.WriteLine($" (LSY) {nameof(ProcessModelIdentityType.LocalSystem)}"); + Console.WriteLine($" (NSE) {nameof(ProcessModelIdentityType.NetworkService)}"); + ColorConsole.WriteLine(); + ColorConsole.Write(" (EX) ", ConsoleColor.Red); ColorConsole.WriteLine("Exit"); + username = Console.ReadLine().ToUpper(); + if (username == "EX") { return null; } + if (string.IsNullOrEmpty(username)) { username = nameof(ProcessModelIdentityType.ApplicationPoolIdentity); }; + if (username == "API" || username == nameof(ProcessModelIdentityType.ApplicationPoolIdentity)) { username = nameof(ProcessModelIdentityType.ApplicationPoolIdentity); } + else if (username == "LSE" || username == nameof(ProcessModelIdentityType.LocalService)) { username = nameof(ProcessModelIdentityType.LocalService); } + else if (username == "LSY" || username == nameof(ProcessModelIdentityType.LocalSystem)) { username = nameof(ProcessModelIdentityType.LocalSystem); } + else if (username == "NSE" || username == nameof(ProcessModelIdentityType.NetworkService)) { username = nameof(ProcessModelIdentityType.NetworkService); } + else + { + Console.WriteLine("Enter password, then [Enter]"); + password = Console.ReadLine().ToUpper(); + //bool valid = System.Web.ApplicationSerices.AuthenticationService.ValidateUser(username, password, mull); + } + parameters = new Dictionary { { "username", username }, { "password", password }, }; + } + WebApplicationManagerCore.SetPoolUserAccount(d.Xml_AppName, d.Xml_SiteName, username, password); + Console.WriteLine($"Pool user changed from {olduserinfo} to {WebApplicationManagerCore.GetUserInfo(d.Xml_PoolName)} for pool {d.Xml_PoolName}."); + return parameters; + } + #endregion + } + + public static class WebApplicationManagerCore + { + #region private method: CollectWebAppInfo + public static WebApplication CollectWebAppInfo(SiteCollection sites,ApplicationPoolCollection pools, WebApplication wadef) + { + var wa = new WebApplication(wadef); + try + { + wa.Site = sites[wa.Xml_SiteName]; + if (wa.Site == null) { throw new Exception(); } + else + { + var ss = wa.Site.State; + wa.SiteState = ss.ToString(); + wa.SitePortList = ""; + foreach (var binding in wa.Site.Bindings) + { + if (binding?.EndPoint?.Port != null) { wa.SitePortList += $"[{binding.EndPoint.Port}]"; } + } + var wn = Path.Combine("/", wa.Xml_AppName); + var _wa = wa.Site.Applications[wn]; + if (_wa == null) + { + wa.ApplicationState = "Unregistered/Unknown"; + wa.ApplicationPath = ""; + wa.Application = null; + wa.AppImpersonateIdentity = ""; + } + else + { + wa.ApplicationState = "Registered"; + wa.ApplicationPath = _wa.Path; + wa.Application = _wa; + wa.AppImpersonateIdentity = GetImpersonateIdentityInfo(wa.Xml_IdentityConfigFile); + } + } + } + catch (Exception ex) + { + wa.Site = null; + wa.SiteState = "Unregistered/Unknown"; + wa.SitePortList = ""; + wa.Application = null; + wa.ApplicationState = "Unregistered/Unknown"; + wa.ApplicationPath = ""; + }; + try + { + wa.ApplicationPool = pools[wa.Xml_PoolName]; + if (wa.ApplicationPool == null) { throw new Exception(); } + else + { + wa.PoolState = wa.ApplicationPool.State.ToString(); + string pa = $"{wa.ApplicationPool.ProcessModel.IdentityType}"; + if (wa.ApplicationPool.ProcessModel.IdentityType == ProcessModelIdentityType.SpecificUser) + { + pa += $"({wa.ApplicationPool.ProcessModel.UserName}[{wa.ApplicationPool.ProcessModel.Password}])"; + } + wa.PoolAccount = pa; + } + } + catch + { + wa.ApplicationPool = null; + wa.PoolState = "Unregistered/Unknown"; + wa.PoolAccount = ""; + } + return wa; + } + #endregion private method: CollectWebAppInfo + + #region private method:RegisterWebApplication + public static void RegisterWebApplication(string sitename, string poolname, string appname, string poolphypath, string sitephypath, int siteport, bool recreatepool, bool recreatesite, bool recreateapp,ManagedPipelineMode plmode) + { + using (ServerManager serverManager = new ServerManager()) + { + if (!isExistWebAppPool(serverManager, poolname)) { CreateWebAppPool(serverManager, poolname, plmode); } + else if (recreatepool) { RemoveWebAppPool(serverManager, poolname); CreateWebAppPool(serverManager, poolname, plmode); } + + if (!isExistWebSite(serverManager, sitename)) { CreateWebSite(serverManager, sitename, poolname, sitename, sitephypath, siteport); } + else if (recreatesite) { RemoveWebSite(serverManager, sitename); CreateWebSite(serverManager, sitename, poolname, sitename, sitephypath, siteport);} + + SetItemProperty(serverManager, poolname, "managedRuntimeVersion", "v4.0"); + SetItemProperty(serverManager, poolname, "enable32BitAppOnWin64", "true"); + + if (!isExistWebApp(serverManager, sitename, appname)) { CreateWebApp(serverManager, appname, poolname, sitename, poolphypath); } + else if (recreateapp) { RemoveWebApp(serverManager, appname, sitename); CreateWebApp(serverManager, appname, poolname, sitename, poolphypath); } + + CommitChanges(serverManager, poolname); + } + } + #endregion private method:RegisterWebApplication + #region private method:UnRegisterWebApplication + public static void UnRegisterWebApplication(string appname, string poolname, string sitename = "Default Web Site",bool forceremovepool=false, bool forceremovesite = false) + { + using (ServerManager sm = new ServerManager()) + { + if (isExistWebApp(sm, sitename, appname)) { RemoveWebApp(sm, appname, sitename); } + if (isExistWebAppPool(sm,poolname) && forceremovepool) { RemoveWebAppPool(sm,poolname); } + if (isExistWebSite(sm, sitename) && forceremovesite) { RemoveWebSite(sm, poolname); } + CommitChanges(sm, poolname); + } + } + #endregion private method:UnRegisterWebApplication + #region private method:SetImpersonateIdentity + public static void SetImpersonateIdentity(string filepath, string impersonate, string username, string password) + { + var identityxml = new XElement("identity"); + identityxml.Add(new XAttribute("impersonate", impersonate.ToLower())); + if (impersonate.ToLower() == "true") + { + if (!string.IsNullOrWhiteSpace(username)) + { + identityxml.Add(new XAttribute("userName", username)); + identityxml.Add(new XAttribute("password", string.IsNullOrWhiteSpace(password)?"":password)); + } + } + identityxml.Save(filepath); + } + #endregion private method:SetImpersonateIdentity + #region private method:SetPoolUserAccount + public static void SetPoolUserAccount(string appname, string sitename, string username, string password) + { + using (var sm = new ServerManager()) + { + //var pool = GetPool(GetPoolName(sitename, appname)); + var pool = GetPool(sm, sitename, appname); + if (username == nameof(ProcessModelIdentityType.ApplicationPoolIdentity) + || username == nameof(ProcessModelIdentityType.LocalService) + || username == nameof(ProcessModelIdentityType.LocalSystem) + || username == nameof(ProcessModelIdentityType.NetworkService)) + { + pool.ProcessModel.UserName = null; + pool.ProcessModel.Password = null; + pool.ProcessModel.IdentityType = (ProcessModelIdentityType)Enum.Parse(typeof(ProcessModelIdentityType), username); + } + else // if (ProcessModelIdentityType.SpecificUser) + { + pool.ProcessModel.UserName = username; + pool.ProcessModel.Password = password; + pool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser; + } + CommitChanges(sm, pool.Name); + } + } + #endregion private method:SetPoolUserAccount + #region private method:CommitChanges + private static void CommitChanges(ServerManager serverManager, string poolname) + { + serverManager.CommitChanges(); + if (isExistWebAppPool(poolname)) + { + var p = GetPool(serverManager, poolname); + if (p.State == ObjectState.Started) { p.Recycle(); } + } + } + #endregion private method:CommitChanges + + #region private methods: Get information (with no ServerManager parameter) + public static string GetImpersonateIdentityInfo(string filepath) + { + try + { + var xml = XElement.Load(filepath); + var identityXml = xml; //?.Element(XName.Get("identity")); + if (identityXml == null) { return "no info"; } + else + { + var impersonate = identityXml.Attribute(XName.Get("impersonate"))?.Value; + var username = identityXml.Attribute(XName.Get("userName"))?.Value; + var password = identityXml.Attribute(XName.Get("password"))?.Value; + string unpswinfo = $", username[password]: {username}[{password}]"; + var userinfo = impersonate.ToLower() == "true" && !string.IsNullOrWhiteSpace(username) ? unpswinfo : ""; + var retinfo = $"Impersonate={impersonate}{userinfo}"; + return retinfo; + } + } + catch { return "no info (exception)"; } + } + public static string GetUserInfo(string poolname) + { + using (var sm2 = new ServerManager()) + { + var pool = sm2.ApplicationPools[poolname]; + string userinfo = ""; + if (pool != null) + { + if (pool.ProcessModel.IdentityType == ProcessModelIdentityType.SpecificUser) + { + userinfo = $"{pool.ProcessModel.UserName}({pool.ProcessModel.Password})"; + } + else + { + userinfo = $"{pool.ProcessModel.IdentityType}"; + } + } + return userinfo; + } + } + private static string GetPoolName(string sitename,string appname) + { + using (ServerManager serverManager = new ServerManager()) + { + return GetPoolName(serverManager, sitename, appname); + } + } + public static ApplicationPool GetPool(string sitename, string appname) + { + using (ServerManager serverManager = new ServerManager()) + { + return GetPool(serverManager, sitename, appname); + } + } + public static ApplicationPool GetPool(string poolname) + { + using (ServerManager serverManager = new ServerManager()) + { + return GetPool(serverManager,poolname); + } + } + private static bool isExistWebAppPool(string poolname) + { + using (ServerManager serverManager = new ServerManager()) + { + return isExistWebAppPool(serverManager, poolname); + } + } + private static bool isExistWebApp(string sitename,string appname) + { + using (ServerManager serverManager = new ServerManager()) + { + return isExistWebApp(serverManager ,sitename, appname); + } + } + private static bool isExistWebSite(string sitename) + { + using (ServerManager serverManager = new ServerManager()) + { + return isExistWebSite(serverManager ,sitename); + } + } + #endregion private methods: Get information (with no ServerManager parameter) + #region private methods: Get information + public static string GetUserInfo(ServerManager sm, string poolname) + { + var pool = sm.ApplicationPools[poolname]; + string userinfo = ""; + if (pool != null) + { + if (pool.ProcessModel.IdentityType == ProcessModelIdentityType.SpecificUser) + { + userinfo = $"{pool.ProcessModel.UserName}({pool.ProcessModel.Password})"; + } + else + { + userinfo = $"{pool.ProcessModel.IdentityType}"; + } + } + return userinfo; + } + public static string GetPoolName(ServerManager serverManager,string sitename, string appname) + { + Site site = serverManager.Sites[sitename]; + Application application = site.Applications[Path.Combine("/", appname)]; + return application.ApplicationPoolName; + } + public static ApplicationPool GetPool(ServerManager serverManager, string sitename, string appname) + { + Site site = serverManager.Sites[sitename]; + Application application = site.Applications[Path.Combine("/",appname)]; + return serverManager.ApplicationPools[application.ApplicationPoolName]; + } + public static ApplicationPool GetPool(ServerManager serverManager, string poolname) + { + return serverManager.ApplicationPools[poolname]; + } + public static Site GetSite(ServerManager serverManager, string sitename) + { + return serverManager.Sites[sitename]; + } + private static bool isExistWebAppPool(ServerManager serverManager, string poolname) + { + try + { + var p = serverManager.ApplicationPools[poolname]; + if (p == null) { return false; } + return p.State != ObjectState.Unknown; + } + catch { return false; } + } + private static bool isExistWebApp(ServerManager serverManager, string sitename, string appname) + { + try + { + var an = Path.Combine(@"/",appname); + var app = serverManager.Sites[sitename].Applications[an]; + return app != null; + } + catch { return false; } + } + private static bool isExistWebSite(ServerManager serverManager, string sitename) + { + try + { + var s = serverManager.Sites[sitename]; + if (s == null) { return false; } + return s.State != ObjectState.Unknown; + } + catch { return false; } + } + #endregion private methods: Get information + #region private methods: Set information + private static void SetItemProperty(ServerManager serverManager, string poolname, string propertyname, string propertyvalue) + { + var pool = GetPool(serverManager, poolname); + pool.SetAttributeValue(propertyname, propertyvalue); //????????????????????????? + } + private static void CreateWebAppPool(ServerManager serverManager ,string poolname, ManagedPipelineMode plmode) + { + serverManager.ApplicationPools.Add(poolname); + ApplicationPool pool = serverManager.ApplicationPools[poolname]; + pool.ManagedPipelineMode = plmode; + } + private static void CreateWebApp(ServerManager serverManager, string appname, string poolname, string sitename, string poolphypath) + { + Site site = serverManager.Sites[sitename]; + Application application = site.Applications[Path.Combine("/",appname)]; + if (application != null) { site.Applications.Remove(application); } + application = site.Applications.Add(Path.Combine("/",appname), poolphypath); + application.ApplicationPoolName = poolname; + } + private static void CreateWebSite(ServerManager serverManager, string sitename, string poolname, string hostheader, string sitephypath, int siteport = 8000) + { + Site mySite = serverManager.Sites.Add(sitename, sitephypath, siteport); + mySite.Applications[0].ApplicationPoolName = poolname; + //mysite.Applications[0].VirtualDirectories[0].PhysicalPath = sitephypath; + mySite.ServerAutoStart = true; + } + private static void RemoveWebSite(ServerManager serverManager, string sitename) + { + Site mySite = serverManager.Sites[sitename]; + if (mySite != null) { serverManager.Sites.Remove(mySite); } + } + private static void RemoveWebAppPool(ServerManager serverManager,string poolname) + { + try + { + ApplicationPool pool = serverManager.ApplicationPools[poolname]; + serverManager.ApplicationPools.Remove(pool); + } + catch { } + } + private static void RemoveWebApp(ServerManager serverManager, string appname, string sitename) + { + Site site = serverManager.Sites[sitename]; + Application application = site.Applications[Path.Combine("/",appname)]; + site.Applications.Remove(application); + } + #endregion private methods: Set information (wit no ServerManager parameter) + #region private methods: Set information (with no ServerManager parameter) + private static void SetItemProperty(string poolname, string propertyname, string propertyvalue) + { + using (ServerManager serverManager = new ServerManager()) + { + SetItemProperty(serverManager, poolname, propertyname, propertyvalue); + CommitChanges(serverManager, poolname); + } + } + private static void CreateWebAppPool(string poolname, ManagedPipelineMode plmode) + { + using (ServerManager serverManager = new ServerManager()) + { + CreateWebAppPool(serverManager, poolname, plmode); + CommitChanges(serverManager, poolname); + } + } + private static void CreateWebApp(string appname, string poolname, string sitename, string poolphypath) + { + using (ServerManager serverManager = new ServerManager()) + { + CreateWebApp(serverManager, appname, poolname, sitename, poolphypath); + CommitChanges(serverManager, poolname); + } + } + private static void CreateWebSite(string sitename, string poolname, string hostheader, string sitephypath, int siteport = 8000) + { + using (ServerManager serverManager = new ServerManager()) + { + CreateWebSite(serverManager, sitename, poolname, hostheader, sitephypath, siteport); + CommitChanges(serverManager, poolname); + } + } + private static void RemoveWebSite(string sitename) + { + using (ServerManager serverManager = new ServerManager()) { RemoveWebSite(serverManager, sitename); } + } + private static void RemovePool(string poolname) + { + using (ServerManager sm = new ServerManager()) + { + RemoveWebAppPool(sm, poolname); + CommitChanges(sm, poolname); + } + } + private static void RemoveWebApp(string sitename, string appname) + { + using (ServerManager sm = new ServerManager()) + { + RemoveWebApp(sm, appname, sitename); + CommitChanges(sm, GetPoolName(sm,sitename, appname)); + } + } + #endregion private methods: Set information (with no ServerManager parameter) + + } + #region WebApplicationManagerXmlProcessor class + public class WebApplicationManagerXmlProcessor : XmlParser + { + #region constructor + public WebApplicationManagerXmlProcessor(string xmlcs, string basefolder, string lcid) : base(xmlcs, basefolder, lcid, null) + { + _webapplist = new List(); + var waxmllist = GetAllXElements(nameof(WebApplication.XmlStructure.WebApplication)); + if (waxmllist != null && waxmllist.Any()) + { + foreach (var waxml in waxmllist) { _webapplist.Add(new WebApplication(waxml)); } + } + } + #endregion constructor + #region properties + List _webapplist = null; + #endregion properties + + #region GetDefinitionList + public List GetDefinitionList() { return _webapplist; } + #endregion GetDefinitionList + } + #endregion WebApplicationManagerXmlProcessor class + #region WebApplication class + public class WebApplication : XmlLinqBase + { + #region properties from Xml definition + public string Xml_AppName; + public string Xml_Description; + public string Xml_PoolName; + public string Xml_SiteName; + public string Xml_AppPhysicalPath; + public ManagedPipelineMode Xml_PoolPipeLineMode; + public string Xml_SitePhysicalPath; + public string Xml_IdentityConfigFile; + public ProcessModelIdentityType Xml_PoolIdentitytype; + public string Xml_PoolUsername; + public string Xml_PoolPassword; + public bool Xml_RecreatePool; + public bool Xml_RecreateSite; + public bool Xml_RecreateApp; + public bool Xml_ForceRemovePool; + public bool Xml_ForceRemoveSite; + #endregion properties from Xml definition + + #region properties from environment + public Site Site; + public string SitePortList; + public string SiteState; + public ApplicationPool ApplicationPool; + public string PoolState; + public string PoolAccount; + public string AppImpersonateIdentity; + public Application Application; + public string ApplicationState; + public string ApplicationPath; + #endregion properties from environment + public WebApplication() { } + public WebApplication(XElement webappXml) + { + string ATTRIBUTEMANDATORY = nameof(XmlStructure.WebApplication) + " attribute is mandatory! Name: {0}"; + + Xml_AppName = webappXml.Attribute(XName.Get(nameof(XmlStructure.WebApplication.Attributes.Name)))?.Value; + if (string.IsNullOrWhiteSpace(Xml_AppName)) { throw new ApplicationException(string.Format(ATTRIBUTEMANDATORY, nameof(XmlStructure.WebApplication.Attributes.Name))); } + + Xml_Description = GetValue(nameof(XmlStructure.WebApplication.Attributes.Description), webappXml, Xml_AppName); + Xml_PoolName = GetValue(nameof(XmlStructure.WebApplication.Attributes.AppPool), webappXml, XmlStructure.WebApplication.Attributes.AppPool.Values.DEFAULT); + Xml_SiteName = GetValue(nameof(XmlStructure.WebApplication.Attributes.WebSite), webappXml, XmlStructure.WebApplication.Attributes.WebSite.Values.DEFAULT); + + Xml_AppPhysicalPath = webappXml.Attribute(XName.Get(nameof(XmlStructure.WebApplication.Attributes.InstallDir)))?.Value; + if (string.IsNullOrWhiteSpace(Xml_AppPhysicalPath)) { throw new ApplicationException(string.Format(ATTRIBUTEMANDATORY, nameof(XmlStructure.WebApplication.Attributes.InstallDir))); } + + string PoolPipeLineModeStr = webappXml.Attribute(XName.Get(nameof(XmlStructure.WebApplication.Attributes.AppPoolPipeLineMode)))?.Value; + if (string.IsNullOrWhiteSpace(PoolPipeLineModeStr)) { Xml_PoolPipeLineMode = ManagedPipelineMode.Integrated; } + else + { + Xml_PoolPipeLineMode = GetValue(nameof(XmlStructure.WebApplication.Attributes.AppPoolPipeLineMode), webappXml, ManagedPipelineMode.Integrated); + } + Xml_SitePhysicalPath = GetValue(nameof(XmlStructure.WebApplication.Attributes.SiteRootDir), webappXml, Xml_AppPhysicalPath); + Xml_IdentityConfigFile = webappXml.Attribute(XName.Get(nameof(XmlStructure.WebApplication.Attributes.IdentityConfigFile)))?.Value; + + Xml_PoolUsername = GetValue(nameof(XmlStructure.WebApplication.Attributes.AppPoolUsername), webappXml, nameof(ProcessModelIdentityType.ApplicationPoolIdentity)); + Xml_PoolPassword = GetValue(nameof(XmlStructure.WebApplication.Attributes.AppPoolPassword), webappXml, ""); + try { Xml_PoolIdentitytype = (ProcessModelIdentityType)Enum.Parse(typeof(ProcessModelIdentityType), Xml_PoolUsername); } + catch { Xml_PoolIdentitytype = ProcessModelIdentityType.SpecificUser; } + if (Xml_PoolIdentitytype != ProcessModelIdentityType.SpecificUser) { Xml_PoolPassword = ""; Xml_PoolUsername = ""; } + + Xml_RecreatePool = GetValue(nameof(XmlStructure.WebApplication.Attributes.RecreatePool), webappXml, false); + Xml_RecreateSite = GetValue(nameof(XmlStructure.WebApplication.Attributes.RecreateSite), webappXml, false); + Xml_RecreateApp = GetValue(nameof(XmlStructure.WebApplication.Attributes.RecreateApp), webappXml, true); + + Xml_ForceRemovePool = GetValue(nameof(XmlStructure.WebApplication.Attributes.ForceRemovePool), webappXml, false); + Xml_ForceRemoveSite = GetValue(nameof(XmlStructure.WebApplication.Attributes.ForceRemoveSite), webappXml, false); + } + public WebApplication(WebApplication wadef) + { + Xml_AppName = wadef.Xml_AppName; + Xml_Description = wadef.Xml_Description; + Xml_PoolName = wadef.Xml_PoolName; + Xml_SiteName = wadef.Xml_SiteName; + Xml_AppPhysicalPath = wadef.Xml_AppPhysicalPath; + Xml_SitePhysicalPath = wadef.Xml_SitePhysicalPath; + Xml_IdentityConfigFile = wadef.Xml_IdentityConfigFile; + Xml_PoolIdentitytype = wadef.Xml_PoolIdentitytype; + Xml_RecreateSite = wadef.Xml_RecreateSite; + Xml_RecreatePool = wadef.Xml_RecreatePool; + Xml_RecreateApp = wadef.Xml_RecreateApp; + Xml_ForceRemovePool = wadef.Xml_ForceRemovePool; + Xml_ForceRemoveSite = wadef.Xml_ForceRemoveSite; + } + #region XmlStructure + public static class XmlStructure + { + public static class WebApplication + { + public static class Attributes + { + public static class Name { } + public static class Description { } + public static class AppPool { public static class Values { public const string DEFAULT = "DefaultAppPool"; } } + public static class WebSite { public static class Values { public const string DEFAULT = "Default Web Site"; } } + public static class InstallDir { } + public static class SiteRootDir { } + public static class IdentityConfigFile { } + public static class AppPoolUsername { } + public static class AppPoolPassword { } + public static class AppPoolPipeLineMode { } + public static class RecreatePool { } + public static class RecreateSite { } + public static class RecreateApp { } + public static class ForceRemovePool { } + public static class ForceRemoveSite { } + } + } + } + #endregion XmlStructure + } + #endregion WebApplication class + #endregion WebApplicationManager +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/WindowsServiceManager.cs b/Vrh.Log4Pro.MaintenanceConsole/WindowsServiceManager.cs new file mode 100644 index 0000000..15f4e2b --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/WindowsServiceManager.cs @@ -0,0 +1,921 @@ +using System; +using System.IO; +using System.Configuration.Install; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Web.Administration; +using System.Management; +using System.Diagnostics; + +using Vrh.XmlProcessing; +using System.Xml.Linq; +using System.Text.RegularExpressions; + +namespace Vrh.Log4Pro.MaintenanceConsole +{ + #region WindowsServiceManager class + public static class WindowsServiceManager + { + #region Execute + public static object Execute(object o1 = null, object o2 = null) + { + string xmlcs = "file=Config.Xml;element=WindowsServices;"; + var config = new WindowsServiceManagerXmlProcessor(xmlcs, "", "hu-HU"); + + var menufunctions = new Menu("Manage Windows Services", "Select the management function!") + .AddMenuItem(new Menu.Item("WSR", "Register", Register)) + .AddMenuItem(new Menu.Item("WSU", "Unregister", Unregister)) + .AddMenuItem(new Menu.Item("WSS", "Start", Start)) + .AddMenuItem(new Menu.Item("WSX", "Stop", Stop)) + .AddMenuItem(new Menu.Item("WSK", "Kill", Kill)) + .AddMenuItem(new Menu.Item("WSP", "Purge", Purge)) + .AddMenuItem(new Menu.Item("WSA", "Set user account", SetUserAccount)) + .SetSelectionMode(Menu.SelectionMode.Single); + + while (true) + { + Menu.Selection sr; + + menufunctions.DisplayTitle(); + var menuservices = DisplayServices(config); + menufunctions.DisplayItems(3, 35); + sr = menufunctions.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { break; } + else if (sr.Result == Menu.SelectionResult.None) { continue; } + else if (sr.Result == Menu.SelectionResult.Error) { continue; } + menufunctions.SetParameters(sr,config); + menufunctions.Execute(sr.SelectedKeyList); + } + return null; + } + #endregion Execute + + public static Menu DisplayServices(WindowsServiceManagerXmlProcessor config,string prompt = null,bool silent=false) + { + List wsdefList = config.GetDefinitionList(); + var menuservices = new Menu("Windows services",prompt) + .SetMenuItemDisplayer(DisplayServiceInfo) + .SetSelectionMode(Menu.SelectionMode.Multi) + .SetHeaderWidth(4); + menuservices.ClearMenuItemList(); + foreach (var wsdef in wsdefList) + { + var w = CollectWindowsServiceInfo(wsdef); + menuservices.AddMenuItem(new Menu.Item(null, null, null, w)); + } + if (!silent) { menuservices.DisplayItems(1); } + return menuservices; + } + + #region private method: DisplayServiceInfo + private static object DisplayServiceInfo(object wsobj, int lineix) + { + WindowsService ws = wsobj as WindowsService; + if (lineix == 0) + { + ColorConsole.Write($"{ws.Description}", ConsoleColor.Black, ConsoleColor.White); + ColorConsole.WriteLine($"{ws.DisplayName}", bracket: "()"); + return ws.DisplayName+ws.Description; + } + else if (lineix == 1) + { + ColorConsole.Write($"Win service:"); + ColorConsole.Write($"{ws.Name}", ConsoleColor.Cyan); + var fc2 = ws.Status.Contains("Unregistered") ? ConsoleColor.Red : ConsoleColor.Green; + ColorConsole.Write($"("); + ColorConsole.Write($"{ws.Status}", fc2); + var fc0 = + ws.State.Contains("Started") ? ConsoleColor.Green + : ws.State.Contains("Stopped") ? ConsoleColor.Red + : ws.State.Contains("Unregistered") ? ConsoleColor.Red + : ConsoleColor.Yellow; + ColorConsole.Write($" / "); + ColorConsole.Write($"{ws.State}", fc0); + ColorConsole.Write($")"); + ColorConsole.Write($", StartMode:"); + ColorConsole.Write($"{ws.StartMode}", ConsoleColor.Cyan); + if(ws.Status=="OK" && ws.State != nameof(ObjectState.Stopped) && ws.State != nameof(ObjectState.Unknown)) + { + ColorConsole.Write($", Priority:"); + ColorConsole.Write($"{ws.PriorityClass}", ConsoleColor.White); + ColorConsole.Write($", ProcessId:"); + ColorConsole.Write($"{ws.ProcessId}", ConsoleColor.White); + } + ColorConsole.WriteLine(); + return ws.Name; + } + else if (lineix == 2) + { + if (string.IsNullOrEmpty(ws.PathName)) { return ""; } + var cmdarray = CommandLineParser.SplitArgs(ws.PathName).ToArray(); + ColorConsole.Write($"Start command:"); + ColorConsole.WriteLine($"{cmdarray[0]}", ConsoleColor.White); + return ws.PathName; + } + else if (lineix == 3) + { + if (string.IsNullOrEmpty(ws.PathName)) { return ""; } + var cmdparams = CommandLineParser.SplitArgs(ws.PathName).Skip(1).ToArray(); + if (cmdparams.Length==0) { return ""; } + var cmdparamsstr = string.Join("][", cmdparams); + ColorConsole.Write($"Start arguments:"); + ColorConsole.WriteLine($"[{cmdparamsstr}]", ConsoleColor.White); + return cmdparamsstr; + } + else if (lineix == 4) + { + ColorConsole.Write($"User:"); + ColorConsole.WriteLine($"{ws.StartName}", ConsoleColor.White); + return ws.StartName; + } + else if (lineix == 5) + { + ColorConsole.WriteLine(); + return " "; + } + return null; + } + #endregion private method: DisplayServiceInfo + + #region private CollectWindowsServiceInfo + private static WindowsService CollectWindowsServiceInfo(WindowsService ws) + { + using (var wmiService = WindowsServiceManagerCore.GetServiceObject(ws.Name)) + { + using (var service = WindowsServiceManagerCore.GetServiceController(ws.Name)) + { + var wmiserviceproblem = false; + try { wmiService.Get();}catch { wmiserviceproblem = true; } + if (wmiService == null || service == null || wmiserviceproblem) + { + ws.DisplayName = ws.Xml_DisplayName; + ws.Description = ws.Xml_Description; + ws.ThisDependsOn = ws.Xml_Dependon; + ws.ServicesDependOnThis = new string[] { "???" }; + ws.PathName = $"\"{ws.Xml_Exe}\" {ws.Xml_Arguments}"; + ws.StartMode = ws.Xml_StartMode; + ws.State = "Unregistered"; + ws.Status = "Unregistered"; + if (ws.Xml_IdentityType== ServiceAccount.User) { ws.StartName = ws.Xml_Username + $"({ws.Xml_Password})"; } + else { ws.StartName = ws.Xml_IdentityType.ToString(); } + ws.ProcessId = 0; + ws.PriorityClass = ws.Xml_Priority; + } + else + { + ws.DisplayName = (string)wmiService[nameof(WindowsService.DisplayName)]; + ws.Description = (string)wmiService[nameof(WindowsService.Description)]; + if (string.IsNullOrEmpty(ws.Description)) { ws.Description = ws.DisplayName; } + if (string.IsNullOrEmpty(ws.DisplayName)) { ws.DisplayName = ws.Description; } + ws.ServicesDependOnThis = service.DependentServices.Select(s => s.ServiceName).ToArray(); + try { ws.ThisDependsOn = service.ServicesDependedOn.Select(s => s.ServiceName).ToArray(); } catch { ws.ThisDependsOn = new string[] { "???" }; } + ws.PathName = (string)wmiService[nameof(WindowsService.PathName)];if (ws.PathName == null) { ws.PathName = ""; } + var sm = (string)wmiService[nameof(WindowsService.StartMode)]; + ws.StartMode = + sm == "Auto" ? ServiceStartMode.Automatic + : sm == "Auto" ? ServiceStartMode.Manual + : sm == "Auto" ? ServiceStartMode.Disabled + : sm == "Auto" ? ServiceStartMode.Boot + : sm == "Auto" ? ServiceStartMode.System + : ServiceStartMode.Manual; + ws.State = (string)wmiService[nameof(WindowsService.State)]; + ws.Status = (string)wmiService[nameof(WindowsService.Status)]; + ws.StartName = (string)wmiService[nameof(WindowsService.StartName)]; + ws.ProcessId = 0; + ws.PriorityClass = "-"; + if (ws.State != nameof(ObjectState.Stopped)) + { + ws.ProcessId = Convert.ToInt32(wmiService[nameof(WindowsService.ProcessId)]); + ws.PriorityClass = Process.GetProcessById(ws.ProcessId).PriorityClass.ToString(); + } + } + } + } + return ws; + } + #endregion private CollectWindowsServiceInfo + + #region First level Executors with UI + private static object Purge(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + while (true) + { + ColorConsole.Write($"Enter servicename mask (% and ? are wildchars)!",ConsoleColor.Yellow); + var mask = ColorConsole.ReadLine($" ---> ", ConsoleColor.Yellow).ToUpper(); + if (mask == "EX") { return o; } + else if (mask == "") { continue; } + var sl = WindowsServiceManagerCore.GetServiceControllers(mask); + if (sl.Length > 10) { sl = sl.Take(10).ToArray(); } + + var menuservices = new Menu("Windows services", "Select the windows service(es) to delete (max 10 in one run)!") + .SetSelectionMode(Menu.SelectionMode.Multi) + .SetHeaderWidth(4); + + for (var i = 0; i < sl.Length; i++) + { + menuservices.AddMenuItem(new Menu.Item(null, $"{sl[i].ServiceName} ({sl[i].DisplayName})", null, sl[i].ServiceName)); + } + menuservices.DisplayItems(1); + var ms = menuservices.Select(); + if (ms.Result == Menu.SelectionResult.None) { continue; } + else if (ms.Result == Menu.SelectionResult.Error) { continue; } + else if (ms.Result == Menu.SelectionResult.Exit) { break; } + else + { + foreach (var p in ms.SelectedParameterList) + { + ColorConsole.Write($"Enter CONFIRM to delete service ", ConsoleColor.Yellow); + ColorConsole.Write($"{p}", ConsoleColor.White,bracket:"''"); + ColorConsole.Write($"!", ConsoleColor.Yellow); + var confirmation = ColorConsole.ReadLine(prefix: " ---> ").ToUpper(); + if (confirmation == "EX") { return o; } + else if (confirmation == "") + { + ColorConsole.WriteLine($"Service '{p}' skipped!", ConsoleColor.Green); + continue; + } + else if (confirmation == "CONFIRM") + { + WindowsServiceManagerCore.Unregister((string)p); + ColorConsole.WriteLine($"Service '{p}' deleted!", ConsoleColor.Green); + } + } + } + } + return o; + } + private static object Kill(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(Kill)}'!", silent:true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + var success = WindowsServiceManagerCore.Kill(ws.Name); + ColorConsole.WriteLine($"Service killed. Name:{ws.Name}", ConsoleColor.Green); + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); + } + } + return o; + } + private static object Register(object parameter, object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(Register)}'!", silent: true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + var success = WindowsServiceManagerCore.Register(ws); + ColorConsole.WriteLine($"Service registered. Name:{ws.Name}", ConsoleColor.Green); + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); + } + } + return o; + } + private static object Unregister(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(Unregister)}'!", silent: true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + var success = WindowsServiceManagerCore.Unregister(ws); + ColorConsole.WriteLine($"Service unregistered. Name:{ws.Name}", ConsoleColor.Green); + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); + } + } + return o; + } + private static object Start(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(Start)}'!", silent: true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + var success = WindowsServiceManagerCore.Start(ws.Name, ws.Xml_StartTimeout); + ColorConsole.WriteLine($"Service started. Name:{ws.Name}", ConsoleColor.Green); + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); + } + } + return o; + } + private static object Stop(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(Kill)}'!", silent: true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + var success = WindowsServiceManagerCore.Stop(ws.Name, ws.Xml_StopTimeout); + ColorConsole.WriteLine($"Service stopped. Name:{ws.Name}", ConsoleColor.Green); + } + catch (Exception ex) + { + ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); + } + } + return o; + } + private static object SetUserAccount(object parameter,object o) + { + var config = parameter as WindowsServiceManagerXmlProcessor; + var menuservices = DisplayServices(config, $"Select the windows service(es) to manage with function '{nameof(SetUserAccount)}'!", silent: true); + + Menu.Selection sr = menuservices.Select(); + if (sr.Result == Menu.SelectionResult.Exit) { return o; } + else if (sr.Result == Menu.SelectionResult.None) { return o; } + else if (sr.Result == Menu.SelectionResult.Error) { return o; } + else if (sr.Result == Menu.SelectionResult.Ok) { } + else { } + + string username = null, password = null; + foreach (var p in sr.SelectedParameterList) + { + WindowsService ws = p as WindowsService; + try + { + using (var wmiService = WindowsServiceManagerCore.GetServiceObject(ws.Name)) + { + if (username==null) + { + string olduserinfo = WindowsServiceManagerCore.GetUserInfo(wmiService); + ColorConsole.WriteLine($"Current user info: {olduserinfo}"); + ColorConsole.WriteLine("Enter username then password. [Enter]=save.EX=exit"); + ColorConsole.WriteLine("Username is in the form of 'domainname\\username'"); + ColorConsole.WriteLine("Special usernames are: (Empty=API) "); + + ColorConsole.WriteLine($"LSE", ConsoleColor.Yellow, bracket: "()", prefix: " ", suffix: $" {nameof(ServiceAccount.LocalService)}"); + ColorConsole.WriteLine($"LSY", ConsoleColor.Yellow, bracket: "()", prefix: " ", suffix: $" {nameof(ServiceAccount.LocalSystem)}"); + ColorConsole.WriteLine($"NSE", ConsoleColor.Yellow, bracket: "()", prefix: " ", suffix: $" {nameof(ServiceAccount.NetworkService)}"); + + ColorConsole.WriteLine(); + ColorConsole.WriteLine($"EX", ConsoleColor.Red, bracket: "()", prefix: " ", suffix: " Exit"); + + username = ColorConsole.ReadLine("username -->", ConsoleColor.Yellow, bracket: "[]"); + if (username == "EX") { return null; } + else if (string.IsNullOrEmpty(username)) { username = nameof(ServiceAccount.LocalSystem); } + else if (username.ToUpper() == "LSE" || username == nameof(ServiceAccount.LocalService)) { username = nameof(ServiceAccount.LocalService); } + else if (username.ToUpper() == "LSY" || username == nameof(ServiceAccount.LocalSystem)) { username = nameof(ServiceAccount.LocalSystem); } + else if (username.ToUpper() == "NSE" || username == nameof(ServiceAccount.NetworkService)) { username = nameof(ServiceAccount.NetworkService); } + else + { + password = ColorConsole.ReadLine("password -->", ConsoleColor.Yellow, bracket: "[]"); + if (password == "EX") { return null; } + } + } + if (WindowsServiceManagerCore.SetUserAccount(wmiService, username, password)) { ColorConsole.WriteLine($"Service user account changed. Name:{ws.Name}", ConsoleColor.Green); } + else { ColorConsole.WriteLine($"Service user account change FAILED! Name:{ws.Name}", ConsoleColor.Red); } + } + } + catch (Exception ex) { ColorConsole.WriteLine(ex.Message, ConsoleColor.Red); } + } + return o; + } + #endregion First level Executors with UI + } + #endregion WindowsServiceManager class + + #region WindowsServiceManagerCore class + public static class WindowsServiceManagerCore + { + public static string GetUserInfo(ManagementObject wmiService) + { + return (string)wmiService[nameof(WindowsService.StartName)]; + } + public static ManagementObject GetServiceObject(string servicename) { return new ManagementObject("Win32_Service.Name='" + servicename + "'"); } + public static ServiceController GetServiceController(string servicename) { return new ServiceController(servicename); } + public static ServiceController[] GetServiceControllers(string servicenamemask) + { + var servicenameregex = @"^" + Regex.Escape(servicenamemask).Replace("%",".*").Replace(@"\?",".")+ @"$"; + + var ServiceControllerList = new List(); + var allservicecontrollers = ServiceController.GetServices(); + foreach (var sc in allservicecontrollers) + { + try + { + if (Regex.Match(sc.ServiceName, servicenameregex).Success) { ServiceControllerList.Add(sc); } + } + catch { ColorConsole.WriteLine($"Error in mask '{servicenamemask}'!",ConsoleColor.Red); return null; } + } + return ServiceControllerList.ToArray(); + } + public static bool SetUserAccount(ManagementObject service, string username, string password) + { + object[] accountParams = new object[11]; + accountParams[6] = username; + accountParams[7] = password; + uint returnCode = (uint)service.InvokeMethod("Change", accountParams); + if (returnCode == 0) { return true; } + else { return false; } + } + + public static bool Kill(string sname) + { + using (var wmiService = WindowsServiceManagerCore.GetServiceObject(sname)) + { + string sstate = (string)wmiService[nameof(WindowsService.State)]; + if (sstate != nameof(ObjectState.Stopped)) + { + int pid = Convert.ToInt32(wmiService[nameof(WindowsService.ProcessId)]); + KillProcessAndChildren(pid); + } + return true; + } + } + + /// + /// Kill a process, and all of its children, grandchildren, etc. + /// + /// Process ID. + private static void KillProcessAndChildren(int pid) + { + // Cannot close 'system idle process'. + if (pid == 0) { return;} + ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid); + ManagementObjectCollection moc = searcher.Get(); + foreach (ManagementObject mo in moc) { KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"])); } + try { Process proc = Process.GetProcessById(pid); proc.Kill(); } + catch (ArgumentException) { } // Process already exited. + } + public static bool Register(WindowsService ws) + { + // sc.exe create "ServiceName" binpath= "c:\windows\system32\NewServ.exe" displayname= "Service display name" obj= "username" password="password" start= auto depend= "sname1/sname2/snmae3" + // sc.exe \\myserver create NewService binpath= c:\windows\system32\NewServ.exe + // sc.exe create NewService binpath= c:\windows\system32\NewServ.exe type= share start= auto depend= +TDI NetBIOS + // + // sc.exe [] create [] + // [type= {own | share | kernel | filesys | rec | interact type= {own | share}}] + // [start= {boot | system | auto | demand | disabled | delayed-auto}] + // [error= {normal | severe | critical | ignore}] + // [binpath= ] + // [group= ] + // [tag= {yes | no}] + // [depend= ] + // [obj= { | }] + // [displayname= ] + // [password= ] + + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = true; + startInfo.FileName = "sc.exe"; + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + List argumentlist = new List(); + argumentlist.Add($"create"); argumentlist.Add(ws.Name.Quote()); + argumentlist.Add("displayname="); argumentlist.Add(ws.DisplayName.Quote()); + if (ws.Xml_IdentityType == ServiceAccount.User) + { + argumentlist.Add($"obj="); argumentlist.Add(ws.Xml_Username.Quote()); + argumentlist.Add("password="); argumentlist.Add(ws.Xml_Password.Quote()); + } + else + { + argumentlist.Add($"obj="); argumentlist.Add(ws.Xml_IdentityType.ToString().Quote()); + } + var dependonparametert = String.Join("/", ws.Xml_Dependon); + argumentlist.Add($"depend="); argumentlist.Add(dependonparametert.Quote()); + + var startparameter = + ws.Xml_StartMode == ServiceStartMode.Automatic ? "auto" + : ws.Xml_StartMode == ServiceStartMode.Manual ? "demand" + : ws.Xml_StartMode == ServiceStartMode.Disabled ? "disabled" + : ws.Xml_StartMode == ServiceStartMode.Boot ? "boot" + : "system"; + argumentlist.Add($"start="); argumentlist.Add(startparameter.Quote()); + + var registerarguments = ""; + switch (ws.Xml_RegistrationMode) + { + case nameof(WindowsService.XmlStructure.WindowsService.Attributes.RegistrationMode.Values.standard): + { + registerarguments = ws.Xml_Arguments; + } + break; + case nameof(WindowsService.XmlStructure.WindowsService.Attributes.RegistrationMode.Values.topshelf): + { + //"C:\Log4ProIS\VRH.iLogger\VRH.Common.iLogger.Topshelf.exe" -displayname "VRH iLogger for Log4ProIS" -servicename "VRH iLogger for Log4ProIS" -APPCONFIG appconfigILOGGER.config + registerarguments = "-displayname " + ws.Xml_DisplayName.Quote() + " -servicename " + ws.Name.Quote() + " " + ws.Xml_Arguments; + } + break; + case nameof(WindowsService.XmlStructure.WindowsService.Attributes.RegistrationMode.Values.redis): + { + //"C:\Log4ProIS\VRH.iLogger\VRH.Common.iLogger.Topshelf.exe" -displayname "VRH iLogger for Log4ProIS" -servicename "VRH iLogger for Log4ProIS" -APPCONFIG appconfigILOGGER.config + registerarguments = "--service-run " + ws.Xml_Arguments.Quote() + " --service-name " + ws.Name.Quote(); + } + break; + } + var binpathparameter = Path.Combine(ws.Xml_InstallDir, ws.Xml_Exe).Quote() + " " + registerarguments; + argumentlist.Add($"binpath="); argumentlist.Add(binpathparameter.Quote()); + + startInfo.Arguments = String.Join(" ", argumentlist); + + ColorConsole.WriteLine(startInfo.FileName + " " + startInfo.Arguments); + try + { + using (Process exeProcess = Process.Start(startInfo)) { exeProcess.WaitForExit(); } + } + catch + { + // Log error. + } + return true; + } + + public static bool Unregister(WindowsService ws) + { + return Unregister(ws.Name, ws.Xml_StopTimeout); + } + public static bool Unregister(string sname,int stoptimeout=10000) + { + using (var s = GetServiceController(sname)) { Stop(s.ServiceName, stoptimeout); } + + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = true; + startInfo.FileName = "sc.exe"; + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + + List argumentlist = new List(); + argumentlist.Add($"delete"); argumentlist.Add(sname.Quote()); + startInfo.Arguments = String.Join(" ", argumentlist); + + ColorConsole.WriteLine(startInfo.FileName + " " + startInfo.Arguments); + try + { + // Start the process with the info we specified. + // Call WaitForExit and then the using statement will close. + using (Process exeProcess = Process.Start(startInfo)) { exeProcess.WaitForExit(); } + } + catch + { + // Log error. + } + return true; + // sc.exe delete "ServiceName" + // sc.exe \\myserver delete NewService + // + // sc.exe + // [] + // delete + // [] + } + + public static bool Start(string sname, int starttimeout) + { + using (var service = WindowsServiceManagerCore.GetServiceController(sname)) + { + if (service.ServicesDependedOn.Any()) + { + bool result = true; + foreach (var sn in service.ServicesDependedOn) { result = Start(sn.ServiceName, starttimeout); } + } + var sl = new List() { ServiceControllerStatus.Running, ServiceControllerStatus.Stopped, ServiceControllerStatus.Paused, }; + WaitForStatus(service, sl, starttimeout); + if (service.Status.Equals(ServiceControllerStatus.Stopped)) { service.Start(); } + else if (service.Status.Equals(ServiceControllerStatus.Running)) {; } + else if (service.Status.Equals(ServiceControllerStatus.Paused)) { service.Continue(); } + else { throw new Exception($"Start service {service.ServiceName} failed because of incorrect service status!"); } + service.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, 0, starttimeout)); + if (!service.Status.Equals(ServiceControllerStatus.Running)) { throw new Exception($"Start service {service.ServiceName} failed!"); } + return true; + } + } + public static bool Stop(string sname, int stoptimeout) + { + using (var service = WindowsServiceManagerCore.GetServiceController(sname)) + { + if (service.DependentServices.Any()) + { + bool result = true; + foreach (var s in service.ServicesDependedOn) + { + string sn; + try + { + sn = s.ServiceName; + result = Stop(s.ServiceName, stoptimeout); + } + catch { } + } + } + var sl = new List() { ServiceControllerStatus.Running, ServiceControllerStatus.Stopped, ServiceControllerStatus.Paused, }; + WaitForStatus(service, sl, stoptimeout); + if (service.Status.Equals(ServiceControllerStatus.Running)) { service.Stop(); } + else if (service.Status.Equals(ServiceControllerStatus.Paused)) { service.Stop(); } + else if (service.Status.Equals(ServiceControllerStatus.Stopped)) {; } + else + { + try { Kill(sname); } catch { } + if (!service.Status.Equals(ServiceControllerStatus.Stopped)) + { + throw new Exception($"Stop service {service.ServiceName} failed because of incorrect service status!"); + } + } + service.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, 0, stoptimeout)); + if (!service.Status.Equals(ServiceControllerStatus.Stopped)) { throw new Exception($"Stop service {service.ServiceName} failed!"); } + return true; + } + } + private async static void WaitForStatus(ServiceController srvCtl, List sl, int toms) + { + CancellationTokenSource cts = new CancellationTokenSource(); + var ct = cts.Token; + List tasks = new List(); + var ts = new TimeSpan(0, 0, 0, 0, toms); + foreach (var s in sl) { if (srvCtl.Status.Equals(s)) { return; } }; + foreach (var s in sl) { tasks.Add(Task.Run(new Action(() => { try { srvCtl.WaitForStatus(s, ts); } catch { } }), ct)); }; + // When any of the tasks completes, it means the service reached on of the statuses you were tracking + var t = await Task.WhenAny(tasks); + // To cancel the rest of the tasks that are still waiting + cts.Cancel(); + } + + } + #endregion WindowsServiceManagerCore class + #region WindowsServiceManagerXmlProcessor class + public class WindowsServiceManagerXmlProcessor : XmlParser + { + List _winservicelist; + List _winservicelistinstartorder; + List _winservicelistinstoporder; + #region constructor + public WindowsServiceManagerXmlProcessor(string xmlcs, string basefolder, string lcid) : base(xmlcs, basefolder, lcid, null) + { + _winservicelist = new List(); + var wsxmllist = GetAllXElements(nameof(WindowsService.XmlStructure.WindowsService)); + if (wsxmllist != null && wsxmllist.Any()) + { + foreach (var wsxml in wsxmllist) { var ws = new WindowsService(wsxml); if (ws.Valid) { _winservicelist.Add(ws); } } + } + _winservicelistinstartorder = ProduceDefinitionListInStartOrder(); + _winservicelistinstoporder = ProduceDefinitionListInStartOrder(); _winservicelistinstoporder.Reverse(); + } + #endregion constructor + #region GetDefinitionList + public List GetDefinitionList() { return _winservicelist; } + public List GetDefinitionListInStartOrder() { return _winservicelistinstartorder; } + public List GetDefinitionListInStopOrder() { return _winservicelistinstoporder; } + private List ProduceDefinitionListInStartOrder() + { + List listinstartorder = new List(); + bool ready = false;//akkor lesz false, ha már minden + while (!ready) + { + ready = true; + foreach (var s in _winservicelist) + { + var namelistinstartorder = listinstartorder.Select(x => x.Name); + if (namelistinstartorder.Contains(s.Name)) { continue; }//ez a szerviz már bekerült a startorder listába + ready = false; + var dl = s.Xml_Dependon; + if (dl.Length == 0) { listinstartorder.Add(s); continue; }//ha nem függ senkitől, betesszük a startorder listába + foreach (var dli in dl) + { + if (string.IsNullOrWhiteSpace(dli)) { continue; } + if (!namelistinstartorder.Contains(dli)) { continue; }//van egy olyan függősége, ami nincs benne a startorder listában + } + listinstartorder.Add(s); break;//minden függősége már benne van a start order listában, így ez is bekerül oda + } + } + return listinstartorder; + } + #endregion GetDefinitionList + } + #endregion WindowsServiceManagerXmlProcessor class + #region WindowsService class + public class WindowsService : XmlLinqBase + { + #region fields + public string Name; + public bool Valid = true; + public string DisplayName; + public string Description; + public string PathName; + public string[] ThisDependsOn; + public string[] ServicesDependOnThis; + public int ProcessId; + public ServiceStartMode StartMode; + public string State; + public string Status; + public string StartName; + public string PriorityClass; + + public string Xml_DisplayName; + public string Xml_Description; + public string Xml_Exe; + public string Xml_InstallDir; + public ServiceStartMode Xml_StartMode; + public string Xml_Priority; + public string Xml_Arguments; + public string[] Xml_Dependon; + public string Xml_RegistrationMode; + public ServiceAccount Xml_IdentityType; + public string Xml_Username; + public string Xml_Password; + public int Xml_StartTimeout; + public int Xml_StopTimeout; + #endregion fields + + #region basic constructor + public WindowsService() { } + #endregion basic constructor + #region xml constructor + public WindowsService(XElement winservicexml) + { + string ATTRIBUTEMANDATORY = nameof(XmlStructure.WindowsService) + " attribute is mandatory! Name: {0}"; + Name = winservicexml.Attribute(XName.Get(nameof(XmlStructure.WindowsService.Attributes.Name)))?.Value; + if (!ValidServiceName(Name)) + { + Valid = false; + return; + //throw new ApplicationException(string.Format(ATTRIBUTEMANDATORY, nameof(XmlStructure.WindowsService.Attributes.Name))); + } + Xml_DisplayName = GetValue(nameof(XmlStructure.WindowsService.Attributes.DisplayName), winservicexml, Name); + Xml_Description = GetValue(nameof(XmlStructure.WindowsService.Attributes.Description), winservicexml, Xml_DisplayName); + Xml_Exe = winservicexml.Attribute(XName.Get(nameof(XmlStructure.WindowsService.Attributes.Exe)))?.Value; + if (string.IsNullOrWhiteSpace(Xml_Exe)) { throw new ApplicationException(string.Format(ATTRIBUTEMANDATORY, nameof(XmlStructure.WindowsService.Attributes.Exe))); } + Xml_InstallDir = winservicexml.Attribute(XName.Get(nameof(XmlStructure.WindowsService.Attributes.InstallDir)))?.Value; + if (string.IsNullOrWhiteSpace(Xml_Exe)) { throw new ApplicationException(string.Format(ATTRIBUTEMANDATORY, nameof(XmlStructure.WindowsService.Attributes.InstallDir))); } + + var startModestr = GetValue(nameof(XmlStructure.WindowsService.Attributes.StartMode), winservicexml, ""); + if (string.IsNullOrWhiteSpace(startModestr)) { startModestr = nameof(ServiceStartMode.Manual); } + Xml_StartMode = GetValue(startModestr, ServiceStartMode.Manual); + + Xml_Priority = GetValue(nameof(XmlStructure.WindowsService.Attributes.Priority), winservicexml, XmlStructure.WindowsService.Attributes.Priority.Values.DEFAULT); + Xml_Arguments = GetValue(nameof(XmlStructure.WindowsService.Attributes.Arguments), winservicexml, ""); + + var XmlDependon = new List(); + string[] dependonarray = GetValue(nameof(XmlStructure.WindowsService.Attributes.DependOn), winservicexml, "").Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var ds in dependonarray) + { + if (ValidServiceName(ds)) { XmlDependon.Add(ds); } + } + Xml_Dependon = XmlDependon.ToArray(); + + Xml_RegistrationMode = GetValue(nameof(XmlStructure.WindowsService.Attributes.RegistrationMode), winservicexml, XmlStructure.WindowsService.Attributes.RegistrationMode.Values.DEFAULT); + Xml_RegistrationMode = Xml_RegistrationMode.Replace('-', '_'); + try + { + Xml_IdentityType = GetValue(nameof(XmlStructure.WindowsService.Attributes.UserName), winservicexml, ServiceAccount.LocalSystem); + Xml_Username = null; + Xml_Password = null; + } + catch + { + Xml_IdentityType = ServiceAccount.User; + Xml_Username = GetValue(nameof(XmlStructure.WindowsService.Attributes.UserName), winservicexml, XmlStructure.WindowsService.Attributes.UserName.Values.DEFAULT); + Xml_Password = GetValue(nameof(XmlStructure.WindowsService.Attributes.Password), winservicexml, ""); + } + Xml_StartTimeout = GetValue(nameof(XmlStructure.WindowsService.Attributes.StartTimeout), winservicexml, XmlStructure.WindowsService.Attributes.StartTimeout.Values.DEFAULT); + Xml_StopTimeout = GetValue(nameof(XmlStructure.WindowsService.Attributes.StopTimeout), winservicexml, XmlStructure.WindowsService.Attributes.StopTimeout.Values.DEFAULT); + } + private bool ValidServiceName(string sn) { return !string.IsNullOrWhiteSpace(Name) && sn[0] != '@'; } + #endregion xml constructor + #region cloner constructor + public WindowsService(WindowsService ws) + { + Name = ws.Name; + Valid = ws.Valid; + Xml_DisplayName = ws.Xml_DisplayName; + Xml_Description = ws.Xml_Description; + Xml_Exe = ws.Xml_Exe; + Xml_InstallDir = ws.Xml_InstallDir; + Xml_StartMode = ws.Xml_StartMode; + Xml_Priority = ws.Xml_Priority; + Xml_Arguments = ws.Xml_Arguments; + Xml_Dependon = ws.Xml_Dependon; + Xml_RegistrationMode = ws.Xml_RegistrationMode; + Xml_IdentityType = ws.Xml_IdentityType; + Xml_Username = ws.Xml_Username; + Xml_Password = ws.Xml_Password; + Xml_StartTimeout = ws.Xml_StartTimeout; + Xml_StopTimeout = ws.Xml_StopTimeout; + } + #endregion cloner constructor + #region XmlStructure + public static class XmlStructure + { + public static class WindowsService + { + public static class Attributes + { + public static class Name { } + public static class DisplayName { } + public static class Description { } + public static class Exe { } + public static class Priority + { + public static class Values + { + public static string DEFAULT = "20"; + } + } + public static class StartMode { } + public static class InstallDir { } + public static class DependOn { } + public static class UserName + { + public static class Values + { + public static string DEFAULT = nameof(ProcessModelIdentityType.LocalService); + } + } + public static class Password { } + public static class Arguments { } + public static class StartTimeout + { + public static class Values + { + public static int DEFAULT = 10000; + } + } + public static class StopTimeout + { + public static class Values + { + public static int DEFAULT = 10000; + } + } + public static class RegistrationMode + { + public static class Values + { + public static string DEFAULT = nameof(standard); + public class standard { }; + public class redis { }; + public class topshelf { }; + } + } + } + } + } + #endregion XmlStructure + } + #endregion WindowsService class + +} diff --git a/Vrh.Log4Pro.MaintenanceConsole/XmlParser.xml b/Vrh.Log4Pro.MaintenanceConsole/XmlParser.xml new file mode 100644 index 0000000..f0a0a22 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/XmlParser.xml @@ -0,0 +1,140 @@ + + + + 127.0.0.1 + @SINGLESERVERIP@ + WebALM + + @SINGLESERVERIP@ + WebPack + + @SINGLESERVERIP@ + WebAndon + + + + c:\Log4ProIS + c:\Log4ProIS\CONFIG + @APPROOTPATH@\VRH.Log4Pro.ACALM + + @APPROOTPATH@\wwwroot_Log4ProIS + @WEBAPPROOT@\App_Data + @WEBAPPROOT@\App_Data\ALM + @WEBAPPROOT@\App_Data\CP + @WEBAPPROOT@\App_Data\ANDON + @WEBAPPROOT@\App_Data\IS-TRM + + @APPROOTPATH@\wwwroot_WebPack + @WEBAPPROOT_WEBPACK@\App_Data + + @APPROOTPATH@\wwwroot_WebAndon + @WEBAPPROOT_ANDON@\App_Data + + @APPROOTPATH@\wwwroot_WebALM + @WEBAPPROOT_ALM@\App_Data + + @APPROOTPATH@\VRH.Log4Pro.ASEDC-DCWF_ALM\ + @APPROOTPATH@\VRH.Log4Pro.ASEDC-DCWF_CP\ + @APPROOTPATH@\VRH.Log4Pro.ASEDC-ASEMON\ + + localhost + c:\Log4ProISBackups + + @ALMBACKUPS@\Documents + + + @APPDATA@\WebMonitor\XmlParserButtonDefinitions.xml + + false + SITE_SLNOTEBOOKDELL + + + + true + + true + Menu.Xml + Accordion + max-width:40vmax; + max-width:80vmax; + max-width:120vmax; + max-width:90vmax;margin 100; padding 10; + max-width:50vmax;font-size:larger;color:white; + max-width:70vmax;font-size:larger;color:white; + max-width:100vmax;font-size:larger;color:white; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Vrh.Log4Pro.MaintenanceConsole/packages.config b/Vrh.Log4Pro.MaintenanceConsole/packages.config new file mode 100644 index 0000000..9b561d7 --- /dev/null +++ b/Vrh.Log4Pro.MaintenanceConsole/packages.config @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WindowsService1/App.config b/WindowsService1/App.config new file mode 100644 index 0000000..b50c74f --- /dev/null +++ b/WindowsService1/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/WindowsService1/Program.cs b/WindowsService1/Program.cs new file mode 100644 index 0000000..feed01d --- /dev/null +++ b/WindowsService1/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace WindowsService1 +{ + static class Program + { + /// + /// The main entry point for the application. + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new Service1() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/WindowsService1/ProjectInstaller.Designer.cs b/WindowsService1/ProjectInstaller.Designer.cs new file mode 100644 index 0000000..6c80cff --- /dev/null +++ b/WindowsService1/ProjectInstaller.Designer.cs @@ -0,0 +1,59 @@ + +namespace WindowsService1 +{ + partial class ProjectInstaller + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); + this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); + // + // serviceProcessInstaller1 + // + this.serviceProcessInstaller1.Password = null; + this.serviceProcessInstaller1.Username = null; + //this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.; + // + // serviceInstaller1 + // + this.serviceInstaller1.ServiceName = "Service1"; + //this.serviceInstaller1. = "Service1"; + // + // ProjectInstaller + // + this.Installers.AddRange(new System.Configuration.Install.Installer[] { + this.serviceProcessInstaller1, + this.serviceInstaller1}); + + } + + #endregion + + private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; + private System.ServiceProcess.ServiceInstaller serviceInstaller1; + } +} \ No newline at end of file diff --git a/WindowsService1/ProjectInstaller.cs b/WindowsService1/ProjectInstaller.cs new file mode 100644 index 0000000..e3fb049 --- /dev/null +++ b/WindowsService1/ProjectInstaller.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.Linq; +using System.Threading.Tasks; + +namespace WindowsService1 +{ + [RunInstaller(true)] + public partial class ProjectInstaller : System.Configuration.Install.Installer + { + public ProjectInstaller() + { + InitializeComponent(); + } + } +} diff --git a/WindowsService1/ProjectInstaller.resx b/WindowsService1/ProjectInstaller.resx new file mode 100644 index 0000000..5c8b468 --- /dev/null +++ b/WindowsService1/ProjectInstaller.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 196, 17 + + + False + + \ No newline at end of file diff --git a/WindowsService1/Properties/AssemblyInfo.cs b/WindowsService1/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5a8299a --- /dev/null +++ b/WindowsService1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WindowsService1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("WindowsService1")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c02910b6-11c6-4e58-b830-3f64caed0eb5")] + +// 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/WindowsService1/Service1.Designer.cs b/WindowsService1/Service1.Designer.cs new file mode 100644 index 0000000..42153f0 --- /dev/null +++ b/WindowsService1/Service1.Designer.cs @@ -0,0 +1,38 @@ + +namespace WindowsService1 +{ + partial class Service1 + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "Service1"; + } + + #endregion + } +} diff --git a/WindowsService1/Service1.cs b/WindowsService1/Service1.cs new file mode 100644 index 0000000..cc5dfdd --- /dev/null +++ b/WindowsService1/Service1.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace WindowsService1 +{ + public partial class Service1 : ServiceBase + { + public Service1() + { + InitializeComponent(); + } + + protected override void OnStart(string[] args) + { + } + + protected override void OnStop() + { + } + } +} diff --git a/WindowsService1/WindowsService1.csproj b/WindowsService1/WindowsService1.csproj new file mode 100644 index 0000000..5e6444f --- /dev/null +++ b/WindowsService1/WindowsService1.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {C02910B6-11C6-4E58-B830-3F64CAED0EB5} + WinExe + WindowsService1 + WindowsService1 + v4.6.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Component + + + ProjectInstaller.cs + + + Component + + + Service1.cs + + + + + + + + + + ProjectInstaller.cs + + + + \ No newline at end of file -- libgit2 0.21.2