Unity:程序间信息传递及后台监听键盘输入技术问询
Hey there! Let's tackle your launcher system's two key needs: background global keyboard listening and sending messages to the running app. I'll break this down with practical, Windows-specific solutions (since you're targeting .exe apps):
Standard keyboard event handlers only work when your launcher has focus. To listen globally even when minimized or in the background, you'll need a low-level keyboard hook on Windows. This type of hook doesn’t require injecting code into other processes, making it perfect for a lightweight launcher.
Example Implementation (C#)
You’ll need to P/Invoke Windows API functions from user32.dll:
using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class GlobalKeyboardListener { private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; public static void StartListening() { _hookID = SetHook(_proc); Application.Run(); // Keeps the message loop alive for the hook } public static void StopListening() { UnhookWindowsHookEx(_hookID); } private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (var curProcess = System.Diagnostics.Process.GetCurrentProcess()) using (var curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); // Handle your key press here (e.g., trigger an action) Console.WriteLine($"Key pressed: {(Keys)vkCode}"); } return CallNextHookEx(_hookID, nCode, wParam, lParam); } [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); }
- Critical note: The hook needs a running message loop (hence
Application.Run()). If your launcher is a console app, run this in a separate thread or use a minimal Windows Forms/WPF skeleton to keep the loop active.
There are reliable ways to communicate with a running .exe process, depending on whether you control the target app’s code:
Option 1: Custom Windows Messages (For Your Own Target App)
If you built the target app, define a custom Windows message and handle it in its message loop. Use SendMessage from your launcher to send data:
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); // Define a unique custom message (avoid conflicts with system messages) private const uint WM_MY_LAUNCHER_MESSAGE = 0x0400 + 100; // Send data to the target app's main window public static void SendMessageToApp(IntPtr mainWindowHandle, string data) { IntPtr dataPtr = Marshal.StringToHGlobalAuto(data); SendMessage(mainWindowHandle, WM_MY_LAUNCHER_MESSAGE, IntPtr.Zero, dataPtr); Marshal.FreeHGlobal(dataPtr); }
In the target app, override WndProc to process the message:
protected override void WndProc(ref Message m) { if (m.Msg == WM_MY_LAUNCHER_MESSAGE) { string receivedData = Marshal.PtrToStringAuto(m.LParam); // Handle the incoming data here } base.WndProc(ref m); }
Option 2: Named Pipes (Flexible for Any App)
For bidirectional or complex communication, named pipes work seamlessly—even with third-party apps (if you can add a pipe listener to them). Here’s a quick launcher-side snippet:
using System.IO.Pipes; public static void SendDataViaPipe(string data) { using (var pipeClient = new NamedPipeClientStream(".", "MyLauncherPipe", PipeDirection.Out)) { pipeClient.Connect(1000); // Connect to the target app's pipe server using (var writer = new StreamWriter(pipeClient)) { writer.WriteLine(data); writer.Flush(); } } }
The target app would run a NamedPipeServerStream to listen for incoming data.
- When launching
path/app.exe, store the process’s main window handle (useProcess.MainWindowHandleonce the window loads). - Start the global keyboard listener in a background thread (or keep the message loop running).
- When a key press is detected, use your chosen inter-process communication method to send a message to the running app.
This setup lets your launcher stay in the background, listen for keyboard input globally, and communicate with the target app without needing focus.
内容的提问来源于stack exchange,提问作者Vanguard3000

