Create method to allow elevated permission requests on Windows

This commit is contained in:
Will Hedgecock 2022-01-28 13:22:29 -06:00
parent a742cad114
commit 28c21ec4eb
9 changed files with 54 additions and 23 deletions

View File

@ -2,7 +2,7 @@
* SerialPort_Windows.c * SerialPort_Windows.c
* *
* Created on: Feb 25, 2012 * Created on: Feb 25, 2012
* Last Updated on: Jan 25, 2022 * Last Updated on: Jan 28, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
@ -63,6 +63,7 @@ jfieldID parityField;
jfieldID flowControlField; jfieldID flowControlField;
jfieldID sendDeviceQueueSizeField; jfieldID sendDeviceQueueSizeField;
jfieldID receiveDeviceQueueSizeField; jfieldID receiveDeviceQueueSizeField;
jfieldID requestElevatedPermissionsField;
jfieldID rs485ModeField; jfieldID rs485ModeField;
jfieldID rs485DelayBeforeField; jfieldID rs485DelayBeforeField;
jfieldID rs485DelayAfterField; jfieldID rs485DelayAfterField;
@ -269,7 +270,7 @@ static void enumeratePorts(void)
for (int i = 0; i < numDevs; ++i) for (int i = 0; i < numDevs; ++i)
{ {
// Determine if the port is currently enumerated and already open // 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) if (!isOpen)
for (int j = 0; j < serialPorts.length; ++j) 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)) 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 // Check if actually connected and present in the port list
for (int j = 0; j < serialPorts.length; ++j) 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 // Update the port description
serialPorts.ports[j]->enumerated = 1; 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 // Create new SerialComm object containing the enumerated values
jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor); jobject serialCommObject = (*env)->NewObject(env, serialCommClass, serialCommConstructor);
if (checkJniError(env, __LINE__ - 1)) return arrayObject; if (checkJniError(env, __LINE__ - 1)) return arrayObject;
wcscpy_s(comPort, sizeof(comPort) / sizeof(wchar_t), L"\\\\.\\"); (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)serialPorts.ports[i]->portPath, wcslen(serialPorts.ports[i]->portPath)));
wcscat_s(comPort, sizeof(comPort) / sizeof(wchar_t), serialPorts.ports[i]->portPath);
(*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewString(env, (jchar*)comPort, wcslen(comPort)));
if (checkJniError(env, __LINE__ - 1)) return arrayObject; 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))); (*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; 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; if (checkJniError(env, __LINE__ - 1)) return;
receiveDeviceQueueSizeField = (*env)->GetFieldID(env, serialCommClass, "receiveDeviceQueueSize", "I"); receiveDeviceQueueSizeField = (*env)->GetFieldID(env, serialCommClass, "receiveDeviceQueueSize", "I");
if (checkJniError(env, __LINE__ - 1)) return; 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"); rs485ModeField = (*env)->GetFieldID(env, serialCommClass, "rs485Mode", "Z");
if (checkJniError(env, __LINE__ - 1)) return; if (checkJniError(env, __LINE__ - 1)) return;
rs485DelayBeforeField = (*env)->GetFieldID(env, serialCommClass, "rs485DelayBefore", "I"); 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; if (checkJniError(env, __LINE__ - 1)) return 0;
const wchar_t *portName = (wchar_t*)(*env)->GetStringChars(env, portNameJString, NULL); const wchar_t *portName = (wchar_t*)(*env)->GetStringChars(env, portNameJString, NULL);
if (checkJniError(env, __LINE__ - 1)) return 0; 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); unsigned char disableAutoConfig = (*env)->GetBooleanField(env, obj, disableConfigField);
if (checkJniError(env, __LINE__ - 1)) return 0; if (checkJniError(env, __LINE__ - 1)) return 0;
unsigned char autoFlushIOBuffers = (*env)->GetBooleanField(env, obj, autoFlushIOBuffersField); 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 // 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 // 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) 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)

View File

@ -2,7 +2,7 @@
* WindowsHelperFunctions.c * WindowsHelperFunctions.c
* *
* Created on: May 05, 2015 * Created on: May 05, 2015
* Last Updated on: Jan 25, 2022 * Last Updated on: Jan 28, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * 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) 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 // 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) if (vector->capacity == vector->length)
{ {
serialPort** newArray = (serialPort**)realloc(vector->ports, ++vector->capacity * sizeof(serialPort*)); 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)); memset(port, 0, sizeof(serialPort));
port->handle = (void*)-1; port->handle = (void*)-1;
port->enumerated = 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->portLocation = (wchar_t*)malloc((wcslen(location)+1)*sizeof(wchar_t));
port->friendlyName = (wchar_t*)malloc((wcslen(friendlyName)+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)); port->portDescription = (wchar_t*)malloc((wcslen(description)+1)*sizeof(wchar_t));
// Store port strings // 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->portLocation, wcslen(location)+1, location);
wcscpy_s(port->friendlyName, wcslen(friendlyName)+1, friendlyName); wcscpy_s(port->friendlyName, wcslen(friendlyName)+1, friendlyName);
wcscpy_s(port->portDescription, wcslen(description)+1, description); 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) 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 // 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) 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 vector->ports[i];
return NULL; return NULL;
} }
@ -113,7 +117,7 @@ void removePort(serialPortVector* vector, serialPort* port)
} }
// Windows-specific functionality // 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 // Search for this port in all FTDI enumerated ports
HKEY key, paramKey = 0; 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)); RegSetValueExW(paramKey, L"LatencyTimer", 0, REG_DWORD, (LPBYTE)&latency, sizeof(latency));
RegCloseKey(paramKey); RegCloseKey(paramKey);
} }
else else if (requestElevatedPermissions)
{ {
// Create registry update file // Create registry update file
char *workingDirectory = _getcwd(NULL, 0); char *workingDirectory = _getcwd(NULL, 0);

View File

@ -2,7 +2,7 @@
* WindowsHelperFunctions.h * WindowsHelperFunctions.h
* *
* Created on: May 05, 2015 * Created on: May 05, 2015
* Last Updated on: Jan 21, 2022 * Last Updated on: Jan 28, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2022 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
@ -51,7 +51,7 @@ serialPort* fetchPort(serialPortVector* vector, const wchar_t* key);
void removePort(serialPortVector* vector, serialPort* port); void removePort(serialPortVector* vector, serialPort* port);
// Windows-specific functionality // 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); int getPortPathFromSerial(wchar_t* portPath, const char* serialNumber);
#endif // #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__ #endif // #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__

View File

@ -719,6 +719,29 @@ public final class SerialPort
*/ */
public final synchronized void disableExclusiveLock() { disableExclusiveLock = true; } 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* Care should be taken when choosing to use this function as it <i>may</i> 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 * Returns the source code line location of the latest error encountered during execution of
* the native code for this port. * the native code for this port.

View File

@ -2,10 +2,10 @@
* SerialPortTest.java * SerialPortTest.java
* *
* Created on: Feb 27, 2015 * Created on: Feb 27, 2015
* Last Updated on: Nov 29, 2021 * Last Updated on: Jan 28, 2022
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2021 Fazecast, Inc. * Copyright (C) 2012-2022 Fazecast, Inc.
* *
* This file is part of jSerialComm. * This file is part of jSerialComm.
* *
@ -87,13 +87,13 @@ public class SerialPortTest
SerialPort[] ports = SerialPort.getCommPorts(); SerialPort[] ports = SerialPort.getCommPorts();
System.out.println("\nAvailable Ports:\n"); System.out.println("\nAvailable Ports:\n");
for (int i = 0; i < ports.length; ++i) 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"); System.out.println("\nRe-enumerating ports again in 2 seconds...\n");
try { Thread.sleep(2000); } catch (Exception e) {} try { Thread.sleep(2000); } catch (Exception e) {}
ports = SerialPort.getCommPorts(); ports = SerialPort.getCommPorts();
System.out.println("Available Ports:\n"); System.out.println("Available Ports:\n");
for (int i = 0; i < ports.length; ++i) 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; SerialPort ubxPort;
System.out.print("\nChoose your desired serial port or enter -1 to specify a port directly: "); System.out.print("\nChoose your desired serial port or enter -1 to specify a port directly: ");
int serialPortChoice = 0; int serialPortChoice = 0;
@ -113,6 +113,7 @@ public class SerialPortTest
} }
else else
ubxPort = ports[serialPortChoice]; ubxPort = ports[serialPortChoice];
ubxPort.allowElevatedPermissionsRequest();
byte[] readBuffer = new byte[2048]; byte[] readBuffer = new byte[2048];
System.out.println("\nPre-setting RTS: " + (ubxPort.setRTS() ? "Success" : "Failure")); System.out.println("\nPre-setting RTS: " + (ubxPort.setRTS() ? "Success" : "Failure"));
boolean openedSuccessfully = ubxPort.openPort(0); boolean openedSuccessfully = ubxPort.openPort(0);