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
*
* 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)

View File

@ -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);

View File

@ -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__

View File

@ -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.
* <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
* the native code for this port.

View File

@ -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);