From 1c67d6145e6eed5454991218f7c50ab3caafdfd3 Mon Sep 17 00:00:00 2001 From: mika-n Date: Mon, 1 Jul 2019 02:45:55 +0300 Subject: [PATCH] Added cmdline options to send start/stop/shutdown/LoadProfile/LoadTempProfile commands to a background DS4Win app via IPC messaging interface. --- DS4Windows/DS4Forms/DS4Form.cs | 59 ++++++++++++++++++++++++++++++++++ DS4Windows/Program.cs | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/DS4Windows/DS4Forms/DS4Form.cs b/DS4Windows/DS4Forms/DS4Form.cs index abbc0b0..16a7e87 100644 --- a/DS4Windows/DS4Forms/DS4Form.cs +++ b/DS4Windows/DS4Forms/DS4Form.cs @@ -85,6 +85,9 @@ 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)] + static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + public DS4Form(string[] args) { Global.FindConfigLocation(); @@ -386,6 +389,17 @@ 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.rootHub.Debug += On_Debug; AppLogger.GuiLog += On_Debug; @@ -1137,6 +1151,51 @@ Properties.Resources.DS4Update, MessageBoxButtons.YesNo, MessageBoxIcon.Question systemShutdown = true; break; } + case Program.WM_COPYDATA: + { + // Received InterProcessCommunication (IPC) message. Handle the requested command + Program.COPYDATASTRUCT cds = (Program.COPYDATASTRUCT)m.GetLParam(typeof(Program.COPYDATASTRUCT)); + if (cds.cbData >= 4) + { + int tdevice = -1; + + byte[] buffer = new byte[cds.cbData]; + Marshal.Copy(cds.lpData, buffer, 0, cds.cbData); + string[] strData = Encoding.ASCII.GetString(buffer).Split('.'); + + if (strData.Length >= 1) + { + strData[0] = strData[0].ToLower(); + + if (strData[0] == "start") + ServiceStartup(true); + else if (strData[0] == "stop") + ServiceShutdown(true); + else if (strData[0] == "shutdown") + ScpForm_Closing(this, new FormClosingEventArgs(CloseReason.ApplicationExitCall, false)); + else if (strData[0] == "loadprofile" && strData.Length >= 3) + { + // Command syntax: LoadProfile.device#.profileName (fex LoadProfile.1.GameSnake) + if(int.TryParse(strData[1], out tdevice)) tdevice--; + + if (tdevice >= 0 && tdevice < 4) + { + ProfilePath[tdevice] = strData[2]; + LoadProfile(tdevice, true, Program.rootHub); + } + } + else if (strData[0] == "loadtempprofile" && strData.Length >= 3) + { + // Command syntax: LoadTempProfile.device#.profileName (fex LoadTempProfile.1.GameSnake) + if (int.TryParse(strData[1], out tdevice)) tdevice--; + + if (tdevice >= 0 && tdevice < 4) + LoadTempProfile(tdevice, strData[2], true, Program.rootHub); + } + } + } + break; + } default: break; } diff --git a/DS4Windows/Program.cs b/DS4Windows/Program.cs index 81dc8e7..3f0ad2d 100644 --- a/DS4Windows/Program.cs +++ b/DS4Windows/Program.cs @@ -11,6 +11,22 @@ namespace DS4Windows { static class Program { + [DllImport("user32.dll", EntryPoint = "FindWindow")] + private static extern IntPtr FindWindow(string sClass, string sWindow); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] + private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref COPYDATASTRUCT lParam); + + public const int WM_COPYDATA = 0x004A; + + [StructLayout(LayoutKind.Sequential)] + public struct COPYDATASTRUCT + { + public IntPtr dwData; + public int cbData; + public IntPtr lpData; + } + // Add "global\" in front of the EventName, then only one instance is allowed on the // whole system, including other users. But the application can not be brought // into view, of course. @@ -66,6 +82,46 @@ namespace DS4Windows Environment.ExitCode = 0; return; } + else if (s == "command" || s == "-command") + { + i++; + + IntPtr hWndDS4WindowsForm = IntPtr.Zero; + + Global.FindConfigLocation(); + if (args[i].Length > 0 && !string.IsNullOrEmpty(Global.appdatapath) && System.IO.File.Exists(Global.appdatapath + "\\IPCClassName.dat")) + { + hWndDS4WindowsForm = FindWindow(System.IO.File.ReadAllText(Global.appdatapath + "\\IPCClassName.dat"), "DS4Windows"); + if (hWndDS4WindowsForm != IntPtr.Zero) + { + COPYDATASTRUCT cds; + cds.lpData = IntPtr.Zero; + + try + { + cds.dwData = IntPtr.Zero; + cds.cbData = args[i].Length; + cds.lpData = Marshal.StringToHGlobalAnsi(args[i]); + SendMessage(hWndDS4WindowsForm, WM_COPYDATA, IntPtr.Zero, ref cds); + } + finally + { + if(cds.lpData != IntPtr.Zero) + Marshal.FreeHGlobal(cds.lpData); + } + } + else + { + Environment.ExitCode = 2; + } + } + else + { + Environment.ExitCode = 1; + } + + return; + } } try