Skip to content

Commit 3f4410c

Browse files
committed
Bug Fixes & Performance Improvements
0 parents  commit 3f4410c

File tree

4 files changed

+206
-0
lines changed

4 files changed

+206
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
bin/
2+
obj/
3+
/packages/
4+
riderModule.iml
5+
/_ReSharper.Caches/

vJoyMMFServer.sln

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vJoyMMFServer", "vJoyMMFServer\vJoyMMFServer.csproj", "{3C0F6880-D0B9-4027-94C0-4741381A28A0}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{3C0F6880-D0B9-4027-94C0-4741381A28A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{3C0F6880-D0B9-4027-94C0-4741381A28A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{3C0F6880-D0B9-4027-94C0-4741381A28A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{3C0F6880-D0B9-4027-94C0-4741381A28A0}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal

vJoyMMFServer/Program.cs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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+
}

vJoyMMFServer/vJoyMMFServer.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

0 commit comments

Comments
 (0)