static void enumeratePorts()

in src/main/c/Windows/SerialPort_Windows.c [119:609]


static void enumeratePorts(JNIEnv *env)
{
	// Reset the enumerated flag on all non-open serial ports
	for (int i = 0; i < serialPorts.length; ++i)
		serialPorts.ports[i]->enumerated = (serialPorts.ports[i]->handle != INVALID_HANDLE_VALUE);

	// Enumerate all serial ports present on the current system
	wchar_t *deviceID = NULL;
	DWORD deviceIdLength = 0;
	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)
	{
		HDEVINFO devList = SetupDiGetClassDevsW(&setupClasses[i].guid, NULL, NULL, setupClasses[i].flags);
		if (devList != INVALID_HANDLE_VALUE)
		{
			// Iterate through all devices
			DWORD devInterfaceIndex = 0;
			DEVPROPTYPE devInfoPropType;
			SP_DEVINFO_DATA devInfoData;
			SP_DRVINFO_DATA_W driverInfoData;
			devInfoData.cbSize = sizeof(devInfoData);
			driverInfoData.cbSize = sizeof(driverInfoData);
			while (SetupDiEnumDeviceInfo(devList, devInterfaceIndex++, &devInfoData))
			{
				// Attempt to determine the device's Vendor ID and Product ID
				DWORD deviceIdRequiredLength;
				int vendorID = -1, productID = -1;
				wchar_t *serialNumberString = NULL;
				if (!SetupDiGetDeviceInstanceIdW(devList, &devInfoData, NULL, 0, &deviceIdRequiredLength) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (deviceIdRequiredLength > deviceIdLength))
				{
					wchar_t *newMemory = (wchar_t*)realloc(deviceID, deviceIdRequiredLength * sizeof(wchar_t));
					if (newMemory)
					{
						deviceID = newMemory;
						deviceIdLength = deviceIdRequiredLength;
					}
				}
				if (SetupDiGetDeviceInstanceIdW(devList, &devInfoData, deviceID, deviceIdLength, NULL))
				{
					wchar_t *vendorIdString = wcsstr(deviceID, L"VID_"), *productIdString = wcsstr(deviceID, L"PID_");
					if (vendorIdString && productIdString)
					{
						serialNumberString = (wchar_t*)malloc(128*sizeof(wchar_t));
						vendorID = wcstoul(vendorIdString + 4, NULL, 16);
						productID = wcstoul(productIdString + 4, NULL, 16);
						wchar_t *serialEnd = wcspbrk(productIdString + 9, L"\\\r\n");
						if (serialEnd)
							*serialEnd = L'\0';
						wcscpy_s(serialNumberString, 128, productIdString + 9);
					}
				}

				// Fetch the corresponding COM port for this device
				DWORD comPortLength = 0;
				wchar_t *comPort = NULL, *comPortString = NULL;
				char friendlyNameMemory = 0, portDescriptionMemory = 0;
				HKEY key = SetupDiOpenDevRegKey(devList, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
				if (key != INVALID_HANDLE_VALUE)
				{
					if ((RegQueryValueExW(key, L"PortName", NULL, NULL, NULL, &comPortLength) == ERROR_SUCCESS) && (comPortLength < 32))
					{
						comPortLength += sizeof(wchar_t);
						comPort = (wchar_t*)malloc(comPortLength);
						if (comPort && (RegQueryValueExW(key, L"PortName", NULL, NULL, (LPBYTE)comPort, &comPortLength) == ERROR_SUCCESS))
							comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort;
					}
					RegCloseKey(key);
				}
				if (!comPortString || wcsstr(comPortString, L"LPT"))
				{
					if (comPort)
						free(comPort);
					if (serialNumberString)
						free(serialNumberString);
					continue;
				}

				// Fetch the friendly name for this device
				DWORD friendlyNameLength = 0;
				wchar_t *friendlyNameString = NULL;
				SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &friendlyNameLength);
				if (friendlyNameLength && (friendlyNameLength < 256))
				{
					friendlyNameLength += sizeof(wchar_t);
					friendlyNameString = (wchar_t*)malloc(friendlyNameLength);
					if (!friendlyNameString || !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (BYTE*)friendlyNameString, friendlyNameLength, NULL))
					{
						if (friendlyNameString)
							free(friendlyNameString);
						friendlyNameString = comPortString;
						friendlyNameLength = comPortLength;
					}
					else
					{
						friendlyNameMemory = 1;
						friendlyNameString[(friendlyNameLength / sizeof(wchar_t)) - 1] = 0;
					}
				}
				else
				{
					friendlyNameString = comPortString;
					friendlyNameLength = comPortLength;
				}

				// Fetch the manufacturer of this device
				DWORD manufacturerLength = 0;
				wchar_t *manufacturerString = NULL;
				SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_MFG, NULL, NULL, 0, &manufacturerLength);
				if (manufacturerLength && (manufacturerLength < 256))
				{
					manufacturerLength += sizeof(wchar_t);
					manufacturerString = (wchar_t*)malloc(manufacturerLength);
					if (!manufacturerString || !SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_MFG, NULL, (BYTE*)manufacturerString, manufacturerLength, NULL))
					{
						if (manufacturerString)
						{
							free(manufacturerString);
							manufacturerString = NULL;
						}
					}
				}

				// Fetch the device driver loaded for this device
				wchar_t *driverString = SetupDiGetSelectedDriverW(devList, &devInfoData, &driverInfoData) ? driverInfoData.Description : NULL;

				// Fetch the bus-reported device description
				DWORD portDescriptionLength = 0;
				wchar_t *portDescriptionString = NULL;
				if (SetupDiGetDevicePropertyW && (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, NULL, 0, &portDescriptionLength, 0) || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) && portDescriptionLength && (portDescriptionLength < 256))
				{
					portDescriptionLength += sizeof(wchar_t);
					portDescriptionString = (wchar_t*)malloc(portDescriptionLength);
					if (!portDescriptionString || !SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescriptionString, portDescriptionLength, NULL, 0))
					{
						if (portDescriptionString)
							free(portDescriptionString);
						portDescriptionString = friendlyNameString;
						portDescriptionLength = friendlyNameLength;
					}
					else
					{
						portDescriptionMemory = 1;
						portDescriptionString[(portDescriptionLength / sizeof(wchar_t)) - 1] = 0;
					}
				}
				else
				{
					portDescriptionString = friendlyNameString;
					portDescriptionLength = friendlyNameLength;
				}

				// Fetch the physical location for this device
				DWORD locationLength = 0;
				wchar_t *locationString = NULL;
				unsigned long busNumber = 0, hubNumber = 0, portNumber = 0;
				if (!SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_BUSNUMBER, NULL, (BYTE*)&busNumber, sizeof(busNumber), NULL))
					busNumber = 0;
				if (!SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_ADDRESS, NULL, (BYTE*)&portNumber, sizeof(portNumber), NULL))
					portNumber = 0;
				SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, NULL, 0, &locationLength);
				if (locationLength && (locationLength < 256))
				{
					locationLength += sizeof(wchar_t);
					locationString = (wchar_t*)malloc(locationLength);
					if (locationString && SetupDiGetDeviceRegistryPropertyW(devList, &devInfoData, SPDRP_LOCATION_INFORMATION, NULL, (BYTE*)locationString, locationLength, NULL))
					{
						locationString[(locationLength / sizeof(wchar_t)) - 1] = 0;
						if (wcsstr(locationString, L"Hub_#"))
							hubNumber = _wtoi(wcschr(wcsstr(locationString, L"Hub"), L'#') + 1);
						if (portNumber == -1)
						{
							if (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);
								}
							}
							else if (wcsstr(locationString, L"Port_") && wcschr(locationString, L'-') && wcschr(locationString, L']'))
							{
								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);
									if (end)
										*end = L' ';
								}
								if (hubNumber == -1)
								{
									wchar_t *hubString = wcsrchr(locationString, L'-') + 1;
									if (hubString)
									{
										wchar_t *end = wcschr(hubString, L']');
										if (end)
											*end = L'\0';
										hubNumber = _wtoi(hubString);
										if (end)
											*end = L']';
									}
								}
								if (!serialNumberString && wcschr(locationString, L'[') && wcschr(locationString, L']'))
								{
									serialNumberString = (wchar_t*)malloc(32*sizeof(wchar_t));
									wchar_t *serialBegin = wcschr(locationString, L'[') + 1;
									wchar_t *serialEnd = wcspbrk(serialBegin, L"-]");
									*serialEnd = L'\0';
									wcscpy_s(serialNumberString, 32, serialBegin);
								}
							}
						}
					}
					if (locationString)
						free(locationString);
				}
				locationString = (wchar_t*)malloc(32*sizeof(wchar_t));
				if (locationString)
					_snwprintf_s(locationString, 32, _TRUNCATE, L"%lu-%lu.%lu", busNumber, hubNumber, portNumber);
				else
				{
					free(comPort);
					if (serialNumberString)
						free(serialNumberString);
					if (manufacturerString)
						free(manufacturerString);
					if (friendlyNameMemory)
						free(friendlyNameString);
					if (portDescriptionMemory)
						free(portDescriptionString);
					continue;
				}

				// Check if port is already enumerated
				serialPort *port = fetchPort(&serialPorts, comPortString);
				if (port)
					replaceDetails(port, friendlyNameString, portDescriptionString, locationString, serialNumberString ? serialNumberString : L"Unknown", manufacturerString ? manufacturerString : L"Unknown", driverString ? driverString : L"Unknown", vendorID, productID);
				else
					pushBack(&serialPorts, comPortString, friendlyNameString, portDescriptionString, locationString, serialNumberString ? serialNumberString : L"Unknown", manufacturerString ? manufacturerString : L"Unknown", driverString ? driverString : L"Unknown", vendorID, productID);

				// Clean up memory and reset device info structure
				free(comPort);
				free(locationString);
				if (serialNumberString)
					free(serialNumberString);
				if (manufacturerString)
					free(manufacturerString);
				if (friendlyNameMemory)
					free(friendlyNameString);
				if (portDescriptionMemory)
					free(portDescriptionString);
				devInfoData.cbSize = sizeof(devInfoData);
			}
			SetupDiDestroyDeviceInfoList(devList);
		}
	}

	// Attempt to locate any FTDI-specified port descriptions
	HINSTANCE ftdiLibInstance = LoadLibrary(TEXT("ftd2xx.dll"));
	if (ftdiLibInstance != NULL)
	{
		FT_OpenFunction FT_Open = (FT_OpenFunction)GetProcAddress(ftdiLibInstance, "FT_Open");
		FT_CloseFunction FT_Close = (FT_CloseFunction)GetProcAddress(ftdiLibInstance, "FT_Close");
		FT_CreateDeviceInfoListFunction FT_CreateDeviceInfoList = (FT_CreateDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_CreateDeviceInfoList");
		FT_GetDeviceInfoListFunction FT_GetDeviceInfoList = (FT_GetDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_GetDeviceInfoList");
		FT_EEPROM_ReadFunction FT_EEPROM_Read = (FT_EEPROM_ReadFunction)GetProcAddress(ftdiLibInstance, "FT_EEPROM_Read");
		unsigned char allowOpenForEnumeration = (*env)->GetStaticBooleanField(env, serialCommClass, allowOpenForEnumerationField);
		if (FT_CreateDeviceInfoList && FT_GetDeviceInfoList && FT_EEPROM_Read)
		{
			DWORD numDevs;
			if ((FT_CreateDeviceInfoList(&numDevs) == FT_OK) && (numDevs > 0))
			{
				FT_DEVICE_LIST_INFO_NODE *devInfo = (FT_DEVICE_LIST_INFO_NODE*)malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*numDevs);
				if (devInfo && (FT_GetDeviceInfoList(devInfo, &numDevs) == FT_OK))
				{
					for (int i = 0; i < numDevs; ++i)
					{
						// Determine if the port is currently enumerated and already open
						char isOpen = ((devInfo[i].Flags & FT_FLAGS_OPENED) || (devInfo[i].SerialNumber[0] == 0)) ? 1 : 0;
						if (!isOpen)
							for (int j = 0; j < serialPorts.length; ++j)
								if ((memcmp(serialPorts.ports[j]->ftdiSerialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->ftdiSerialNumber)) == 0) && (serialPorts.ports[j]->handle != INVALID_HANDLE_VALUE))
								{
									serialPorts.ports[j]->enumerated = 1;
									isOpen = 1;
									break;
								}

						// Update the port description if not already open
						const int comPortLength = 16;
						wchar_t *comPort = (wchar_t*)malloc(comPortLength);
						devInfo[i].Description[sizeof(devInfo[i].Description)-1] = 0;
						devInfo[i].SerialNumber[sizeof(devInfo[i].SerialNumber)-1] = 0;
						if (!isOpen && comPort && getPortPathFromSerial(comPort, comPortLength, devInfo[i].SerialNumber))
						{
							// Check if actually connected and present in the port list
							for (int j = 0; j < serialPorts.length; ++j)
								if ((wcscmp(serialPorts.ports[j]->portPath + 4, comPort) == 0) && strlen(devInfo[i].Description))
								{
									// Check whether we are allowed to open the port to complete enumeration
									unsigned char successfullyEnumerated = 0;
									if (allowOpenForEnumeration)
									{
										// Open the port and read its configuration from EEPROM
										FT_HANDLE ftHandle;
										FT_EEPROM_HEADER ftEepromHeader = { .deviceType = devInfo[i].Type };
										char manufacturer[64], manufacturerId[64], description[64], serialNumber[64];
										if (FT_Open(0, &ftHandle) == FT_OK)
										{
											switch (devInfo[i].Type)
											{
												case FT_DEVICE_2232C:
												{
													FT_EEPROM_2232 ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												case FT_DEVICE_232R:
												{
													FT_EEPROM_232R ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												case FT_DEVICE_2232H:
												{
													FT_EEPROM_2232H ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												case FT_DEVICE_4232H:
												{
													FT_EEPROM_4232H ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												case FT_DEVICE_232H:
												{
													FT_EEPROM_232H ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												case FT_DEVICE_X_SERIES:
												{
													FT_EEPROM_X_SERIES ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
												default:
												{
													FT_EEPROM_232B ftDeviceHeader = { .common = ftEepromHeader };
													successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
													break;
												}
											}
											FT_Close(ftHandle);
										}

										// Update port details if enumeration was successful
										if (successfullyEnumerated)
										{
											// Update the port description
											serialPorts.ports[j]->enumerated = 1;
											size_t descLength = 8 + strlen(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, description, -1, serialPorts.ports[j]->portDescription, descLength);
											}

											// Update the port serial number
											size_t serialNumLength = 1 + strlen(serialNumber);
											newMemory = (wchar_t*)realloc(serialPorts.ports[j]->serialNumber, serialNumLength*sizeof(wchar_t));
											if (newMemory)
											{
												serialPorts.ports[j]->serialNumber = newMemory;
												MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, serialNumber, -1, serialPorts.ports[j]->serialNumber, serialNumLength);
											}

											// Update the port manufacturer
											size_t manufacturerLength = 1 + strlen(manufacturer);
											newMemory = (wchar_t*)realloc(serialPorts.ports[j]->manufacturer, manufacturerLength*sizeof(wchar_t));
											if (newMemory)
											{
												serialPorts.ports[j]->manufacturer = newMemory;
												MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, manufacturer, -1, serialPorts.ports[j]->manufacturer, manufacturerLength);
											}
										}
									}

									// Take what we can get if unable to enumerate by opening the port
									if (!successfullyEnumerated)
									{
										// 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);
										}

										// Update the port serial number
										size_t serialNumLength = 1 + strlen(devInfo[i].SerialNumber);
										newMemory = (wchar_t*)realloc(serialPorts.ports[j]->serialNumber, serialNumLength*sizeof(wchar_t));
										if (newMemory)
										{
											serialPorts.ports[j]->serialNumber = newMemory;
											MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].SerialNumber, -1, serialPorts.ports[j]->serialNumber, serialNumLength);
										}
										memcpy(serialPorts.ports[j]->ftdiSerialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->ftdiSerialNumber));
									}
								}
						}
						if (comPort)
							free(comPort);
					}
				}
				if (devInfo)
					free(devInfo);
			}
		}
		FreeLibrary(ftdiLibInstance);
	}

	// Attempt to locate any non-registered virtual serial ports (e.g., from VSPE)
	HKEY key, paramKey;
	DWORD keyType, numValues, maxValueLength, maxComPortLength;
	if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) &&
			(RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, &maxValueLength, &maxComPortLength, NULL, NULL) == ERROR_SUCCESS))
	{
		// Allocate memory
		++maxValueLength;
		++maxComPortLength;
		WCHAR *valueName = (WCHAR*)malloc(maxValueLength*sizeof(WCHAR));
		WCHAR *comPort = (WCHAR*)malloc(maxComPortLength*sizeof(WCHAR));

		// Iterate through all COM ports
		for (DWORD i = 0; i < numValues; ++i)
		{
			// Get serial port name and COM value
			DWORD valueLength = maxValueLength;
			DWORD comPortLength = maxComPortLength;
			memset(valueName, 0, valueLength*sizeof(WCHAR));
			memset(comPort, 0, comPortLength*sizeof(WCHAR));
			if ((RegEnumValueW(key, i, valueName, &valueLength, NULL, &keyType, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS) && (keyType == REG_SZ))
			{
				// Set port name and description
				wchar_t* comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort;
				wchar_t* friendlyNameString = wcsrchr(valueName, L'\\') ? (wcsrchr(valueName, L'\\') + 1) : valueName;

				// Add new SerialComm object to vector if it does not already exist
				serialPort *port = fetchPort(&serialPorts, comPortString);
				if (port)
					port->enumerated = 1;
				else
					pushBack(&serialPorts, comPortString, friendlyNameString, L"Virtual Serial Port", L"X-X.X", L"Unknown", L"Unknown", L"Unknown", -1, -1);
			}
		}

		// Clean up memory
		free(valueName);
		free(comPort);
		RegCloseKey(key);
	}

	// Clean up memory
	if (deviceID)
		free(deviceID);

	// Remove all non-enumerated ports from the serial port listing
	for (int i = 0; i < serialPorts.length; ++i)
		if (!serialPorts.ports[i]->enumerated)
		{
			removePort(&serialPorts, serialPorts.ports[i]);
			i--;
		}
	portsEnumerated = 1;
}