Improve reliability for finding FTDI devices on Windows

pull/4/head
Paul Hollinsky 2019-01-22 13:16:58 -05:00
parent ae78122cbe
commit d042086c90
3 changed files with 102 additions and 8 deletions

View File

@ -3,11 +3,14 @@
#include <Windows.h>
#include <string>
#include <vector>
namespace icsneo {
class Registry {
public:
static bool EnumerateSubkeys(std::wstring path, std::vector<std::wstring>& subkeys);
// Get string value
static bool Get(std::wstring path, std::wstring key, std::wstring& value);
static bool Get(std::string path, std::string key, std::string& value);

View File

@ -19,6 +19,46 @@ Registry::Key::~Key() {
RegCloseKey(key);
}
bool Registry::EnumerateSubkeys(std::wstring path, std::vector<std::wstring>& subkeys) {
Key regKey(path);
if(!regKey.IsOpen())
return false;
char className[MAX_PATH];
memset(className, 0, sizeof(className));
DWORD classNameLen = MAX_PATH;
DWORD subKeyCount = 0;
DWORD maxSubKeyLen, maxClassStringLen, valueCount, maxValueNameLen, maxValueDataLen, securityDescriptorLen;
FILETIME lastWriteTime;
auto ret = RegQueryInfoKey(
regKey.GetKey(),
className,
&classNameLen,
nullptr,
&subKeyCount,
&maxSubKeyLen,
&maxClassStringLen,
&valueCount,
&maxValueNameLen,
&maxValueDataLen,
&securityDescriptorLen,
&lastWriteTime);
if(ret != ERROR_SUCCESS)
return false;
subkeys.clear();
for(DWORD i = 0; i < subKeyCount; i++) {
DWORD nameLen = MAX_PATH;
char name[MAX_PATH];
memset(name, 0, sizeof(name));
ret = RegEnumKeyEx(regKey.GetKey(), i, name, &nameLen, nullptr, nullptr, nullptr, &lastWriteTime);
if(ret == ERROR_SUCCESS)
subkeys.push_back(converter.from_bytes(name));
}
return true;
}
bool Registry::Get(std::wstring path, std::wstring key, std::wstring& value) {
Key regKey(path);
if(!regKey.IsOpen())

View File

@ -71,17 +71,68 @@ std::vector<neodevice_t> VCP::FindByProduct(int product, std::vector<std::wstrin
}
std::wstringstream oss;
if(!sn || conversionError) {
// This is a device with characters in the serial number
oss << entry.substr(startchar + 1, 6);
}
else {
if(!sn || conversionError)
oss << entry.substr(startchar + 1, 6); // This is a device with characters in the serial number
else
oss << sn;
}
std::string serial = converter.to_bytes(oss.str());
if(serial.find_first_of('\\') != std::string::npos)
continue; // Not sure how this happened but a slash is not valid in the serial
// The serial number should not have a path slash in it. If it does, that means we don't have the real serial.
if(serial.find_first_of('\\') != std::string::npos) {
// The serial number was not in the first serenum key where we expected it.
// We can try to match the ContainerID with the one in ALL_ENUM\USB and get a serial that way
std::wstringstream uess;
uess << ALL_ENUM_REG_KEY << L"\\USB\\" << vss.str() << L'&' << pss.str() << L'\\';
std::wstringstream ciss;
ciss << ALL_ENUM_REG_KEY << entry;
std::wstring containerIDFromEntry, containerIDFromEnum;
if(!Registry::Get(ciss.str(), L"ContainerID", containerIDFromEntry))
continue; // We did not get a container ID. This can happen on Windows XP and before.
if(containerIDFromEntry.empty())
continue; // The container ID was empty?
std::vector<std::wstring> subkeys;
if(!Registry::EnumerateSubkeys(uess.str(), subkeys))
continue; // VID/PID combo was not present at all.
if(subkeys.empty())
continue; // No devices for VID/PID.
std::wstring correctSerial;
for(auto& subkey : subkeys) {
std::wstringstream skss;
skss << uess.str() << L'\\' << subkey;
if(!Registry::Get(skss.str(), L"ContainerID", containerIDFromEnum))
continue;
if(containerIDFromEntry != containerIDFromEnum)
continue;
correctSerial = subkey;
break;
}
if(correctSerial.empty())
continue; // Didn't find the device within the subkeys of the enumeration
sn = 0;
conversionError = false;
try {
sn = std::stoi(correctSerial);
}
catch(...) {
conversionError = true;
}
if(!sn || conversionError) {
// This is a device with characters in the serial number
if(correctSerial.size() != 6)
continue;
serial = converter.to_bytes(correctSerial);
}
else {
std::wstringstream soss;
soss << sn;
serial = converter.to_bytes(soss.str());
}
if(serial.find_first_of('\\') != std::string::npos)
continue;
}
strcpy_s(device.serial, sizeof(device.serial), serial.c_str());
// Serial number is saved, we want the COM port number now