public ClientInfo TryConnect(Guid extProcID, string address)
        {
            var info = new ClientInfo();
            ServerExtensionClient client;

            try
            {
                client = new ServerExtensionClient(address);
                client.RegisterClient();
                info.ExtensionID = client.ID;
                if (client.SingleInstanceOnly)
                {
                    bool isNotUnique;
                    lock (_clients)
                        isNotUnique = _clients.Any(c => c.ExtensionID == info.ExtensionID);
                    if (isNotUnique)
                    {
                        _logger.Warn("A second instance of the extension \"" + info.ExtensionID + "\" was found, but the extension has specified that only one instance is allowed to run at a time. The second instance will be stopped.");
                        _extProcMgr.Stop(extProcID);
                        return(null);
                    }
                }
                info.ExtProcID   = extProcID;
                info.Name        = client.Name;
                info.Description = client.Description;
                info.Commands    = client.GetCommands();
                info.Client      = client;
                _logger.Info("Connected to extension: " + info.Name);
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Exception thrown while trying to connect to extension \"" + info.Name + "\"", ex);
                return(null);
            }
            client.Disconnected += c =>
            {
                lock (_clients)
                    _clients.Remove(info);
                _logger.Info("Lost connection to extension: " + info.Name);
            };
            client.NotificationReceived += (src, msg, lvl) =>
            {
                var handler = ExtensionNotificationReceived;
                if (handler != null)
                {
                    handler(info.ExtProcID, info.ExtensionID, info.Name, src, msg, lvl);
                }
            };

            lock (_clients)
                _clients.Add(info);
            return(info);
        }
        private void UpdateRunningExtensions()
        {
            _logger.Info("Parsing extensions.txt file...");

            IEnumerable <string> lines;

            try
            {
                lines = _file.Exists ? File.ReadLines(_file.FullName) : new string[0];
            }
            catch (IOException ex)
            {
                _logger.WarnException("Unable to read extensions file", ex);
                return;
            }

            var bag = new List <KeyValuePair <string, Guid> >(_extProcMgr.GetExtensionProcessList()
                                                              .Select(p => new KeyValuePair <string, Guid>(string.Concat(p.DirectoryName, '\t', p.RequestedExtensionIDs.Concat(",")).Trim(), p.ID)));

            foreach (var line in lines)
            {
                var linestr = Regex.Replace((line ?? "").Trim(), @"\t+", "\t");
                if (string.IsNullOrWhiteSpace(linestr) || linestr.StartsWith("#"))
                {
                    continue;
                }
                var arr = linestr.Split('\t');
                if (arr.Length > 2)
                {
                    continue;
                }
                var      dir = arr[0].Trim();
                string[] ids = null;
                if (arr.Length == 2)
                {
                    ids = arr[1].Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim()).ToArray();
                }

                // linestr becomes the key identifying the extension process. there may be multiple processes with the same linestr.
                linestr = string.Concat(dir, '\t', ids.Concat(",")).Trim();
                // find out if we already have an extension process matching linestr and if so, remove it from the bag so it stays running.
                var item = bag.FirstOrDefault(b => b.Key == linestr);
                if (item.Key != null)
                {
                    _logger.Info("Extension config line matches pre-existing process: " + linestr);
                    bag.Remove(item);
                }
                else
                {
                    // seeing as there were no entries in the bag matching the current linestr, we've identified a new process that needs to be started up
                    _logger.Info("New extension config line found -> starting new extension process: " + linestr);
                    _extProcMgr.Execute(dir, ids ?? new string[0]);
                }
            }

            // anything left in the bag is no longer in the config file and needs to be shut down
            foreach (var item in bag)
            {
                _logger.Info("Existing extension process is no longer in the config file and will be shut down: " + item.Key);
                _extProcMgr.Stop(item.Value);
            }

            _logger.Info("Extensions list successfully updated from config file.");
        }