1+ // No "using vJoyInterfaceWrap;" !!! We are using late-binding.
2+ using System ;
3+ using System . Collections . Generic ;
4+ using System . IO ;
5+ using System . IO . MemoryMappedFiles ;
6+ using System . Reflection ;
7+ using System . Runtime . InteropServices ;
8+ using System . Text ;
9+ using System . Threading ;
10+ using Microsoft . Win32 ;
11+
12+ namespace vJoyMMFServer // Changed to the correct namespace
13+ {
14+ // Your command classes remain unchanged. They are perfect.
15+ public abstract class vJoyCommand {
16+ public uint DeviceId { get ; set ; } = 1 ;
17+ public abstract void Execute ( dynamic joystick , Type hidUsagesType ) ;
18+ }
19+ public class AxisCommand : vJoyCommand {
20+ public string AxisName { get ; set ; }
21+ public uint Value { get ; set ; }
22+ public override void Execute ( dynamic joystick , Type hidUsagesType ) {
23+ dynamic usage = Enum . Parse ( hidUsagesType , "HID_USAGE_" + AxisName . ToUpper ( ) ) ;
24+ joystick . SetAxis ( ( int ) Value , DeviceId , usage ) ;
25+ Console . WriteLine ( $ "OK: Set Device { DeviceId } , Axis { AxisName } to { Value } ") ;
26+ }
27+ }
28+ public class ButtonCommand : vJoyCommand {
29+ public uint ButtonId { get ; set ; }
30+ public bool IsPressed { get ; set ; }
31+ public override void Execute ( dynamic joystick , Type hidUsagesType ) {
32+ joystick . SetBtn ( IsPressed , DeviceId , ButtonId ) ;
33+ Console . WriteLine ( $ "OK: Set Device { DeviceId } , Button { ButtonId } to { ( IsPressed ? "Pressed" : "Unpressed" ) } ") ;
34+ }
35+ }
36+
37+ class Program
38+ {
39+ // Your original static members are all here.
40+ [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
41+ private static extern IntPtr LoadLibrary ( string lpFileName ) ;
42+
43+ private static Assembly ? _vjoyWrapperAssembly ;
44+ private static dynamic ? _joystick ;
45+ private static Type ? _hidUsagesType ;
46+ private static readonly string [ ] AxisIdToName = { "" , "X" , "Y" , "Z" , "Rx" , "Ry" , "Rz" , "sl0" , "sl1" } ;
47+
48+ // New constants for our MMF communication
49+ private const string MapName = "VJoyCommandBus" ;
50+ private const string EventName = "VJoyCommandEvent" ;
51+ private const int MapSize = 1024 ; // 1KB buffer for commands
52+
53+ static int Main ( string [ ] args )
54+ {
55+ // First, perform your one-time vJoy initialization.
56+ Console . WriteLine ( "vJoy MMF Daemon starting..." ) ;
57+ if ( ! InitializeVJoy ( ) )
58+ {
59+ Console . Error . WriteLine ( "FATAL: vJoy Initialization Failed." ) ;
60+ // Give a moment for the user to see the error if running in a visible console.
61+ Thread . Sleep ( 5000 ) ;
62+ return - 1 ;
63+ }
64+ Console . WriteLine ( "vJoy Initialized Successfully." ) ;
65+
66+ // Now, set up the MMF and Event Handle for IPC.
67+ var commandEvent = new EventWaitHandle ( false , EventResetMode . AutoReset , EventName ) ;
68+ using ( var mmf = MemoryMappedFile . CreateOrOpen ( MapName , MapSize ) )
69+ {
70+ using ( var accessor = mmf . CreateViewAccessor ( ) )
71+ {
72+ Console . WriteLine ( "Daemon ready. Waiting for commands via Shared Memory..." ) ;
73+ while ( true )
74+ {
75+ // Sleep with 0% CPU until AHK signals us.
76+ commandEvent . WaitOne ( ) ;
77+
78+ // Woken up! Read the command string from shared memory.
79+ int stringLength = accessor . ReadInt32 ( 0 ) ;
80+ if ( stringLength > 0 && stringLength < MapSize - 4 )
81+ {
82+ byte [ ] buffer = new byte [ stringLength ] ;
83+ accessor . ReadArray ( 4 , buffer , 0 , stringLength ) ;
84+ string commandLine = Encoding . UTF8 . GetString ( buffer ) ;
85+
86+ // Split the string into an array and pass it to your existing parser.
87+ string [ ] commandArgs = commandLine . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries ) ;
88+ ProcessArgs ( commandArgs ) ;
89+ }
90+ }
91+ }
92+ }
93+ }
94+
95+ // Your original ProcessArgs method is used here, virtually unchanged.
96+ private static void ProcessArgs ( string [ ] args )
97+ {
98+ var commandsToRun = new List < vJoyCommand > ( ) ;
99+ uint currentDeviceId = 1 ;
100+ try {
101+ for ( int i = 0 ; i < args . Length ; i ++ ) {
102+ if ( args [ i ] == "--device" ) { currentDeviceId = uint . Parse ( args [ ++ i ] ) ; continue ; }
103+ if ( args [ i ] == "--axis" || args [ i ] == "--axis-name" ) {
104+ var cmd = new AxisCommand { DeviceId = currentDeviceId } ;
105+ string axisIdentifier = args [ ++ i ] ;
106+ if ( args [ i - 1 ] == "--axis" ) {
107+ uint axisId = uint . Parse ( axisIdentifier ) ;
108+ if ( axisId < 1 || axisId > AxisIdToName . Length - 1 ) throw new ArgumentException ( $ "Invalid axis ID: { axisId } ") ;
109+ cmd . AxisName = AxisIdToName [ axisId ] ;
110+ } else { cmd . AxisName = axisIdentifier ; }
111+ i ++ ;
112+ if ( args [ i ] == "--percent" ) {
113+ int percent = int . Parse ( args [ ++ i ] ) ;
114+ if ( percent < 0 || percent > 100 ) throw new ArgumentException ( "Percentage must be 0-100." ) ;
115+ cmd . Value = ( uint ) Math . Round ( ( percent / 100.0 ) * 32767 ) ;
116+ } else if ( args [ i ] == "--value" ) { cmd . Value = uint . Parse ( args [ ++ i ] ) ; }
117+ commandsToRun . Add ( cmd ) ;
118+ }
119+ else if ( args [ i ] == "--button" ) {
120+ var cmd = new ButtonCommand { DeviceId = currentDeviceId } ;
121+ cmd . ButtonId = uint . Parse ( args [ ++ i ] ) ;
122+ i ++ ;
123+ if ( args [ i ] == "--state" ) {
124+ string stateStr = args [ ++ i ] . ToLower ( ) ;
125+ if ( stateStr == "p" || stateStr == "pressed" ) cmd . IsPressed = true ;
126+ else if ( stateStr == "u" || stateStr == "unpressed" ) cmd . IsPressed = false ;
127+ else throw new ArgumentException ( $ "Invalid state: { stateStr } ") ;
128+ } else if ( args [ i ] == "--value" ) { cmd . IsPressed = ( int . Parse ( args [ ++ i ] ) == 1 ) ; }
129+ commandsToRun . Add ( cmd ) ;
130+ }
131+ }
132+ }
133+ catch ( Exception ex ) { Console . Error . WriteLine ( $ "Error parsing command: '{ string . Join ( " " , args ) } '. Details: { ex . Message } ") ; return ; }
134+ if ( commandsToRun . Count > 0 ) {
135+ var commandsByDevice = new Dictionary < uint , List < vJoyCommand > > ( ) ;
136+ foreach ( var cmd in commandsToRun ) { if ( ! commandsByDevice . ContainsKey ( cmd . DeviceId ) ) { commandsByDevice [ cmd . DeviceId ] = new List < vJoyCommand > ( ) ; } commandsByDevice [ cmd . DeviceId ] . Add ( cmd ) ; }
137+ foreach ( var deviceBatch in commandsByDevice ) { if ( AcquireDevice ( deviceBatch . Key ) ) { foreach ( var cmd in deviceBatch . Value ) { cmd . Execute ( _joystick ! , _hidUsagesType ! ) ; } } }
138+ }
139+ }
140+
141+ // Your original supporting methods are here, completely UNCHANGED.
142+ private static void ShowHelp ( ) { /* Not used in daemon mode, but safe to keep */ }
143+ private static bool AcquireDevice ( uint deviceId ) {
144+ // Using dynamic here means we don't need a compile-time reference.
145+ int status = Convert . ToInt32 ( _joystick ! . GetVJDStatus ( deviceId ) ) ;
146+ if ( status != 1 && status != 0 ) { return false ; }
147+ if ( ! _joystick . AcquireVJD ( deviceId ) ) { return false ; }
148+ return true ;
149+ }
150+ private static bool InitializeVJoy ( ) {
151+ if ( _joystick != null ) return true ;
152+ string vjoyInstallPath ;
153+ try {
154+ using ( var key = Registry . LocalMachine . OpenSubKey ( @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{8E31F76F-74C3-47F1-9550-E041EEDC5FBB}_is1" ) ) {
155+ if ( key ? . GetValue ( "InstallLocation" ) == null ) { Console . Error . WriteLine ( "Error: vJoy not found in registry." ) ; return false ; }
156+ vjoyInstallPath = key . GetValue ( "InstallLocation" ) ! . ToString ( ) ! ;
157+ }
158+ } catch ( Exception ex ) { Console . Error . WriteLine ( $ "Error accessing registry: { ex . Message } ") ; return false ; }
159+ string arch = Environment . Is64BitProcess ? "x64" : "x86" ;
160+ string nativeDllPath = Path . Combine ( vjoyInstallPath , arch , "vJoyInterface.dll" ) ;
161+ string wrapperDllPath = Path . Combine ( vjoyInstallPath , arch , "vJoyInterfaceWrap.dll" ) ;
162+ if ( ! File . Exists ( nativeDllPath ) || ! File . Exists ( wrapperDllPath ) ) { Console . Error . WriteLine ( "Error: vJoy DLLs not found at expected path." ) ; return false ; }
163+ try {
164+ if ( LoadLibrary ( nativeDllPath ) == IntPtr . Zero ) { throw new DllNotFoundException ( $ "Failed to load native DLL. Error: { Marshal . GetLastWin32Error ( ) } ") ; }
165+ _vjoyWrapperAssembly = Assembly . LoadFrom ( wrapperDllPath ) ;
166+ } catch ( Exception ex ) { Console . Error . WriteLine ( $ "Error loading DLLs: { ex . Message } ") ; return false ; }
167+ Type ? vJoyType = _vjoyWrapperAssembly . GetType ( "vJoyInterfaceWrap.vJoy" ) ;
168+ if ( vJoyType == null ) { return false ; }
169+ _joystick = Activator . CreateInstance ( vJoyType ) ;
170+ _hidUsagesType = _vjoyWrapperAssembly . GetType ( "HID_USAGES" ) ;
171+ if ( _hidUsagesType == null ) { return false ; }
172+ return true ;
173+ }
174+ }
175+ }
0 commit comments