using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.Core.DI; using Tango.FSE.Common; using Tango.FSE.Common.FileAssociation; using Tango.FSE.Common.FSEApplication; using Tango.FSE.Common.Threading; using Tango.Logging; using ZetaIpc.Runtime.Server; namespace Tango.FSE.UI.FileAssociation { [TangoCreateWhenRegistered] public class DefaultFileAssociationProvider : FSEExtendedObject, IFileAssociationProvider { private Dictionary> _handlers; private IpcServer _ipcServer; private bool _initialized; public const string FILE_ASSOCIATION_PREFIX = "-file"; public const int FILE_ASSOCIATION_ARGS_COUNT = 3; public bool HasStartArgsPackage { get; private set; } [TangoInject] private IDispatcherProvider DispatcherProvider { get; set; } [TangoInject] private IFSEApplicationManager ApplicationManager { get; set; } public DefaultFileAssociationProvider() { _handlers = new Dictionary>(); _ipcServer = new IpcServer(); } public void RegisterFileAssociationHandler(string targetName, Action handler) { _handlers.Add(targetName.ToLower(), handler); } public async void OnApplicationReady(IFSEApplicationManager applicationManager) { if (_initialized) return; _initialized = true; try { LogManager.Log("Starting file association IPC service..."); _ipcServer.Start(Settings.FileAssociationServicePort); _ipcServer.ReceivedRequest += _ipcServer_ReceivedRequest; } catch (Exception ex) { LogManager.Log(ex, "Error starting file association IPC service. Another instance of the application might already be running..."); } var args = applicationManager.StartupArgs; LogManager.Log("Processing file association if any..."); if (args.Count == FILE_ASSOCIATION_ARGS_COUNT) { if (args[0] == FILE_ASSOCIATION_PREFIX) { //This delay is a workaround for when the invoked handler navigates to some module. //The navigation system does not catches the navigation request properly... :/ await Task.Delay(500); LogManager.Log($"File association package found: '{String.Join(" ", args)}'. Invoking handler..."); try { String targetName = args[1].ToLower(); if (_handlers.ContainsKey(targetName)) { var package = new FileAssociationPackage() { TargetName = targetName, File = args[2] }; _handlers[targetName].Invoke(package); } else { LogManager.Log("File association handler not found.", LogCategory.Warning); } } catch (Exception ex) { LogManager.Log(ex, $"Error invoking file association handler for package '{String.Join(" ", args)}'."); } } } } private void _ipcServer_ReceivedRequest(object sender, ReceivedRequestEventArgs e) { try { e.Handled = true; LogManager.Log($"File association package received through IPC server.\n{e.Request}\nInvoking handler..."); var package = JsonConvert.DeserializeObject(e.Request); if (_handlers.ContainsKey(package.TargetName.ToLower())) { DispatcherProvider.Invoke(() => { if (ApplicationManager.WindowState == System.Windows.WindowState.Minimized) { ApplicationManager.WindowState = System.Windows.WindowState.Normal; } ApplicationManager.ActivateMainWindow(); _handlers[package.TargetName.ToLower()].Invoke(package); }); } else { LogManager.Log($"Handler not found for target '{package.TargetName}'.", LogCategory.Warning); } } catch (Exception ex) { LogManager.Log(ex, $"Error invoking file association handler."); } } public void OnApplicationStarted(IFSEApplicationManager applicationManager) { HasStartArgsPackage = applicationManager.StartupArgs.Count == FILE_ASSOCIATION_ARGS_COUNT && applicationManager.StartupArgs[0] == FILE_ASSOCIATION_PREFIX; } } }