diff --git a/src/main/c/Windows/SerialPort_Windows.c b/src/main/c/Windows/SerialPort_Windows.c index d39e60c..3ec5ae1 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 25, 2022 + * Last Updated on: Jan 28, 2022 * Author: Will Hedgecock * * Copyright (C) 2012-2022 Fazecast, Inc. @@ -63,6 +63,7 @@ jfieldID parityField; jfieldID flowControlField; jfieldID sendDeviceQueueSizeField; jfieldID receiveDeviceQueueSizeField; +jfieldID requestElevatedPermissionsField; jfieldID rs485ModeField; jfieldID rs485DelayBeforeField; jfieldID rs485DelayAfterField; @@ -269,7 +270,7 @@ static void enumeratePorts(void) 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) ? 1 : 0; + char isOpen = ((devInfo[i].Flags & FT_FLAGS_OPENED) || !strlen(devInfo[i].SerialNumber)) ? 1 : 0; if (!isOpen) for (int j = 0; j < serialPorts.length; ++j) if ((memcmp(serialPorts.ports[j]->serialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->serialNumber)) == 0) && (serialPorts.ports[j]->handle != INVALID_HANDLE_VALUE)) @@ -284,7 +285,7 @@ static void enumeratePorts(void) { // 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) + if ((wcscmp(serialPorts.ports[j]->portPath + 4, comPort) == 0) && strlen(devInfo[i].Description)) { // Update the port description serialPorts.ports[j]->enumerated = 1; @@ -331,9 +332,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP // Create new SerialComm object containing the enumerated values jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor); if (checkJniError(env, __LINE__ - 1)) return arrayObject; - wcscpy_s(comPort, sizeof(comPort) / sizeof(wchar_t), L"\\\\.\\"); - wcscat_s(comPort, sizeof(comPort) / sizeof(wchar_t), serialPorts.ports[i]->portPath); - (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)comPort, wcslen(comPort))); + (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portPath, wcslen(serialPorts.ports[i]->portPath))); if (checkJniError(env, __LINE__ - 1)) return arrayObject; (*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->friendlyName, wcslen(serialPorts.ports[i]->friendlyName))); if (checkJniError(env, __LINE__ - 1)) return arrayObject; @@ -393,6 +392,8 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar if (checkJniError(env, __LINE__ - 1)) return; receiveDeviceQueueSizeField = (*env)->GetFieldID(env, serialCommClass, "receiveDeviceQueueSize", "I"); if (checkJniError(env, __LINE__ - 1)) return; + requestElevatedPermissionsField = (*env)->GetFieldID(env, serialCommClass, "requestElevatedPermissions", "Z"); + if (checkJniError(env, __LINE__ - 1)) return; rs485ModeField = (*env)->GetFieldID(env, serialCommClass, "rs485Mode", "Z"); if (checkJniError(env, __LINE__ - 1)) return; rs485DelayBeforeField = (*env)->GetFieldID(env, serialCommClass, "rs485DelayBefore", "I"); @@ -464,6 +465,8 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative( if (checkJniError(env, __LINE__ - 1)) return 0; const wchar_t *portName = (wchar_t*)(*env)->GetStringChars(env, portNameJString, NULL); if (checkJniError(env, __LINE__ - 1)) return 0; + unsigned char requestElevatedPermissions = (*env)->GetBooleanField(env, obj, requestElevatedPermissionsField); + if (checkJniError(env, __LINE__ - 1)) return 0; unsigned char disableAutoConfig = (*env)->GetBooleanField(env, obj, disableConfigField); if (checkJniError(env, __LINE__ - 1)) return 0; unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField); @@ -486,7 +489,7 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative( } // Reduce the port's latency to its minimum value - reduceLatencyToMinimum(port->portPath + 4); + reduceLatencyToMinimum(portName + 4, requestElevatedPermissions); // 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 64fff1b..3840d17 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 25, 2022 + * Last Updated on: Jan 28, 2022 * Author: Will Hedgecock * * Copyright (C) 2012-2022 Fazecast, Inc. @@ -39,6 +39,7 @@ serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t* friendlyName, const wchar_t* description, const wchar_t* location) { // Allocate memory for the new SerialPort storage structure + unsigned char containsSlashes = ((key[0] == L'\\') && (key[1] == L'\\') && (key[2] == L'.') && (key[3] == L'\\')); if (vector->capacity == vector->length) { serialPort** newArray = (serialPort**)realloc(vector->ports, ++vector->capacity * sizeof(serialPort*)); @@ -60,13 +61,19 @@ serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t memset(port, 0, sizeof(serialPort)); port->handle = (void*)-1; port->enumerated = 1; - port->portPath = (wchar_t*)malloc((wcslen(key)+1)*sizeof(wchar_t)); + port->portPath = (wchar_t*)malloc((wcslen(key)+(containsSlashes ? 1 : 5))*sizeof(wchar_t)); port->portLocation = (wchar_t*)malloc((wcslen(location)+1)*sizeof(wchar_t)); port->friendlyName = (wchar_t*)malloc((wcslen(friendlyName)+1)*sizeof(wchar_t)); port->portDescription = (wchar_t*)malloc((wcslen(description)+1)*sizeof(wchar_t)); // Store port strings - wcscpy_s(port->portPath, wcslen(key)+1, key); + if (containsSlashes) + wcscpy_s(port->portPath, wcslen(key)+1, key); + else + { + wcscpy_s(port->portPath, wcslen(key)+5, L"\\\\.\\"); + wcscat_s(port->portPath, wcslen(key)+5, key); + } wcscpy_s(port->portLocation, wcslen(location)+1, location); wcscpy_s(port->friendlyName, wcslen(friendlyName)+1, friendlyName); wcscpy_s(port->portDescription, wcslen(description)+1, description); @@ -77,13 +84,10 @@ serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t serialPort* fetchPort(serialPortVector* vector, const wchar_t* key) { - // Move past any opening slashes - if ((key[0] == L'\\') && (key[1] == L'\\') && (key[2] == L'.') && (key[3] == L'\\')) - key += 4; - // Retrieve the serial port specified by the passed-in key + int keyOffset = ((key[0] == L'\\') && (key[1] == L'\\') && (key[2] == L'.') && (key[3] == L'\\')) ? 0 : 4; for (int i = 0; i < vector->length; ++i) - if (wcscmp(key, vector->ports[i]->portPath) == 0) + if (wcscmp(key, vector->ports[i]->portPath + keyOffset) == 0) return vector->ports[i]; return NULL; } @@ -113,7 +117,7 @@ void removePort(serialPortVector* vector, serialPort* port) } // Windows-specific functionality -void reduceLatencyToMinimum(const wchar_t* portName) +void reduceLatencyToMinimum(const wchar_t* portName, unsigned char requestElevatedPermissions) { // Search for this port in all FTDI enumerated ports HKEY key, paramKey = 0; @@ -144,7 +148,7 @@ void reduceLatencyToMinimum(const wchar_t* portName) RegSetValueExW(paramKey, L"LatencyTimer", 0, REG_DWORD, (LPBYTE)&latency, sizeof(latency)); RegCloseKey(paramKey); } - else + else if (requestElevatedPermissions) { // Create registry update file char *workingDirectory = _getcwd(NULL, 0); diff --git a/src/main/c/Windows/WindowsHelperFunctions.h b/src/main/c/Windows/WindowsHelperFunctions.h index e88a983..d6ac0f7 100644 --- a/src/main/c/Windows/WindowsHelperFunctions.h +++ b/src/main/c/Windows/WindowsHelperFunctions.h @@ -2,7 +2,7 @@ * WindowsHelperFunctions.h * * Created on: May 05, 2015 - * Last Updated on: Jan 21, 2022 + * Last Updated on: Jan 28, 2022 * Author: Will Hedgecock * * Copyright (C) 2012-2022 Fazecast, Inc. @@ -51,7 +51,7 @@ serialPort* fetchPort(serialPortVector* vector, const wchar_t* key); void removePort(serialPortVector* vector, serialPort* port); // Windows-specific functionality -void reduceLatencyToMinimum(const wchar_t* portName); +void reduceLatencyToMinimum(const wchar_t* portName, unsigned char requestElevatedPermissions); 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 115e073..0816bae 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -719,6 +719,29 @@ public final class SerialPort */ public final synchronized void disableExclusiveLock() { disableExclusiveLock = true; } + /** + * Allows the library to request elevation of the current user's permissions for use in making certain + * system-specific changes regarding this serial port. + *
+ * Examples of such changes include reducing the default latency for FTDI-type devices using + * the Windows registry, or adding the current Linux user to the same OS group to which the serial + * port belongs so that they can access the port without having to make these changes manually. + *
+ * On Windows, if elevated permissions are required, a User Access Control (UAC) dialog box will + * appear, requesting authorization to carry out the privileged operation. + * On a non-Windows system, elevated permissions will be requested as if you had used the 'sudo' command + * in a terminal. As such, this function should not be used if your application does not contain or use + * a console. + *
+ * Care should be taken when choosing to use this function as it may cause a prompt to appear + * during runtime of your final application requesting permission to make these elevated changes which + * may detract from the user experience of your application. When possible, making any system changes + * related to serial port usage should be done manually before attempting to use such ports, but in some + * situations, this function may make it easier for your application to automatically apply these + * necessary changes. + */ + public final synchronized void allowElevatedPermissionsRequest() { requestElevatedPermissions = true; } + /** * Returns the source code line location of the latest error encountered during execution of * the native code for this port. diff --git a/src/main/resources/Windows/aarch64/jSerialComm.dll b/src/main/resources/Windows/aarch64/jSerialComm.dll index bac600d..79b76f5 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 aee94dd..e9c7d12 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 52b282e..4301fb9 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 6bfe0b9..695d567 100644 Binary files a/src/main/resources/Windows/x86_64/jSerialComm.dll and b/src/main/resources/Windows/x86_64/jSerialComm.dll differ diff --git a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java index bb683ab..be2a653 100644 --- a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java +++ b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java @@ -2,10 +2,10 @@ * SerialPortTest.java * * Created on: Feb 27, 2015 - * Last Updated on: Nov 29, 2021 + * Last Updated on: Jan 28, 2022 * Author: Will Hedgecock * - * Copyright (C) 2012-2021 Fazecast, Inc. + * Copyright (C) 2012-2022 Fazecast, Inc. * * This file is part of jSerialComm. * @@ -87,13 +87,13 @@ public class SerialPortTest SerialPort[] ports = SerialPort.getCommPorts(); System.out.println("\nAvailable Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); System.out.println("\nRe-enumerating ports again in 2 seconds...\n"); try { Thread.sleep(2000); } catch (Exception e) {} ports = SerialPort.getCommPorts(); System.out.println("Available Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); SerialPort ubxPort; System.out.print("\nChoose your desired serial port or enter -1 to specify a port directly: "); int serialPortChoice = 0; @@ -113,6 +113,7 @@ public class SerialPortTest } else ubxPort = ports[serialPortChoice]; + ubxPort.allowElevatedPermissionsRequest(); byte[] readBuffer = new byte[2048]; System.out.println("\nPre-setting RTS: " + (ubxPort.setRTS() ? "Success" : "Failure")); boolean openedSuccessfully = ubxPort.openPort(0);