Skip to content

Commit 033c147

Browse files
hifihedgehogclaude
andcommitted
Remove dead code, fix MIDI resource leak, add defensive guards
Dead code removed: - XInputState struct (GamepadTypes.cs) — vestigial from XInput removal - SetAxis/SetBtn/SetDiscPov P/Invoke decls (VJoyVirtualController.cs) — never called - _preservedVJoyNodes field (InputService.cs) — written but never read - 6 unused DirectInput effect GUIDs (DeviceEffectItem.cs) - Unused ObjectAspect/EffectParameterFlags enum values (InputTypes.cs) - Stale TrimDeviceNodes comment (VJoyVirtualController.cs) Bugs fixed: - MIDI Connect() now uses try/catch to clean up _session/_virtualDevice/_connection on any exception path (not just null returns) - CreateVirtualController catch block now calls vc?.Dispose() to prevent leaking partially-initialized controllers - HidHide QueryDosDevice: guard against Array.IndexOf returning -1 Stale comments fixed: - MapTo range 0–3 → 0–15 in UserSetting.cs and Step3 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent aa1200d commit 033c147

File tree

10 files changed

+29
-90
lines changed

10 files changed

+29
-90
lines changed

PadForge.App/Common/HidHideController.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ private static string ToDosDevicePath(string filePath)
451451
if (result == 0) return null;
452452

453453
// QueryDosDevice returns a multi-SZ; take the first entry.
454-
string dosDevice = new string(buffer, 0, Array.IndexOf(buffer, '\0'));
454+
int nullIdx = Array.IndexOf(buffer, '\0');
455+
if (nullIdx < 0) return null;
456+
string dosDevice = new string(buffer, 0, nullIdx);
455457

456458
// Build full DOS path: \Device\HarddiskVolumeN + \rest\of\path
457459
string relativePath = fullPath.Substring(drive.Length);

PadForge.App/Common/Input/InputManager.Step3.UpdateOutputStates.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public partial class InputManager
1111
// Maps each device's CustomInputState to a Gamepad struct
1212
// based on the PadSetting mapping rules configured for that device.
1313
//
14-
// Each UserSetting links a device (InstanceGuid) to a pad slot (MapTo 0–3)
14+
// Each UserSetting links a device (InstanceGuid) to a pad slot (MapTo 0–15)
1515
// and references a PadSetting that contains the mapping rules.
1616
//
1717
// PadSetting string fields like "ButtonA", "LeftThumbAxisX", etc. contain

PadForge.App/Common/Input/InputManager.Step5.VirtualDevices.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,14 +672,15 @@ private IVirtualController CreateVirtualController(int padIndex)
672672
&& _vigemClient == null)
673673
return null;
674674

675+
IVirtualController vc = null;
675676
try
676677
{
677678
// Snapshot XInput slot mask BEFORE connecting (Xbox 360 only).
678679
uint maskBefore = 0;
679680
if (controllerType == VirtualControllerType.Xbox360)
680681
maskBefore = GetXInputConnectedSlotMask();
681682

682-
IVirtualController vc = controllerType switch
683+
vc = controllerType switch
683684
{
684685
VirtualControllerType.DualShock4 => new DS4VirtualController(_vigemClient),
685686
VirtualControllerType.VJoy => CreateVJoyController(),
@@ -720,6 +721,7 @@ private IVirtualController CreateVirtualController(int padIndex)
720721
}
721722
catch (Exception ex)
722723
{
724+
vc?.Dispose();
723725
RaiseError($"Failed to create {SlotControllerTypes[padIndex]} virtual controller for pad {padIndex}", ex);
724726
return null;
725727
}

PadForge.App/Common/Input/MidiVirtualController.cs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,31 +104,32 @@ public void Connect()
104104
if (_session == null)
105105
throw new InvalidOperationException("Failed to create MIDI session.");
106106

107-
_virtualDevice = MidiVirtualDeviceManager.CreateVirtualDevice(config);
108-
if (_virtualDevice == null)
107+
try
109108
{
110-
_session.Dispose();
111-
_session = null;
112-
throw new InvalidOperationException("Failed to create virtual MIDI device.");
113-
}
109+
_virtualDevice = MidiVirtualDeviceManager.CreateVirtualDevice(config);
110+
if (_virtualDevice == null)
111+
throw new InvalidOperationException("Failed to create virtual MIDI device.");
114112

115-
_virtualDevice.SuppressHandledMessages = true;
113+
_virtualDevice.SuppressHandledMessages = true;
116114

117-
_connection = _session.CreateEndpointConnection(_virtualDevice.DeviceEndpointDeviceId);
118-
if (_connection == null)
119-
{
120-
_session.Dispose();
121-
_session = null;
122-
throw new InvalidOperationException("Failed to create MIDI endpoint connection.");
123-
}
115+
_connection = _session.CreateEndpointConnection(_virtualDevice.DeviceEndpointDeviceId);
116+
if (_connection == null)
117+
throw new InvalidOperationException("Failed to create MIDI endpoint connection.");
124118

125-
_connection.AddMessageProcessingPlugin(_virtualDevice);
119+
_connection.AddMessageProcessingPlugin(_virtualDevice);
126120

127-
if (!_connection.Open())
121+
if (!_connection.Open())
122+
throw new InvalidOperationException("Failed to open MIDI endpoint connection.");
123+
}
124+
catch
128125
{
129-
_session.Dispose();
126+
if (_connection != null && _session != null)
127+
_session.DisconnectEndpointConnection(_connection.ConnectionId);
128+
_connection = null;
129+
_virtualDevice = null;
130+
_session?.Dispose();
130131
_session = null;
131-
throw new InvalidOperationException("Failed to open MIDI endpoint connection.");
132+
throw;
132133
}
133134

134135
_connected = true;

PadForge.App/Common/Input/VJoyVirtualController.cs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,6 @@ public void Dispose()
444444
Disconnect();
445445
}
446446

447-
// TrimDeviceNodes removed — single-node model means there's always
448-
// exactly 0 or 1 device nodes. Scaling is done via registry descriptors.
449-
450447
private int _submitCallCount;
451448
private int _submitFailCount;
452449

@@ -2633,20 +2630,6 @@ internal static class VJoyNative
26332630
[return: MarshalAs(UnmanagedType.Bool)]
26342631
public static extern bool UpdateVJD(uint rID, ref JoystickPositionV2 pData);
26352632

2636-
// ── Individual axis/button/POV setters ──
2637-
2638-
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
2639-
[return: MarshalAs(UnmanagedType.Bool)]
2640-
public static extern bool SetAxis(int value, uint rID, uint axis);
2641-
2642-
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
2643-
[return: MarshalAs(UnmanagedType.Bool)]
2644-
public static extern bool SetBtn([MarshalAs(UnmanagedType.Bool)] bool value, uint rID, byte nBtn);
2645-
2646-
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
2647-
[return: MarshalAs(UnmanagedType.Bool)]
2648-
public static extern bool SetDiscPov(int value, uint rID, byte nPov);
2649-
26502633
// HID Usage IDs for axes (Generic Desktop page 0x01)
26512634
public const uint HID_USAGE_X = 0x30;
26522635
public const uint HID_USAGE_Y = 0x31;

PadForge.App/Services/InputService.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ public class InputService : IDisposable
5151
private InputHookManager _hookManager;
5252
private SettingsService _settingsService;
5353
private bool _disposed;
54-
private bool _preservedVJoyNodes;
5554

5655
/// <summary>
5756
/// Whether the Devices page is currently visible.
@@ -134,8 +133,6 @@ public void Start()
134133
// 10+ second remove+recreate cycle on every normal restart (especially on
135134
// Win11 builds where pnputil /remove-device returns 3010 and scan-devices
136135
// takes ~10 seconds to clean up ghost PDOs).
137-
_preservedVJoyNodes = false;
138-
139136
// Create engine with the configured polling interval.
140137
_inputManager = new InputManager();
141138
_inputManager.PollingIntervalMs = _mainVm.Settings.PollingRateMs;
@@ -273,7 +270,6 @@ public void Stop(bool preserveVJoyNodes = false)
273270
row.IsOnline = false;
274271
_mainVm.Devices.RefreshCounts();
275272

276-
_preservedVJoyNodes = preserveVJoyNodes;
277273
if (!preserveVJoyNodes)
278274
{
279275
// Remove vJoy device nodes so dormant devices don't appear in

PadForge.Engine/Common/DeviceEffectItem.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ public class DeviceEffectItem
1919
/// <summary>GUID_ConstantForce — {13541C20-8E33-11D0-9AD0-00A0C9A06E35}</summary>
2020
public static readonly Guid ConstantForce = new Guid("13541C20-8E33-11D0-9AD0-00A0C9A06E35");
2121

22-
/// <summary>GUID_RampForce — {13541C21-8E33-11D0-9AD0-00A0C9A06E35}</summary>
23-
public static readonly Guid RampForce = new Guid("13541C21-8E33-11D0-9AD0-00A0C9A06E35");
24-
2522
/// <summary>GUID_Square — {13541C22-8E33-11D0-9AD0-00A0C9A06E35}</summary>
2623
public static readonly Guid Square = new Guid("13541C22-8E33-11D0-9AD0-00A0C9A06E35");
2724

@@ -31,27 +28,12 @@ public class DeviceEffectItem
3128
/// <summary>GUID_Triangle — {13541C24-8E33-11D0-9AD0-00A0C9A06E35}</summary>
3229
public static readonly Guid Triangle = new Guid("13541C24-8E33-11D0-9AD0-00A0C9A06E35");
3330

34-
/// <summary>GUID_SawtoothUp — {13541C25-8E33-11D0-9AD0-00A0C9A06E35}</summary>
35-
public static readonly Guid SawtoothUp = new Guid("13541C25-8E33-11D0-9AD0-00A0C9A06E35");
36-
37-
/// <summary>GUID_SawtoothDown — {13541C26-8E33-11D0-9AD0-00A0C9A06E35}</summary>
38-
public static readonly Guid SawtoothDown = new Guid("13541C26-8E33-11D0-9AD0-00A0C9A06E35");
39-
4031
/// <summary>GUID_Spring — {13541C27-8E33-11D0-9AD0-00A0C9A06E35}</summary>
4132
public static readonly Guid Spring = new Guid("13541C27-8E33-11D0-9AD0-00A0C9A06E35");
4233

4334
/// <summary>GUID_Damper — {13541C28-8E33-11D0-9AD0-00A0C9A06E35}</summary>
4435
public static readonly Guid Damper = new Guid("13541C28-8E33-11D0-9AD0-00A0C9A06E35");
4536

46-
/// <summary>GUID_Inertia — {13541C29-8E33-11D0-9AD0-00A0C9A06E35}</summary>
47-
public static readonly Guid Inertia = new Guid("13541C29-8E33-11D0-9AD0-00A0C9A06E35");
48-
49-
/// <summary>GUID_Friction — {13541C2A-8E33-11D0-9AD0-00A0C9A06E35}</summary>
50-
public static readonly Guid Friction = new Guid("13541C2A-8E33-11D0-9AD0-00A0C9A06E35");
51-
52-
/// <summary>GUID_CustomForce — {13541C2B-8E33-11D0-9AD0-00A0C9A06E35}</summary>
53-
public static readonly Guid CustomForce = new Guid("13541C2B-8E33-11D0-9AD0-00A0C9A06E35");
54-
5537
/// <summary>Synthetic GUID for SDL rumble (no DirectInput equivalent).</summary>
5638
public static readonly Guid SdlRumble = new Guid("53444C52-554D-424C-0000-000000000000"); // "SDLRUMBL"
5739

PadForge.Engine/Common/GamepadTypes.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,6 @@ public void Clear()
5959
}
6060
}
6161

62-
/// <summary>
63-
/// Represents the raw XInput state as returned by XInputGetStateEx.
64-
/// Layout matches XINPUT_STATE (packet number + Gamepad).
65-
/// </summary>
66-
public struct XInputState
67-
{
68-
public uint PacketNumber;
69-
public Gamepad Gamepad;
70-
}
71-
7262
/// <summary>
7363
/// Raw vJoy output state for custom (non-gamepad) configurations.
7464
/// Bypasses the fixed Gamepad struct to support arbitrary axis/button/POV counts.

PadForge.Engine/Common/InputTypes.cs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ public enum DeviceObjectTypeFlags : int
3434
[Flags]
3535
public enum ObjectAspect : int
3636
{
37-
Position = 0x100,
38-
Velocity = 0x200,
39-
Acceleration = 0x300,
40-
Force = 0x400
37+
Position = 0x100
4138
}
4239

4340
// ─────────────────────────────────────────────────────────────────
@@ -49,21 +46,7 @@ public enum ObjectAspect : int
4946
[Flags]
5047
public enum EffectParameterFlags : int
5148
{
52-
None = 0,
53-
Duration = 1,
54-
SamplePeriod = 2,
55-
Gain = 4,
56-
TriggerButton = 8,
57-
TriggerRepeatInterval = 16,
58-
Axes = 32,
59-
Direction = 64,
60-
Envelope = 128,
61-
TypeSpecificParameters = 256,
62-
StartDelay = 512,
63-
AllParameters = 1023, // 0x3FF
64-
Start = 536870912, // 0x20000000
65-
NoRestart = 1073741824, // 0x40000000
66-
NoDownload = unchecked((int)0x80000000)
49+
None = 0
6750
}
6851

6952
// ─────────────────────────────────────────────────────────────────

PadForge.Engine/Data/UserSetting.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class UserSetting : INotifyPropertyChanged
5353
private int _mapTo = -1;
5454

5555
/// <summary>
56-
/// Virtual controller slot index this device is mapped to (0–3).
56+
/// Virtual controller slot index this device is mapped to (0–15).
5757
/// A value of -1 means the device is not mapped to any slot.
5858
/// </summary>
5959
[XmlElement]

0 commit comments

Comments
 (0)