diff --git a/src/main/c/Windows/SerialPort_Windows.c b/src/main/c/Windows/SerialPort_Windows.c index e2b38ec..1f98594 100644 --- a/src/main/c/Windows/SerialPort_Windows.c +++ b/src/main/c/Windows/SerialPort_Windows.c @@ -2,7 +2,7 @@ * SerialPort_Windows.c * * Created on: Feb 25, 2012 - * Last Updated on: Jan 20, 2022 + * Last Updated on: Jan 21, 2022 * Author: Will Hedgecock * * Copyright (C) 2012-2022 Fazecast, Inc. @@ -32,10 +32,11 @@ #include #include #include +#include +#include #include #include #include -#include #include #include #include "ftdi/ftd2xx.h" @@ -75,9 +76,6 @@ jfieldID eventFlagsField; // Runtime-loadable DLL functions typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD); typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD); -typedef int (__stdcall *FT_GetComPortNumberFunction)(FT_HANDLE, LPLONG); -typedef int (__stdcall *FT_OpenFunction)(int, FT_HANDLE*); -typedef int (__stdcall *FT_CloseFunction)(FT_HANDLE); // List of available serial ports serialPortVector serialPorts = { NULL, 0, 0 }; @@ -107,139 +105,149 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP // Enumerate all serial ports present on the current system wchar_t comPort[128]; - HDEVINFO devList = SetupDiGetClassDevsW(&GUID_DEVCLASS_PORTS, NULL, NULL, DIGCF_PRESENT); - if (devList != INVALID_HANDLE_VALUE) + const struct { GUID guid; DWORD flags; } setupClasses[] = { + { .guid = GUID_DEVCLASS_PORTS, .flags = DIGCF_PRESENT }, + { .guid = GUID_DEVCLASS_MODEM, .flags = DIGCF_PRESENT }, + { .guid = GUID_DEVCLASS_MULTIPORTSERIAL, .flags = DIGCF_PRESENT }, + { .guid = GUID_DEVINTERFACE_COMPORT, .flags = DIGCF_PRESENT | DIGCF_DEVICEINTERFACE }, + { .guid = GUID_DEVINTERFACE_MODEM, .flags = DIGCF_PRESENT | DIGCF_DEVICEINTERFACE } + }; + for (int i = 0; i < (sizeof(setupClasses) / sizeof(setupClasses[0])); ++i) { - // Iterate through all devices - DWORD devInterfaceIndex = 0; - DEVPROPTYPE devInfoPropType; - SP_DEVINFO_DATA devInfoData; - devInfoData.cbSize = sizeof(devInfoData); - while (SetupDiEnumDeviceInfo(devList, devInterfaceIndex++, &devInfoData)) + HDEVINFO devList = SetupDiGetClassDevsW(&setupClasses[i].guid, NULL, NULL, setupClasses[i].flags); + if (devList != INVALID_HANDLE_VALUE) { - // Fetch the corresponding COM port for this device - wchar_t *comPortString = NULL; - DWORD comPortLength = sizeof(comPort) / sizeof(wchar_t); - HKEY key = SetupDiOpenDevRegKey(devList, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); - if ((key != INVALID_HANDLE_VALUE) && (RegQueryValueExW(key, L"PortName", NULL, NULL, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS)) - comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; - if (key != INVALID_HANDLE_VALUE) - RegCloseKey(key); - if (!comPortString) - continue; + // Iterate through all devices + DWORD devInterfaceIndex = 0; + DEVPROPTYPE devInfoPropType; + SP_DEVINFO_DATA devInfoData; + devInfoData.cbSize = sizeof(devInfoData); + while (SetupDiEnumDeviceInfo(devList, devInterfaceIndex++, &devInfoData)) + { + // Fetch the corresponding COM port for this device + wchar_t *comPortString = NULL; + DWORD comPortLength = sizeof(comPort) / sizeof(wchar_t); + HKEY key = SetupDiOpenDevRegKey(devList, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + if ((key != INVALID_HANDLE_VALUE) && (RegQueryValueExW(key, L"PortName", NULL, NULL, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS)) + comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; + if (key != INVALID_HANDLE_VALUE) + RegCloseKey(key); + if (!comPortString) + continue; - // Fetch the friendly name for this device - DWORD friendlyNameLength = 0; - wchar_t *friendlyNameString = NULL; - SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_FriendlyName, &devInfoPropType, NULL, 0, &friendlyNameLength, 0); - if (!friendlyNameLength) - SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &friendlyNameLength); - if (friendlyNameLength) - { - friendlyNameString = (wchar_t*)malloc(friendlyNameLength); - if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_FriendlyName, &devInfoPropType, (BYTE*)friendlyNameString, friendlyNameLength, NULL, 0) && - !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (BYTE*)friendlyNameString, friendlyNameLength, NULL)) + // Fetch the friendly name for this device + DWORD friendlyNameLength = 0; + wchar_t *friendlyNameString = NULL; + SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_FriendlyName, &devInfoPropType, NULL, 0, &friendlyNameLength, 0); + if (!friendlyNameLength) + SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &friendlyNameLength); + if (friendlyNameLength) { - friendlyNameLength = comPortLength; - friendlyNameString = (wchar_t*)realloc(friendlyNameString, comPortLength); - wcscpy_s(friendlyNameString, comPortLength / sizeof(wchar_t), comPortString); - } - } - else - { - friendlyNameLength = comPortLength; - friendlyNameString = (wchar_t*)malloc(comPortLength); - wcscpy_s(friendlyNameString, comPortLength / sizeof(wchar_t), comPortString); - } - - // Fetch the bus-reported device description - DWORD portDescriptionLength = 0; - wchar_t *portDescriptionString = NULL; - SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, NULL, 0, &portDescriptionLength, 0); - if (portDescriptionLength) - { - portDescriptionString = (wchar_t*)malloc(portDescriptionLength); - if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescriptionString, portDescriptionLength, NULL, 0)) - { - portDescriptionString = (wchar_t*)realloc(portDescriptionString, friendlyNameLength); - wcscpy_s(portDescriptionString, friendlyNameLength / sizeof(wchar_t), friendlyNameString); - } - } - else - { - portDescriptionString = (wchar_t*)malloc(friendlyNameLength); - wcscpy_s(portDescriptionString, friendlyNameLength / sizeof(wchar_t), friendlyNameString); - } - - // Fetch the physical location for this device - wchar_t *locationString = NULL; - DWORD locationLength = 0, busNumber = -1, hubNumber = -1, portNumber = -1; - if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusNumber, &devInfoPropType, (BYTE*)&busNumber, sizeof(busNumber), NULL, 0) && - !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_BUSNUMBER, NULL, (BYTE*)&busNumber, sizeof(busNumber), NULL)) - busNumber = -1; - if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_Address, &devInfoPropType, (BYTE*)&portNumber, sizeof(portNumber), NULL, 0) && - !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_ADDRESS, NULL, (BYTE*)&portNumber, sizeof(portNumber), NULL)) - portNumber = -1; - SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_LocationInfo, &devInfoPropType, NULL, 0, &locationLength, 0); - if (!locationLength) - SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, NULL, 0, &locationLength); - if (locationLength) - { - locationString = (wchar_t*)malloc(locationLength); - if (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_LocationInfo, &devInfoPropType, (BYTE*)locationString, locationLength, NULL, 0) || - SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, (BYTE*)locationString, locationLength, NULL)) - { - if (wcsstr(locationString, L"Hub")) - hubNumber = _wtoi(wcschr(wcsstr(locationString, L"Hub"), L'#') + 1); - if ((portNumber == -1) && wcsstr(locationString, L"Port")) + friendlyNameString = (wchar_t*)malloc(friendlyNameLength); + if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_FriendlyName, &devInfoPropType, (BYTE*)friendlyNameString, friendlyNameLength, NULL, 0) && + !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (BYTE*)friendlyNameString, friendlyNameLength, NULL)) { - wchar_t *portString = wcschr(wcsstr(locationString, L"Port"), L'#') + 1; - if (portString) - { - wchar_t *end = wcschr(portString, L'.'); - if (end) - *end = L'\0'; - } - portNumber = _wtoi(portString); + friendlyNameLength = comPortLength; + friendlyNameString = (wchar_t*)realloc(friendlyNameString, comPortLength); + wcscpy_s(friendlyNameString, comPortLength / sizeof(wchar_t), comPortString); } } - free(locationString); - } - if (busNumber == -1) - busNumber = 0; - if (hubNumber == -1) - hubNumber = 0; - if (portNumber == -1) - portNumber = 0; - locationString = (wchar_t*)malloc(32*sizeof(wchar_t)); - _snwprintf_s(locationString, 32, 32, L"%d-%d.%d", busNumber, hubNumber, portNumber); - - // Check if port is already enumerated - serialPort *port = fetchPort(&serialPorts, comPortString); - if (port) - { - // See if device has changed locations - port->enumerated = 1; - int oldLength = wcslen(port->portLocation); - int newLength = wcslen(locationString); - if (oldLength != newLength) + else { - port->portLocation = (wchar_t*)realloc(port->portLocation, (1 + newLength) * sizeof(wchar_t)); - wcscpy_s(port->portLocation, 32, locationString); + friendlyNameLength = comPortLength; + friendlyNameString = (wchar_t*)malloc(comPortLength); + wcscpy_s(friendlyNameString, comPortLength / sizeof(wchar_t), comPortString); } - else if (wcscmp(port->portLocation, locationString)) - wcscpy_s(port->portLocation, 32, locationString); - } - else - pushBack(&serialPorts, comPortString, friendlyNameString, portDescriptionString, locationString); - // Clean up memory and reset device info structure - free(locationString); - free(portDescriptionString); - free(friendlyNameString); - devInfoData.cbSize = sizeof(devInfoData); + // Fetch the bus-reported device description + DWORD portDescriptionLength = 0; + wchar_t *portDescriptionString = NULL; + SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, NULL, 0, &portDescriptionLength, 0); + if (portDescriptionLength) + { + portDescriptionString = (wchar_t*)malloc(portDescriptionLength); + if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescriptionString, portDescriptionLength, NULL, 0)) + { + portDescriptionString = (wchar_t*)realloc(portDescriptionString, friendlyNameLength); + wcscpy_s(portDescriptionString, friendlyNameLength / sizeof(wchar_t), friendlyNameString); + } + } + else + { + portDescriptionString = (wchar_t*)malloc(friendlyNameLength); + wcscpy_s(portDescriptionString, friendlyNameLength / sizeof(wchar_t), friendlyNameString); + } + + // Fetch the physical location for this device + wchar_t *locationString = NULL; + DWORD locationLength = 0, busNumber = -1, hubNumber = -1, portNumber = -1; + if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusNumber, &devInfoPropType, (BYTE*)&busNumber, sizeof(busNumber), NULL, 0) && + !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_BUSNUMBER, NULL, (BYTE*)&busNumber, sizeof(busNumber), NULL)) + busNumber = -1; + if (!SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_Address, &devInfoPropType, (BYTE*)&portNumber, sizeof(portNumber), NULL, 0) && + !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_ADDRESS, NULL, (BYTE*)&portNumber, sizeof(portNumber), NULL)) + portNumber = -1; + SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_LocationInfo, &devInfoPropType, NULL, 0, &locationLength, 0); + if (!locationLength) + SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, NULL, 0, &locationLength); + if (locationLength) + { + locationString = (wchar_t*)malloc(locationLength); + if (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_LocationInfo, &devInfoPropType, (BYTE*)locationString, locationLength, NULL, 0) || + SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, (BYTE*)locationString, locationLength, NULL)) + { + if (wcsstr(locationString, L"Hub")) + hubNumber = _wtoi(wcschr(wcsstr(locationString, L"Hub"), L'#') + 1); + if ((portNumber == -1) && wcsstr(locationString, L"Port")) + { + wchar_t *portString = wcschr(wcsstr(locationString, L"Port"), L'#') + 1; + if (portString) + { + wchar_t *end = wcschr(portString, L'.'); + if (end) + *end = L'\0'; + } + portNumber = _wtoi(portString); + } + } + free(locationString); + } + if (busNumber == -1) + busNumber = 0; + if (hubNumber == -1) + hubNumber = 0; + if (portNumber == -1) + portNumber = 0; + locationString = (wchar_t*)malloc(32*sizeof(wchar_t)); + _snwprintf_s(locationString, 32, 32, L"%d-%d.%d", busNumber, hubNumber, portNumber); + + // Check if port is already enumerated + serialPort *port = fetchPort(&serialPorts, comPortString); + if (port) + { + // See if device has changed locations + port->enumerated = 1; + int oldLength = wcslen(port->portLocation); + int newLength = wcslen(locationString); + if (oldLength != newLength) + { + port->portLocation = (wchar_t*)realloc(port->portLocation, (1 + newLength) * sizeof(wchar_t)); + wcscpy_s(port->portLocation, 32, locationString); + } + else if (wcscmp(port->portLocation, locationString)) + wcscpy_s(port->portLocation, 32, locationString); + } + else + pushBack(&serialPorts, comPortString, friendlyNameString, portDescriptionString, locationString); + + // Clean up memory and reset device info structure + free(locationString); + free(portDescriptionString); + free(friendlyNameString); + devInfoData.cbSize = sizeof(devInfoData); + } + SetupDiDestroyDeviceInfoList(devList); } - SetupDiDestroyDeviceInfoList(devList); } // Attempt to locate any FTDI-specified port descriptions @@ -248,10 +256,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP { FT_CreateDeviceInfoListFunction FT_CreateDeviceInfoList = (FT_CreateDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_CreateDeviceInfoList"); FT_GetDeviceInfoListFunction FT_GetDeviceInfoList = (FT_GetDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_GetDeviceInfoList"); - FT_GetComPortNumberFunction FT_GetComPortNumber = (FT_GetComPortNumberFunction)GetProcAddress(ftdiLibInstance, "FT_GetComPortNumber"); - FT_OpenFunction FT_Open = (FT_OpenFunction)GetProcAddress(ftdiLibInstance, "FT_Open"); - FT_CloseFunction FT_Close = (FT_CloseFunction)GetProcAddress(ftdiLibInstance, "FT_Close"); - if (FT_CreateDeviceInfoList && FT_GetDeviceInfoList && FT_GetComPortNumber && FT_Open && FT_Close) + if (FT_CreateDeviceInfoList && FT_GetDeviceInfoList) { DWORD numDevs; if ((FT_CreateDeviceInfoList(&numDevs) == FT_OK) && (numDevs > 0)) @@ -273,30 +278,24 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP } // Update the port description if not already open - if (!isOpen) + if (!isOpen && getPortPathFromSerial(comPort, devInfo[i].SerialNumber)) { - LONG comPortNumber = 0; - if ((FT_Open(i, &devInfo[i].ftHandle) == FT_OK) && (FT_GetComPortNumber(devInfo[i].ftHandle, &comPortNumber) == FT_OK)) - { - // Close port and check if actually connected and present in the port list - FT_Close(devInfo[i].ftHandle); - swprintf(comPort, sizeof(comPort) / sizeof(wchar_t), L"COM%ld", comPortNumber); - for (int j = 0; j < serialPorts.length; ++j) - if (wcscmp(serialPorts.ports[j]->portPath, comPort) == 0) + // Check if actually connected and present in the port list + for (int j = 0; j < serialPorts.length; ++j) + if (wcscmp(serialPorts.ports[j]->portPath, comPort) == 0) + { + // Update the port description + serialPorts.ports[j]->enumerated = 1; + size_t descLength = 8 + strlen(devInfo[i].Description); + wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t)); + if (newMemory) { - // Update the port description - serialPorts.ports[j]->enumerated = 1; - size_t descLength = 8+strlen(devInfo[i].Description); - wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t)); - if (newMemory) - { - serialPorts.ports[j]->portDescription = newMemory; - MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialPorts.ports[j]->portDescription, descLength); - } - memcpy(serialPorts.ports[j]->serialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->serialNumber)); - break; + serialPorts.ports[j]->portDescription = newMemory; + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialPorts.ports[j]->portDescription, descLength); } - } + memcpy(serialPorts.ports[j]->serialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->serialNumber)); + break; + } } } } @@ -444,85 +443,8 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative( return 0; } - // Reduce the port's latency timer to minimum value of 2 - HKEY key, paramKey = 0; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS", 0, KEY_READ, &key) == ERROR_SUCCESS) - { - DWORD index = 0, subkeySize = 255, portNameSize = 16; - wchar_t *subkey = (wchar_t*)malloc(subkeySize*sizeof(wchar_t)), *regPortName = (wchar_t*)malloc(portNameSize*sizeof(wchar_t)); - while (RegEnumKeyExW(key, index++, subkey, &subkeySize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) - { - DWORD latency = 2, oldLatency = 2, oldLatencySize = sizeof(DWORD); - char *subkeyString = (char*)malloc(subkeySize + 2); - memset(subkeyString, 0, subkeySize + 2); - wcstombs(subkeyString, subkey, subkeySize + 1); - subkeySize = 255; - portNameSize = 16; - memset(regPortName, 0, portNameSize * sizeof(wchar_t)); - wcscat_s(subkey, subkeySize, L"\\0000\\Device Parameters"); - if (RegOpenKeyExW(key, subkey, 0, KEY_QUERY_VALUE, ¶mKey) == ERROR_SUCCESS) - { - if ((RegQueryValueExW(paramKey, L"PortName", NULL, NULL, (LPBYTE)regPortName, &portNameSize) == ERROR_SUCCESS) && (wcscmp(port->portPath + 4, regPortName) == 0)) - RegQueryValueExW(paramKey, L"LatencyTimer", NULL, NULL, (LPBYTE)&oldLatency, &oldLatencySize); - RegCloseKey(paramKey); - } - if (oldLatency > latency) - { - if (RegOpenKeyExW(key, subkey, 0, KEY_SET_VALUE, ¶mKey) == ERROR_SUCCESS) - { - RegSetValueExW(paramKey, L"LatencyTimer", 0, REG_DWORD, (LPBYTE)&latency, sizeof(latency)); - RegCloseKey(paramKey); - } - else - { - // Create registry update file - char *workingDirectory = _getcwd(NULL, 0); - wchar_t *workingDirectoryWide = _wgetcwd(NULL, 0); - int workingDirectoryLength = strlen(workingDirectory) + 1; - char *registryFileName = (char*)malloc(workingDirectoryLength + 8); - wchar_t *paramsString = (wchar_t*)malloc((workingDirectoryLength + 11) * sizeof(wchar_t)); - sprintf(registryFileName, "%s\\del.reg", workingDirectory); - swprintf(paramsString, workingDirectoryLength + 11, L"/s %s\\del.reg", workingDirectoryWide); - FILE *registryFile = fopen(registryFileName, "wb"); - if (registryFile) - { - fprintf(registryFile, "Windows Registry Editor Version 5.00\n\n"); - fprintf(registryFile, "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS\\%s\\0000\\Device Parameters]\n", subkeyString); - fprintf(registryFile, "\"LatencyTimer\"=dword:00000002\n\n"); - fclose(registryFile); - } - - // Launch a new administrative process to update the registry value - SHELLEXECUTEINFOW shExInfo = { 0 }; - shExInfo.cbSize = sizeof(shExInfo); - shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - shExInfo.hwnd = NULL; - shExInfo.lpVerb = L"runas"; - shExInfo.lpFile = L"C:\\Windows\\regedit.exe"; - shExInfo.lpParameters = paramsString; - shExInfo.lpDirectory = NULL; - shExInfo.nShow = SW_SHOW; - shExInfo.hInstApp = 0; - if (ShellExecuteExW(&shExInfo)) - { - WaitForSingleObject(shExInfo.hProcess, INFINITE); - CloseHandle(shExInfo.hProcess); - } - - // Delete the registry update file - remove(registryFileName); - free(workingDirectoryWide); - free(workingDirectory); - free(registryFileName); - free(paramsString); - } - } - free(subkeyString); - } - RegCloseKey(key); - free(regPortName); - free(subkey); - } + // Reduce the port's latency to its minimum value + reduceLatencyToMinimum(port->portPath + 4); // Try to open the serial port with read/write access if ((port->handle = CreateFileW(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL)) != INVALID_HANDLE_VALUE) diff --git a/src/main/c/Windows/WindowsHelperFunctions.c b/src/main/c/Windows/WindowsHelperFunctions.c index 7e4ef04..b0cb8b9 100644 --- a/src/main/c/Windows/WindowsHelperFunctions.c +++ b/src/main/c/Windows/WindowsHelperFunctions.c @@ -2,7 +2,7 @@ * WindowsHelperFunctions.c * * Created on: May 05, 2015 - * Last Updated on: Jan 06, 2022 + * Last Updated on: Jan 22, 2022 * Author: Will Hedgecock * * Copyright (C) 2012-2022 Fazecast, Inc. @@ -24,6 +24,13 @@ */ #ifdef _WIN32 +#define WINVER _WIN32_WINNT_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#define NTDDI_VERSION NTDDI_VISTA +#define WIN32_LEAN_AND_MEAN +#include +#include +#include #include #include #include "WindowsHelperFunctions.h" @@ -100,4 +107,124 @@ void removePort(serialPortVector* vector, serialPort* port) free(port); } +// Windows-specific functionality +void reduceLatencyToMinimum(const wchar_t* portName) +{ + // Search for this port in all FTDI enumerated ports + HKEY key, paramKey = 0; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS", 0, KEY_READ, &key) == ERROR_SUCCESS) + { + DWORD index = 0, subkeySize = 255, portNameSize = 16; + wchar_t *subkey = (wchar_t*)malloc(subkeySize*sizeof(wchar_t)), *regPortName = (wchar_t*)malloc(portNameSize*sizeof(wchar_t)); + while (RegEnumKeyExW(key, index++, subkey, &subkeySize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + DWORD latency = 2, oldLatency = 2, oldLatencySize = sizeof(DWORD); + char *subkeyString = (char*)malloc(subkeySize + 2); + memset(subkeyString, 0, subkeySize + 2); + wcstombs(subkeyString, subkey, subkeySize + 1); + subkeySize = 255; + portNameSize = 16; + memset(regPortName, 0, portNameSize * sizeof(wchar_t)); + wcscat_s(subkey, subkeySize, L"\\0000\\Device Parameters"); + if (RegOpenKeyExW(key, subkey, 0, KEY_QUERY_VALUE, ¶mKey) == ERROR_SUCCESS) + { + if ((RegQueryValueExW(paramKey, L"PortName", NULL, NULL, (LPBYTE)regPortName, &portNameSize) == ERROR_SUCCESS) && (wcscmp(portName, regPortName) == 0)) + RegQueryValueExW(paramKey, L"LatencyTimer", NULL, NULL, (LPBYTE)&oldLatency, &oldLatencySize); + RegCloseKey(paramKey); + } + if (oldLatency > latency) + { + if (RegOpenKeyExW(key, subkey, 0, KEY_SET_VALUE, ¶mKey) == ERROR_SUCCESS) + { + RegSetValueExW(paramKey, L"LatencyTimer", 0, REG_DWORD, (LPBYTE)&latency, sizeof(latency)); + RegCloseKey(paramKey); + } + else + { + // Create registry update file + char *workingDirectory = _getcwd(NULL, 0); + wchar_t *workingDirectoryWide = _wgetcwd(NULL, 0); + int workingDirectoryLength = strlen(workingDirectory) + 1; + char *registryFileName = (char*)malloc(workingDirectoryLength + 8); + wchar_t *paramsString = (wchar_t*)malloc((workingDirectoryLength + 11) * sizeof(wchar_t)); + sprintf(registryFileName, "%s\\del.reg", workingDirectory); + swprintf(paramsString, workingDirectoryLength + 11, L"/s %s\\del.reg", workingDirectoryWide); + FILE *registryFile = fopen(registryFileName, "wb"); + if (registryFile) + { + fprintf(registryFile, "Windows Registry Editor Version 5.00\n\n"); + fprintf(registryFile, "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS\\%s\\0000\\Device Parameters]\n", subkeyString); + fprintf(registryFile, "\"LatencyTimer\"=dword:00000002\n\n"); + fclose(registryFile); + } + + // Launch a new administrative process to update the registry value + SHELLEXECUTEINFOW shExInfo = { 0 }; + shExInfo.cbSize = sizeof(shExInfo); + shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + shExInfo.hwnd = NULL; + shExInfo.lpVerb = L"runas"; + shExInfo.lpFile = L"C:\\Windows\\regedit.exe"; + shExInfo.lpParameters = paramsString; + shExInfo.lpDirectory = NULL; + shExInfo.nShow = SW_SHOW; + shExInfo.hInstApp = 0; + if (ShellExecuteExW(&shExInfo)) + { + WaitForSingleObject(shExInfo.hProcess, INFINITE); + CloseHandle(shExInfo.hProcess); + } + + // Delete the registry update file + remove(registryFileName); + free(workingDirectoryWide); + free(workingDirectory); + free(registryFileName); + free(paramsString); + } + } + free(subkeyString); + } + RegCloseKey(key); + free(regPortName); + free(subkey); + } +} + +int getPortPathFromSerial(wchar_t* portPath, const char* serialNumber) +{ + // Search for this port in all FTDI enumerated ports + int found = 0; + HKEY key, paramKey = 0; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS", 0, KEY_READ, &key) == ERROR_SUCCESS) + { + DWORD index = 0, subkeySize = 255, portNameSize = 16; + wchar_t *subkey = (wchar_t*)malloc(subkeySize * sizeof(wchar_t)); + while (RegEnumKeyExW(key, index++, subkey, &subkeySize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + // Determine if this device matches the specified serial number + char *subkeyString = (char*)malloc(subkeySize + 2); + memset(subkeyString, 0, subkeySize + 2); + wcstombs(subkeyString, subkey, subkeySize + 1); + subkeySize = 255; + if (strstr(subkeyString, serialNumber)) + { + portNameSize = 16; + memset(portPath, 0, portNameSize * sizeof(wchar_t)); + wcscat_s(subkey, subkeySize, L"\\0000\\Device Parameters"); + if (RegOpenKeyExW(key, subkey, 0, KEY_QUERY_VALUE, ¶mKey) == ERROR_SUCCESS) + { + if (RegQueryValueExW(paramKey, L"PortName", NULL, NULL, (LPBYTE)portPath, &portNameSize) == ERROR_SUCCESS) + found = 1; + RegCloseKey(paramKey); + } + } + free(subkeyString); + } + RegCloseKey(key); + free(subkey); + } + return found; +} + #endif diff --git a/src/main/c/Windows/WindowsHelperFunctions.h b/src/main/c/Windows/WindowsHelperFunctions.h index f50edff..e88a983 100644 --- a/src/main/c/Windows/WindowsHelperFunctions.h +++ b/src/main/c/Windows/WindowsHelperFunctions.h @@ -2,10 +2,10 @@ * WindowsHelperFunctions.h * * Created on: May 05, 2015 - * Last Updated on: Dec 16, 2021 + * Last Updated on: Jan 21, 2022 * Author: Will Hedgecock * - * Copyright (C) 2012-2021 Fazecast, Inc. + * Copyright (C) 2012-2022 Fazecast, Inc. * * This file is part of jSerialComm. * @@ -50,4 +50,8 @@ serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t serialPort* fetchPort(serialPortVector* vector, const wchar_t* key); void removePort(serialPortVector* vector, serialPort* port); +// Windows-specific functionality +void reduceLatencyToMinimum(const wchar_t* portName); +int getPortPathFromSerial(wchar_t* portPath, const char* serialNumber); + #endif // #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__ diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index 5f4135c..653745e 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -561,6 +561,11 @@ public final class SerialPort *

* All serial port parameters or timeouts can be changed at any time before or after the port has been opened. *

+ * Note on Windows using an FTDI device: The first time this method is called, you may be prompted to allow elevated privileges + * so that the driver latency can be correctly specified. This should only be necessary the first time you use a new FTDI device. + * Declining the elevated privileges will not affect the ability of the serial port to be accessed; however, read/write timing + * may not be as expected. + *

* Note that calling this method on an already opened port will simply reconfigure the port parameters. * * @param safetySleepTime The number of milliseconds to sleep before opening the port in case of frequent closing/openings. @@ -631,6 +636,11 @@ public final class SerialPort *

* All serial port parameters or timeouts can be changed at any time before or after the port has been opened. *

+ * Note on Windows using an FTDI device: The first time this method is called, you may be prompted to allow elevated privileges + * so that the driver latency can be correctly specified. This should only be necessary the first time you use a new FTDI device. + * Declining the elevated privileges will not affect the ability of the serial port to be accessed; however, read/write timing + * may not be as expected. + *

* Note that calling this method on an already opened port will simply reconfigure the port parameters. * * @param safetySleepTime The number of milliseconds to sleep before opening the port in case of frequent closing/openings. @@ -645,6 +655,11 @@ public final class SerialPort *

* All serial port parameters or timeouts can be changed at any time before or after the port has been opened. *

+ * Note on Windows using an FTDI device: The first time this method is called, you may be prompted to allow elevated privileges + * so that the driver latency can be correctly specified. This should only be necessary the first time you use a new FTDI device. + * Declining the elevated privileges will not affect the ability of the serial port to be accessed; however, read/write timing + * may not be as expected. + *

* Note that calling this method on an already opened port will simply reconfigure the port parameters. * * @return Whether the port was successfully opened with a valid configuration. diff --git a/src/main/resources/Windows/aarch64/jSerialComm.dll b/src/main/resources/Windows/aarch64/jSerialComm.dll index ff68add..c5c8ccd 100644 Binary files a/src/main/resources/Windows/aarch64/jSerialComm.dll and b/src/main/resources/Windows/aarch64/jSerialComm.dll differ diff --git a/src/main/resources/Windows/armv7/jSerialComm.dll b/src/main/resources/Windows/armv7/jSerialComm.dll index c6c5545..78b51a9 100644 Binary files a/src/main/resources/Windows/armv7/jSerialComm.dll and b/src/main/resources/Windows/armv7/jSerialComm.dll differ diff --git a/src/main/resources/Windows/x86/jSerialComm.dll b/src/main/resources/Windows/x86/jSerialComm.dll index 2b1ff89..f81271d 100644 Binary files a/src/main/resources/Windows/x86/jSerialComm.dll and b/src/main/resources/Windows/x86/jSerialComm.dll differ diff --git a/src/main/resources/Windows/x86_64/jSerialComm.dll b/src/main/resources/Windows/x86_64/jSerialComm.dll index 9d568fc..ccd2758 100644 Binary files a/src/main/resources/Windows/x86_64/jSerialComm.dll and b/src/main/resources/Windows/x86_64/jSerialComm.dll differ