private void bt_Delete_Click(object sender, EventArgs e) { if (AITOOL.HasImageAdjustProfile(this.comboBox1.Text.Trim())) { AppSettings.Settings.ImageAdjustProfiles.Remove(AITOOL.GetImageAdjustProfileByName(this.comboBox1.Text.Trim(), false)); this.comboBox1.Items.Clear(); this.comboBox1.Text = ""; UpdateProfile(); } }
private void UpdateCombo() { string last = "Default"; if (AppSettings.Settings.ImageAdjustProfiles.Count == 0) { AppSettings.Settings.ImageAdjustProfiles.Add(new ClsImageAdjust("Default")); } if (!string.IsNullOrEmpty(this.comboBox1.Text.Trim())) { last = this.comboBox1.Text.Trim(); } if (!AITOOL.HasImageAdjustProfile(this.comboBox1.Text.Trim()) && !string.IsNullOrEmpty(this.comboBox1.Text.Trim())) { AppSettings.Settings.ImageAdjustProfiles.Add(new ClsImageAdjust(this.comboBox1.Text.Trim())); } this.comboBox1.Items.Clear(); int i = 0; int oldidx = 0; foreach (ClsImageAdjust ia in AppSettings.Settings.ImageAdjustProfiles) { this.comboBox1.Items.Add(ia.Name); if (string.Equals(last.Trim(), ia.Name.Trim(), StringComparison.OrdinalIgnoreCase)) { oldidx = i + 1; } i++; } if (this.comboBox1.Items.Count > 0) { this.comboBox1.SelectedIndex = oldidx; } this.CurIA = AITOOL.GetImageAdjustProfileByName(this.comboBox1.Text.Trim()); }
public static async Task <bool> LoadAsync() { await semaphoreSlim.WaitAsync(); using var Trace = new Trace(); //This c# 8.0 using feature will auto dispose when the function is done. bool Ret = false; try { //////multiple threads may be trying to save at the same time ////lock (ThreadLock) //{ Settings.SettingsValid = false; //assume failure bool Resave = false; UpdateSettingsLocation(); //save to \settings folder or appdata\settings //get backup json from registry AppSettings.LastSettingsJSON = Global.GetSetting("BackupSettingsJSON", ""); bool IsSettingsFileValid = await IsFileValidAsync(AppSettings.Settings.SettingsFileName); bool IsSettingsBakFileValid = await IsFileValidAsync(AppSettings.Settings.SettingsFileName + ".bak"); //read the old configuration file if (!IsSettingsFileValid && !IsSettingsBakFileValid && string.IsNullOrEmpty(AppSettings.LastSettingsJSON)) { //-------------------------------------------------------------------------------------------------------------------- //try to read in OLD aitool.exe.config or user.config files - they were //unreliable because of strict versioning, etc // // NO NEED to add NEW settings to this area //-------------------------------------------------------------------------------------------------------------------- List <FileInfo> filist = new List <FileInfo>(); FileInfo fi = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName(Assembly.GetEntryAssembly().Location) + ".config")); if (fi.Exists) { filist.Add(fi); } filist.AddRange(Global.GetFiles(Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "WindowsFormsApp2"), "user.config")); //sort by date filist = filist.OrderByDescending((d) => d.LastWriteTime).ToList(); Log("First time load, reading old config file: " + filist[0].FullName); XDocument xmlfile = XDocument.Load(filist[0].FullName); //<configuration> // <userSettings> // <WindowsFormsApp2.Properties.Settings> // <setting name="telegram_token" serializeAs="String"> // <value /> // </setting> // <setting name="telegram_chatid" serializeAs="String"> // <value /> // </setting> // <setting name="input_path" serializeAs="String"> // <value>D:\BlueIrisStorage\AIInput</value> // </setting> IEnumerable <XElement> els = xmlfile.XPathSelectElements("/configuration/userSettings/WindowsFormsApp2.Properties.Settings/setting"); if (els == null || els.Count() == 0) { els = xmlfile.XPathSelectElements("/configuration/applicationSettings/WindowsFormsApp2.Properties.Settings/setting"); } int cnt = 0; foreach (XElement el in els) { string val = el.Value; if (!string.IsNullOrEmpty(val)) { if (el.ToString().Contains("telegram_token")) { Settings.telegram_token = val; cnt += 1; } if (el.ToString().Contains("telegram_chatids")) { Settings.telegram_chatids = Global.Split(val, ","); cnt += 1; } if (el.ToString().Contains("input_path")) { Settings.input_path = val; cnt += 1; } if (el.ToString().Contains("deepstack_url")) { Settings.deepstack_url = val; cnt += 1; } if (el.ToString().Contains("log_everything")) { Settings.log_everything = Convert.ToBoolean(val); cnt += 1; } if (el.ToString().Contains("send_errors")) { Settings.send_telegram_errors = Convert.ToBoolean(val); cnt += 1; } if (el.ToString().Contains("close_instantly")) { Settings.close_instantly = Convert.ToInt32(val); cnt += 1; } } } Resave = (cnt > 0); Settings.SettingsValid = true; } else if (IsSettingsFileValid) { //Load regular settings file Log("Debug: Loading settings from " + AppSettings.Settings.SettingsFileName); Settings = Global.ReadFromJsonFile <ClsSettings>(AppSettings.Settings.SettingsFileName); } else if (IsSettingsBakFileValid) { //revert to backup if its good Log("Error: Reverting to backup settings file: " + AppSettings.Settings.SettingsFileName + ".bak"); Log("Loading settings from " + AppSettings.Settings.SettingsFileName + ".bak"); Settings = Global.ReadFromJsonFile <ClsSettings>(AppSettings.Settings.SettingsFileName + ".bak"); } else if (!string.IsNullOrEmpty(AppSettings.LastSettingsJSON) && !File.Exists(AppSettings.Settings.SettingsFileName)) { //revert to REGISTRY backup if its good AND the main settings file doesnt exist at all (so someone can delete the settings file to reset settings) Log("Error: Reverting to REGISTRY backup settings..."); Settings = Global.SetJSONString <ClsSettings>(AppSettings.LastSettingsJSON); } else { //nothing valid Log("Error: Settings file AND backup were missing or corrupt."); if (File.Exists(AppSettings.Settings.SettingsFileName)) { File.Delete(AppSettings.Settings.SettingsFileName); } if (File.Exists(AppSettings.Settings.SettingsFileName + ".bak")) { File.Delete(AppSettings.Settings.SettingsFileName + ".bak"); } } if (Settings != null) { if (Settings.telegram_cooldown_minutes > -1) { Settings.telegram_cooldown_seconds = Convert.ToInt32(Math.Round(TimeSpan.FromMinutes(Settings.telegram_cooldown_minutes).TotalSeconds, 0)); Settings.telegram_cooldown_minutes = -1; } UpdateSettingsLocation(); //save to \settings folder or appdata\settings //Ive had a case where MaskManager was null/corrupt to double check: foreach (Camera cam in Settings.CameraList) { if (string.IsNullOrEmpty(cam.BICamName)) { //if (!string.IsNullOrEmpty(cam.Prefix) && !cam.Prefix.Contains("*")) // cam.BICamName = cam.Prefix.Trim(".-".ToCharArray()); //else cam.BICamName = cam.Name; } if (string.IsNullOrEmpty(cam.MaskFileName)) { cam.MaskFileName = $"{cam.Name}.bmp"; } if (cam.ImageResolutions.Count == 0) { cam.ScanImages(10, 500, -1);//run a quick scan to get resolutions } if (cam.cooldown_time > -1) { cam.cooldown_time_seconds = Convert.ToInt32(Math.Round(TimeSpan.FromMinutes(cam.cooldown_time).TotalSeconds, 0)); cam.cooldown_time = -1; } if (cam.maskManager == null) { cam.maskManager = new MaskManager(); Log("Warning: Had to reset MaskManager for camera " + cam.Name); } //update threshold in all masks if changed during session cam.maskManager.Update(cam); ///this was an old setting we dont want to use any longer, but pull it over if someone enabled it before if (cam.trigger_url_cancels && !string.IsNullOrWhiteSpace(cam.cancel_urls_as_string)) { cam.cancel_urls_as_string = cam.trigger_urls_as_string; cam.trigger_url_cancels = false; } cam.trigger_urls = Global.Split(cam.trigger_urls_as_string, "\r\n|;,").ToArray(); cam.cancel_urls = Global.Split(cam.cancel_urls_as_string, "\r\n|;,").ToArray(); if (cam.Action_image_copy_enabled && !string.IsNullOrWhiteSpace(cam.Action_network_folder) && cam.Action_network_folder_purge_older_than_days > 0 && LastJPGCleanDay != DateTime.Now.DayOfYear && Directory.Exists(cam.Action_network_folder)) { Log($"Debug: Cleaning out jpg files older than '{cam.Action_network_folder_purge_older_than_days}' days in '{cam.Action_network_folder}'..."); List <FileInfo> filist = new List <FileInfo>(Global.GetFiles(cam.Action_network_folder, "*.jpg")); int deleted = 0; int errs = 0; foreach (FileInfo fi in filist) { if ((DateTime.Now - fi.LastWriteTime).TotalDays > cam.Action_network_folder_purge_older_than_days) { try { fi.Delete(); deleted++; } catch { errs++; } } } if (errs == 0) { Log($"Debug: ...Deleted {deleted} out of {filist.Count} files"); } else { Log($"Debug: ...Deleted {deleted} out of {filist.Count} files with {errs} errors."); } LastJPGCleanDay = DateTime.Now.DayOfYear; } } //load cameras the old way if needed if (Settings.CameraList.Count == 0) { string camerafolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cameras"); Log("No cameras loaded in settings, trying to load old camera files from " + camerafolder); List <FileInfo> files = Global.GetFiles(camerafolder, "*.txt"); //load all settings files in a string array //Sort so more recent files are processed first - to make sure any dupes that are skipped are older //I *think* this logic works? files = files.OrderByDescending((d) => d.LastWriteTime).ToList(); //create a camera object for every camera settings file int cnt = 0; foreach (FileInfo file in files) { //check if camera with specified name or its prefix already exists. If yes, then abort. bool fnd = false; foreach (Camera c in AppSettings.Settings.CameraList) { if (string.Equals(c.Name, Path.GetFileNameWithoutExtension(file.FullName), StringComparison.OrdinalIgnoreCase)) { fnd = true; } else if (string.Equals(c.Prefix, System.IO.File.ReadAllLines(file.FullName)[2].Split('"')[1], StringComparison.OrdinalIgnoreCase)) { fnd = true; } } if (!fnd) { cnt++; Camera cam = new Camera(); //create new camera object cam.ReadConfig(file.FullName); //read camera's config from file AppSettings.Settings.CameraList.Add(cam); //add created camera object to CameraList } else { Log("Skipped duplicate camera: " + file); } } if (cnt > 0) { Log($"...Loaded {cnt} camera files."); } else { Log($"...NO old camera txt files could be loaded."); } Resave = (cnt > 1); } //sort the camera list: AppSettings.Settings.CameraList = AppSettings.Settings.CameraList.OrderBy((d) => d.Name).ToList(); AITOOL.UpdateAIURLList(true); //clean up image adjust list List <ClsImageAdjust> iaps = AppSettings.Settings.ImageAdjustProfiles; AppSettings.Settings.ImageAdjustProfiles.Clear(); for (int i = 0; i < iaps.Count; i++) { if (!AppSettings.Settings.ImageAdjustProfiles.Contains(iaps[i])) { Settings.ImageAdjustProfiles.Add(iaps[i]); } } for (int i = 0; i < AppSettings.Settings.AIURLList.Count; i++) { AppSettings.Settings.AIURLList[i].Order = i + 1; if (!AITOOL.HasImageAdjustProfile(AppSettings.Settings.AIURLList[i].ImageAdjustProfile)) { AppSettings.Settings.AIURLList[i].ImageAdjustProfile = "Default"; } } Ret = true; } else { Log("Error: Could not load settings?"); } if (Resave) { //we imported old settings, save them SaveAsync(); } //} } catch (Exception ex) { Log("Error: Could not save settings: " + Global.ExMsg(ex)); } finally { semaphoreSlim.Release(); } return(Ret); }
public static async Task <bool> LoadAsync() { await semaphoreSlim.WaitAsync(); using var Trace = new Trace(); //This c# 8.0 using feature will auto dispose when the function is done. bool Ret = false; try { //////multiple threads may be trying to save at the same time ////lock (ThreadLock) //{ Settings.SettingsValid = false; //assume failure bool Resave = false; UpdateSettingsLocation(); //save to \settings folder or appdata\settings //get backup json from registry AppSettings.LastSettingsJSON = Global.GetRegSetting("BackupSettingsJSON", ""); bool IsSettingsFileValid = await IsFileValidAsync(AppSettings.Settings.SettingsFileName); bool SettingsFileExists = File.Exists(AppSettings.Settings.SettingsFileName); bool IsSettingsBakFileValid = await IsFileValidAsync(AppSettings.Settings.SettingsFileName + ".bak"); string LastRunPath = Global.GetRegSetting("LastRunPath", ""); string LastSettingsFile = ""; string LastSettingsFolder = ""; if (!LastRunPath.IsEmpty() && !Directory.GetCurrentDirectory().EqualsIgnoreCase(LastRunPath) && Directory.Exists(LastRunPath)) { LastSettingsFolder = Path.Combine(LastRunPath, "_SETTINGS"); LastSettingsFile = Path.Combine(LastSettingsFolder, "AITOOL.Settings.JSON"); if (!File.Exists(LastSettingsFile)) { LastSettingsFile = ""; LastSettingsFolder = ""; } } //read the old configuration file if (!IsSettingsFileValid && !IsSettingsBakFileValid && string.IsNullOrEmpty(AppSettings.LastSettingsJSON)) { //-------------------------------------------------------------------------------------------------------------------- //try to read in OLD aitool.exe.config or user.config files - they were //unreliable because of strict versioning, etc // // NO NEED to add NEW settings to this area //-------------------------------------------------------------------------------------------------------------------- List <FileInfo> filist = new List <FileInfo>(); FileInfo fi = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName(Assembly.GetEntryAssembly().Location) + ".config")); if (fi.Exists) { filist.Add(fi); } filist.AddRange(Global.GetFiles(Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "WindowsFormsApp2"), "user.config")); //sort by date filist = filist.OrderByDescending((d) => d.LastWriteTime).ToList(); Log("First time load, reading old config file: " + filist[0].FullName); XDocument xmlfile = XDocument.Load(filist[0].FullName); //<configuration> // <userSettings> // <WindowsFormsApp2.Properties.Settings> // <setting name="telegram_token" serializeAs="String"> // <value /> // </setting> // <setting name="telegram_chatid" serializeAs="String"> // <value /> // </setting> // <setting name="input_path" serializeAs="String"> // <value>D:\BlueIrisStorage\AIInput</value> // </setting> IEnumerable <XElement> els = xmlfile.XPathSelectElements("/configuration/userSettings/WindowsFormsApp2.Properties.Settings/setting"); if (els == null || els.Count() == 0) { els = xmlfile.XPathSelectElements("/configuration/applicationSettings/WindowsFormsApp2.Properties.Settings/setting"); } int cnt = 0; foreach (XElement el in els) { string val = el.Value; if (!string.IsNullOrEmpty(val)) { if (el.ToString().Contains("telegram_token")) { Settings.telegram_token = val; cnt += 1; } if (el.ToString().Contains("telegram_chatids")) { Settings.telegram_chatids = val.SplitStr(","); cnt += 1; } if (el.ToString().Contains("input_path")) { Settings.input_path = val; cnt += 1; } if (el.ToString().Contains("deepstack_url")) { Settings.deepstack_url = val; cnt += 1; } if (el.ToString().Contains("log_everything")) { Settings.log_everything = Convert.ToBoolean(val); cnt += 1; } if (el.ToString().Contains("send_errors")) { Settings.send_telegram_errors = Convert.ToBoolean(val); cnt += 1; } if (el.ToString().Contains("close_instantly")) { Settings.close_instantly = Convert.ToInt32(val); cnt += 1; } } } Resave = (cnt > 0); Settings.SettingsValid = true; } else if (IsSettingsFileValid) { //Load regular settings file Log("Debug: Loading settings from " + AppSettings.Settings.SettingsFileName); Settings = Global.ReadFromJsonFile <ClsSettings>(AppSettings.Settings.SettingsFileName); } else if (IsSettingsBakFileValid) { //revert to backup if its good Log("Error: Reverting to backup settings file: " + AppSettings.Settings.SettingsFileName + ".bak"); Log("Loading settings from " + AppSettings.Settings.SettingsFileName + ".bak"); Settings = Global.ReadFromJsonFile <ClsSettings>(AppSettings.Settings.SettingsFileName + ".bak"); } else if (!string.IsNullOrEmpty(AppSettings.LastSettingsJSON) && SettingsFileExists) { //revert to REGISTRY backup if its good AND the main settings file doesnt exist at all (so someone can delete the settings file to reset settings) Log("Error: Reverting to REGISTRY backup settings..."); Settings = Global.SetJSONString <ClsSettings>(AppSettings.LastSettingsJSON); } else if (!LastSettingsFile.IsEmpty() && !SettingsFileExists && !Global.IsService) { //revert to REGISTRY backup and look for the last folder to migrate settings from string newfolder = Path.GetDirectoryName(Settings.SettingsFileName); Log($"Debug: Previous settings found at {LastSettingsFolder} but not {newfolder}..."); if (System.Windows.Forms.MessageBox.Show($"Would you like to migrate settings from the last folder?\r\n\r\n{LastSettingsFolder}", "Migrate Settings?", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) { //copy the old settings folder: Log($"Debug: Copying settings folder from original '{LastSettingsFolder}' to '{newfolder}'..."); Global.MoveFiles(LastSettingsFolder, newfolder, "*.*", true, true); Log("Debug: Loading settings from " + AppSettings.Settings.SettingsFileName); Settings = Global.ReadFromJsonFile <ClsSettings>(AppSettings.Settings.SettingsFileName); } else { Log("Debug: Did NOT import settings from folder."); } } else if (!AppSettings.LastSettingsJSON.IsEmpty() && !SettingsFileExists && !Global.IsService) { //revert to REGISTRY backup and look for the last folder to migrate settings from string newfolder = Path.GetDirectoryName(Settings.SettingsFileName); Log($"Debug: Previous settings found in the registry but not {newfolder}..."); if (System.Windows.Forms.MessageBox.Show($"Would you like to migrate settings from the last saved REGISTRY copy?", "Migrate Settings?", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) { //copy the old settings folder: Log("Debug: Loading settings from previous REGISTRY..."); Settings = Global.SetJSONString <ClsSettings>(AppSettings.LastSettingsJSON); } else { Log("Debug: Did NOT import settings from previous registry copy."); } } else { //nothing valid Log("Error: Settings file AND backup were missing or corrupt."); if (File.Exists(AppSettings.Settings.SettingsFileName)) { File.Delete(AppSettings.Settings.SettingsFileName); } if (File.Exists(AppSettings.Settings.SettingsFileName + ".bak")) { File.Delete(AppSettings.Settings.SettingsFileName + ".bak"); } } if (Settings != null) { //The client identifier (ClientId) identifies each MQTT client that connects to an MQTT broker. //The broker uses the ClientID to identify the client and the current state of the client.Therefore, //this ID should be unique per client and broker. In MQTT 3.1.1 (the current standard), you can send //an empty ClientId, if you don’t need a state to be held by the broker. The empty ClientID results //in a connection without any state. In this case, the clean session flag must be set to true or //the broker will reject the connection. if (Settings.mqtt_clientid.EqualsIgnoreCase("aitool")) { Settings.mqtt_clientid = "AITool-BATMAN-" + Global.GetMacAddress(); //set the id to a unique number that should not *usually* change on a machine - the mac address of an active adapter } //but only set it once if the default clientid is set if (Settings.telegram_cooldown_minutes > -1) { Settings.telegram_cooldown_seconds = Convert.ToInt32(Math.Round(TimeSpan.FromMinutes(Settings.telegram_cooldown_minutes).TotalSeconds, 0)); Settings.telegram_cooldown_minutes = -1; } UpdateSettingsLocation(); //save to \settings folder or appdata\settings //load cameras the old way if needed if (Settings.CameraList.Count == 0) { string camerafolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cameras"); Log("No cameras loaded in settings, trying to load old camera files from " + camerafolder); List <FileInfo> files = Global.GetFiles(camerafolder, "*.txt"); //load all settings files in a string array //Sort so more recent files are processed first - to make sure any dupes that are skipped are older //I *think* this logic works? files = files.OrderByDescending((d) => d.LastWriteTime).ToList(); //create a camera object for every camera settings file int cnt = 0; foreach (FileInfo file in files) { //check if camera with specified name or its prefix already exists. If yes, then abort. bool fnd = false; foreach (Camera c in AppSettings.Settings.CameraList) { if (string.Equals(c.Name, Path.GetFileNameWithoutExtension(file.FullName), StringComparison.OrdinalIgnoreCase)) { fnd = true; } else if (string.Equals(c.Prefix, System.IO.File.ReadAllLines(file.FullName)[2].Split('"')[1], StringComparison.OrdinalIgnoreCase)) { fnd = true; } } if (!fnd) { cnt++; Camera cam = new Camera(); //create new camera object cam.ReadConfig(file.FullName); //read camera's config from file AppSettings.Settings.CameraList.Add(cam); //add created camera object to CameraList } else { Log("Skipped duplicate camera: " + file); } } if (cnt > 0) { Log($"...Loaded {cnt} camera files."); } else { Log($"...NO old camera txt files could be loaded."); } Resave = (cnt > 1); } Camera DefaultCam = GetCamera("default", ReturnDefault: true); if (DefaultCam == null) { //add a default camera Camera cam = new Camera("Default"); Settings.CameraList.Add(cam); } else if (DefaultCam.DefaultTriggeringObjects == null) { //replace the old default camera with a new one or else we have trouble with the triggering objects manager Camera cam = new Camera(DefaultCam.Name); Settings.CameraList.Remove(DefaultCam); Settings.CameraList.Add(cam); } if (!Settings.ObjectPriority.Has("suv") || !Settings.ObjectPriority.Has("van")) { Settings.ObjectPriority = "person, bear, elephant, car, truck, SUV, van, bicycle, motorcycle, bus, dog, horse, boat, train, airplane, zebra, giraffe, cow, sheep, cat, bird"; } //make sure everything in the cameras look correct: foreach (Camera cam in Settings.CameraList) { cam.UpdateCamera(); } //sort the camera list: AppSettings.Settings.CameraList = AppSettings.Settings.CameraList.OrderBy((d) => d.Name).ToList(); AITOOL.UpdateAIURLList(true); //clean up image adjust list List <ClsImageAdjust> iaps = AppSettings.Settings.ImageAdjustProfiles; AppSettings.Settings.ImageAdjustProfiles.Clear(); for (int i = 0; i < iaps.Count; i++) { if (!AppSettings.Settings.ImageAdjustProfiles.Contains(iaps[i])) { Settings.ImageAdjustProfiles.Add(iaps[i]); } } for (int i = 0; i < AppSettings.Settings.AIURLList.Count; i++) { AppSettings.Settings.AIURLList[i].Order = i + 1; if (!AITOOL.HasImageAdjustProfile(AppSettings.Settings.AIURLList[i].ImageAdjustProfile)) { AppSettings.Settings.AIURLList[i].ImageAdjustProfile = "Default"; } } if (Settings.FacesPath.IsEmpty()) { string pth = Path.GetDirectoryName(Settings.SettingsFileName); Settings.FacesPath = Path.Combine(pth, "FaceStorage"); } string facefile = Path.Combine(Settings.FacesPath, "Faces.JSON"); if (await IsFileValidAsync(facefile, 400)) { AITOOL.FaceMan = Global.ReadFromJsonFile <ClsFaceManager>(facefile); } else { AITOOL.FaceMan = new ClsFaceManager(); } Ret = true; } else { Log("Error: Could not load settings?"); } if (Resave) { //we imported old settings, save them SaveAsync(); } //} } catch (Exception ex) { Log("Error: Could not save settings: " + ex.Msg()); } finally { semaphoreSlim.Release(); } return(Ret); }