Changed IPC communication to use memory mapped memory instead of physical file to store the name of the DS4Form window class.

This commit is contained in:
mika-n 2019-07-03 00:02:29 +03:00
parent 75d08fe76c
commit c6daa18032
2 changed files with 64 additions and 17 deletions

View File

@ -59,7 +59,7 @@ namespace DS4Windows.Forms
private ManagementEventWatcher managementEvWatcher;
private DS4Forms.LanguagePackComboBox languagePackComboBox1;
private AdvancedColorDialog advColorDialog;
Dictionary<Control, string> hoverTextDict = new Dictionary<Control, string>();
Dictionary<Control, string> hoverTextDict = new Dictionary<Control, string>();
// 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;

View File

@ -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;
/// <summary>
/// The main entry point for the application.
/// </summary>
@ -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;
}
}
}