Closed MrTimcakes closed 1 year ago
Perhaps the DLL should start a watchdog and timeout the EnumerateDevices
function if it takes more than ~300ms?
Hopefully, as each enumerated device adds to _DeviceInstances
in its own callback instance, if we cancel EnumDevices
early _DeviceInstances
will still be populated with the correct information. 🤞 other devices are processed before we hang. Otherwise, the timeout will need to be in the callback, which will be more difficult to implement.
This is a very difficult issue to debug as it only happens occasionally.
Luckily, when it does happen and EnumerateDevices
hangs, I can slide the Hz slider on my K70 and EnumerateDevices
immediately returns.
_EnumDevicesCallback
Statistics from 54 samples:
min = 4900ns max = 417400ns Range R = 412500ns Count n = 54 sum = 2455500ns Mean x̄ = 45472.222ns Median x˜ = 44050ns mode = 5300, 45200 Standard Deviation = 75339.76ns Variance s2 = 5676079400
Upon further inspection, the delay doesn't occur within _EnumDevicesCallback
Tested when issue is occurring
_EnumDevicesCallback MrT USB Handbreak: 16000ns _EnumDevicesCallback FANATEC CSL Elite Wheel Base PlayStation: 45300ns _EnumDevicesCallback FANATEC CSL Elite Wheel Base PlayStation: 43400ns
EnumDevices: 40192473100ns
Made an attempt to exit EnumDevices
early but _DeviceInstances
isn't populated ðŸ˜
#include <chrono>
#include <type_traits>
#include <future>
#include <thread>
#include <condition_variable>
DeviceInfo* EnumerateDevices(int& deviceCount) {
DEBUGDATA.push_back(L"Started Spoofer");
try {
std::mutex m;
std::condition_variable cv;
DeviceInfo* retValue;
std::thread t([&cv, &retValue, &deviceCount]()
{
retValue = EnumerateDevicesReal(deviceCount);
cv.notify_one();
});
t.detach();
{
std::unique_lock<std::mutex> l(m);
if (cv.wait_for(l, std::chrono::milliseconds(500)) == std::cv_status::timeout)
throw std::runtime_error("Timeout");
}
return retValue;
} catch (std::runtime_error& e) {
DEBUGDATA.push_back(L"EA! " + _DeviceInstances.size());
DEBUGDATA.push_back(std::to_wstring(_DeviceInstances.size()));
return &_DeviceInstances[0];
}
}
// Return a vector of all attached devices
//DeviceInfo* EnumerateDevices(/*[out]*/ int& deviceCount) {
// return run_with_timeout(EnumerateDevicesReal, std::chrono::milliseconds(500), deviceCount);
//}
DeviceInfo* EnumerateDevicesReal(/*[out]*/ int& deviceCount) {
TimeVar t1 = timeNow();
HRESULT hr = E_FAIL;
if (_DirectInput == NULL) { return NULL; } // If DI not ready, return nothing
_DeviceInstances.clear(); // Clear devices
// First fetch all devices
hr = _DirectInput->EnumDevices( // Invoke device enumeration to the _EnumDevicesCallback callback
DI8DEVCLASS_GAMECTRL, // List devices of type GameController
_EnumDevicesCallback, // Callback executed for each device found
NULL, // Passed to callback as optional arg
DIEDFL_ATTACHEDONLY //| DIEDFL_FORCEFEEDBACK
);
// Next update FFB devices (Important this happens after as it modifies existing entries)
hr = _DirectInput->EnumDevices( // Invoke device enumeration to the _EnumDevicesCallback callback
DI8DEVCLASS_GAMECTRL, // List devices of type GameController
_EnumDevicesCallbackFFB, // Callback executed for each device found
NULL, // Passed to callback as optional arg
DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK
);
DEBUGDATA.push_back(L"EnumDevices:" + std::to_wstring(duration(timeNow() - t1)));
if (_DeviceInstances.size() > 0) {
deviceCount = (int)_DeviceInstances.size();
return &_DeviceInstances[0]; // Return 1st element, structure size & deviceCount are used to find next elements
} else {
deviceCount = 0;
}
return NULL;
}
Typically calls to
EnumerateDevices
which calls_DirectInput->EnumDevices
take about 85ms on average on my system. Sometimes, this is orders of magnitude larger taking in the range of minutes instead of milliseconds. This is due to device drivers on the system, in my case a Corsair K70 Keyboard, hanging and not replying to Window's request to query attached devices.The issue has already been discussed on StackOverflow: https://stackoverflow.com/q/10967795