From c6daa180324b54542448ca16aa255de16c8d0561 Mon Sep 17 00:00:00 2001 From: mika-n Date: Wed, 3 Jul 2019 00:02:29 +0300 Subject: [PATCH] Changed IPC communication to use memory mapped memory instead of physical file to store the name of the DS4Form window class. --- DS4Windows/DS4Forms/DS4Form.cs | 16 ++------- DS4Windows/Program.cs | 65 ++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/DS4Windows/DS4Forms/DS4Form.cs b/DS4Windows/DS4Forms/DS4Form.cs index bc80523..1f84e2a 100644 --- a/DS4Windows/DS4Forms/DS4Form.cs +++ b/DS4Windows/DS4Forms/DS4Form.cs @@ -59,7 +59,7 @@ namespace DS4Windows.Forms private ManagementEventWatcher managementEvWatcher; private DS4Forms.LanguagePackComboBox languagePackComboBox1; private AdvancedColorDialog advColorDialog; - Dictionary hoverTextDict = new Dictionary(); + Dictionary hoverTextDict = new Dictionary(); // 0 index is used for application version text. 1 - 4 indices are used for controller status string[] notifyText = new string[5] { "DS4Windows v" + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion, @@ -87,9 +87,6 @@ namespace DS4Windows.Forms [DllImport("psapi.dll")] private static extern uint GetModuleFileNameEx(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize); - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); - public DS4Form(string[] args) { Global.FindConfigLocation(); @@ -391,16 +388,7 @@ namespace DS4Windows.Forms if (!(StartMinimized || mini)) Form_Resize(null, null); - // Write current window class name as InterProcessCommunication identifier. The IPC classname is used in WM_DATACOPY messaging interface. .NET WinForms creates a semi-random clas names per application. - StringBuilder wndClassNameStr = new StringBuilder(256); - if (GetClassName(this.Handle, wndClassNameStr, wndClassNameStr.Capacity) != 0 && wndClassNameStr.Length > 0) - { - if (System.IO.File.Exists(appdatapath + "\\IPCClassName.dat") && System.IO.File.ReadAllText(appdatapath + "\\IPCClassName.dat") == wndClassNameStr.ToString()) - wndClassNameStr.Clear(); // The wnd classname is still the same, so no need to re-write it - - if(wndClassNameStr.Length > 0) - System.IO.File.WriteAllText(appdatapath + "\\IPCClassName.dat", wndClassNameStr.ToString()); - } + Program.CreateIPCClassNameMMF(this.Handle); Program.rootHub.Debug += On_Debug; diff --git a/DS4Windows/Program.cs b/DS4Windows/Program.cs index d31fce2..81dabe2 100644 --- a/DS4Windows/Program.cs +++ b/DS4Windows/Program.cs @@ -6,11 +6,16 @@ using Process = System.Diagnostics.Process; using System.ComponentModel; using System.Globalization; using Microsoft.Win32.TaskScheduler; +using System.IO.MemoryMappedFiles; +using System.Text; namespace DS4Windows { static class Program { + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + [DllImport("user32.dll", EntryPoint = "FindWindow")] private static extern IntPtr FindWindow(string sClass, string sWindow); @@ -36,6 +41,9 @@ namespace DS4Windows private static Thread controlThread; private static Form ds4form; + private static MemoryMappedFile ipcClassNameMMF = null; // MemoryMappedFile for inter-process communication used to hold className of DS4Form window + private static MemoryMappedViewAccessor ipcClassNameMMA = null; + /// /// The main entry point for the application. /// @@ -86,15 +94,14 @@ namespace DS4Windows IntPtr hWndDS4WindowsForm = IntPtr.Zero; - Global.FindConfigLocation(); - if (args[i].Length > 0 && args[i].Length <= 256 && !string.IsNullOrEmpty(Global.appdatapath) && System.IO.File.Exists(Global.appdatapath + "\\IPCClassName.dat")) + if (args[i].Length > 0 && args[i].Length <= 256) { // Find the main DS4Form window handle and post WM_COPYDATA inter-process message. IPCClasName.dat file was created by the main DS4Windows process // and it contains the name of the DS4Form .NET window class name string (the hash key part of the string is dynamically generated by .NET engine and it may change, // so that's why the main process re-creates the file if the window class name is changed by .NET framework). Finding the WND handle usig both class name and window name // limits chances that WM_COPYDATA message is sent to a wrong window. - hWndDS4WindowsForm = FindWindow(System.IO.File.ReadAllText(Global.appdatapath + "\\IPCClassName.dat"), "DS4Windows"); + hWndDS4WindowsForm = FindWindow(ReadIPCClassNameMMF(), "DS4Windows"); if (hWndDS4WindowsForm != IntPtr.Zero) { COPYDATASTRUCT cds; @@ -178,6 +185,9 @@ namespace DS4Windows while (testThread.IsAlive) Thread.SpinWait(500); threadComEvent.Close(); + + if (ipcClassNameMMA != null) ipcClassNameMMA.Dispose(); + if (ipcClassNameMMF != null) ipcClassNameMMF.Dispose(); } private static void createControlService() @@ -233,5 +243,54 @@ namespace DS4Windows temp.WindowState = FormWindowState.Normal; } } + + public static void CreateIPCClassNameMMF(IntPtr hWnd) + { + if (ipcClassNameMMA != null) return; // Already holding a handle to MMF file. No need to re-write the data + + try + { + StringBuilder wndClassNameStr = new StringBuilder(128); + if (GetClassName(hWnd, wndClassNameStr, wndClassNameStr.Capacity) != 0 && wndClassNameStr.Length > 0) + { + byte[] buffer = ASCIIEncoding.ASCII.GetBytes(wndClassNameStr.ToString()); + + ipcClassNameMMF = MemoryMappedFile.CreateNew("DS4Windows_IPCClassName.dat", 128); + ipcClassNameMMA = ipcClassNameMMF.CreateViewAccessor(0, buffer.Length); + ipcClassNameMMA.WriteArray(0, buffer, 0, buffer.Length); + // The MMF file is alive as long this process holds the file handle open + } + } + catch (Exception) + { + /* Eat all exceptions because errors here are not fatal for DS4Win */ + } + } + + private static string ReadIPCClassNameMMF() + { + MemoryMappedFile mmf = null; + MemoryMappedViewAccessor mma = null; + + try + { + byte[] buffer = new byte[128]; + mmf = MemoryMappedFile.OpenExisting("DS4Windows_IPCClassName.dat"); + mma = mmf.CreateViewAccessor(0, 128); + mma.ReadArray(0, buffer, 0, buffer.Length); + return ASCIIEncoding.ASCII.GetString(buffer); + } + catch (Exception) + { + // Eat all exceptions + } + finally + { + if (mma != null) mma.Dispose(); + if (mmf != null) mmf.Dispose(); + } + + return null; + } } } \ No newline at end of file