namespace ElectronNET.API { using System; using System.Diagnostics; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using ElectronNET.Serialization; /// /// Communicate asynchronously from the main process to renderer processes. /// public sealed class IpcMain { private static IpcMain _ipcMain; private static object _syncRoot = new object(); private static readonly JsonSerializerOptions BoxedObjectSerializationOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = false, Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase), new JsonToBoxedPrimitivesConverter(), } }; internal IpcMain() { } internal static IpcMain Instance { get { if (_ipcMain == null) { lock (_syncRoot) { if (_ipcMain == null) { _ipcMain = new IpcMain(); } } } return _ipcMain; } } /// /// Listens to channel, when a new message arrives listener would be called with /// listener(event, args...). /// /// Channelname. /// Callback Method. public async Task On(string channel, Action listener) { await BridgeConnector.Socket.Emit("registerIpcMainChannel", channel).ConfigureAwait(false); BridgeConnector.Socket.Off(channel); BridgeConnector.Socket.On(channel, (args) => { var arg = FormatArguments(args); listener(arg); }); } private static object FormatArguments(JsonElement args) { var objectArray = args.Deserialize(BoxedObjectSerializationOptions).ToList(); Debug.Assert(objectArray.Count <= 2); if (objectArray.Count == 2) { return objectArray[1]; } return null; } /// /// Send a message to the renderer process synchronously via channel, /// you can also send arbitrary arguments. /// /// Note: Sending a synchronous message will block the whole renderer process, /// unless you know what you are doing you should never use it. /// /// /// public void OnSync(string channel, Func listener) { BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel); BridgeConnector.Socket.On(channel, (args) => { var arg = FormatArguments(args); var result = listener(arg); BridgeConnector.Socket.Emit(channel + "Sync", result); }); } /// /// Send a message to the renderer process synchronously via channel, /// you can also send arbitrary arguments. /// /// Note: Sending a synchronous message will block the whole renderer process, /// unless you know what you are doing you should never use it. /// /// /// public void OnSync(string channel, Func> listener) { BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel); BridgeConnector.Socket.On(channel, (args) => { Task.Run(async () => { var arg = FormatArguments(args); var result = await listener(arg); BridgeConnector.Socket.Emit(channel + "Sync", result); }); }); } /// /// Adds a one time listener method for the event. This listener is invoked only /// the next time a message is sent to channel, after which it is removed. /// /// Channelname. /// Callback Method. public void Once(string channel, Action listener) { BridgeConnector.Socket.Emit("registerOnceIpcMainChannel", channel); BridgeConnector.Socket.Once(channel, (args) => { var arg = FormatArguments(args); listener(arg); }); } /// /// Removes listeners of the specified channel. /// /// Channelname. public void RemoveAllListeners(string channel) { BridgeConnector.Socket.Emit("removeAllListenersIpcMainChannel", channel); } /// /// Send a message to the renderer process asynchronously via channel, you can also send /// arbitrary arguments. Arguments will be serialized in JSON internally and hence /// no functions or prototype chain will be included. The renderer process handles it by /// listening for channel with ipcRenderer module. /// /// BrowserWindow with channel. /// Channelname. /// Arguments data. public void Send(BrowserWindow browserWindow, string channel, params object[] data) { BridgeConnector.Socket.Emit("sendToIpcRenderer", browserWindow, channel, data); } /// /// Send a message to the BrowserView renderer process asynchronously via channel, you can also send /// arbitrary arguments. Arguments will be serialized in JSON internally and hence /// no functions or prototype chain will be included. The renderer process handles it by /// listening for channel with ipcRenderer module. /// /// BrowserView with channel. /// Channelname. /// Arguments data. public void Send(BrowserView browserView, string channel, params object[] data) { BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data); } /// /// Adds a handler for an invokeable IPC. This handler will be called /// whenever a renderer calls ipcRenderer.invoke(channel, ...args). /// /// Channelname. /// Callback Method. public void Handle(string channel, Func listener) { BridgeConnector.Socket.Emit("registerHandleIpcMainChannel", channel); BridgeConnector.Socket.On(channel, (args) => { var arg = FormatArguments(args); var result = listener(arg); BridgeConnector.Socket.Emit(channel + "Handle", result); }); } /// /// Adds a handler for an invokeable IPC. This handler will be called /// whenever a renderer calls ipcRenderer.invoke(channel, ...args). /// /// Channelname. /// Callback Method. public void Handle(string channel, Func> listener) { BridgeConnector.Socket.Emit("registerHandleIpcMainChannel", channel); BridgeConnector.Socket.On(channel, (args) => { Task.Run(async () => { var arg = FormatArguments(args); var result = await listener(arg); BridgeConnector.Socket.Emit(channel + "Handle", result); }); }); } /// /// Handles a single invokeable IPC message, then removes the listener. /// See ipcMain.handle(channel, listener). /// /// Channelname. /// Callback Method. public void HandleOnce(string channel, Func listener) { BridgeConnector.Socket.Emit("registerHandleOnceIpcMainChannel", channel); BridgeConnector.Socket.Once(channel, (args) => { var arg = FormatArguments(args); var result = listener(arg); BridgeConnector.Socket.Emit(channel + "HandleOnce", result); }); } /// /// Handles a single invokeable IPC message, then removes the listener. /// See ipcMain.handle(channel, listener). /// /// Channelname. /// Callback Method. public void HandleOnce(string channel, Func> listener) { BridgeConnector.Socket.Emit("registerHandleOnceIpcMainChannel", channel); BridgeConnector.Socket.Once(channel, (args) => { Task.Run(async () => { var arg = FormatArguments(args); var result = await listener(arg); BridgeConnector.Socket.Emit(channel + "HandleOnce", result); }); }); } /// /// Removes any handler for channel, if present. /// /// Channelname. public void RemoveHandler(string channel) { BridgeConnector.Socket.Emit("removeHandlerIpcMainChannel", channel); } } }