Setting Device Properties before calling Update-DriverFor-Plug-And-Play-Devices-A

I recently (mostly) completed my first driver, which essentially allows the creation of an virtual serial port, which can then be used by an application to expose it to other programs which can connect to it like to any other serial port.

I now wanted to write an command line application that can be used to create and remove these ports, and so far that works fine too.

The problem is, that right now, in the driver, I set the ports name to be what ever windows provides in the registry entry:

HKLM\SYSTEM\CurrentControlSet\Enum\ROOT\PORTS\*port dev id*\Device Parameters\PortName

which I access using WdfDeviceOpenRegistryKey and WdfRegistryQueryUnicodeString.
Its how it was done in the official windows example driver.

But to make it easier to use and add a more clear distinction between normal and virtual ports, I want the ability to optionally specify a custom name on the command line.

My first idea was to simply use SetupDiOpenDevRegKey to change the value in “PortName” to my custom name and hope that windows does not override it if it is already set.

But it turns out, the “Device Parameters” key is only created after the call to UpdateDriverForPlugAndPlayDevicesA which also runs the configuration in the driver.

My question is, is there a way to supply any additional configuration information to the driver before calling UpdateDriverForPlugAndPlayDevicesA ?
I tought about trying to create the Device Parameters key manually, but that is a bit more complicated than it seems, and also seems not like it is intended.

Also, is there a faster way to “install my driver” for the newly created device, than using UpdateDriverForPlugAndPlayDevicesA ?
I noticed that not only takes that function really long, it also takes very long per device.
If I want to create like 5 virtual ports, it takes like half a minute to complete.
Maybe an another method would also solve my configuration issue, I am not sure.

Side note:
For some reason it let me not create the title of this topic with the real function name, it said that the word was to long, so I had to add - in between, is this normal ?

The alternative to UpdateDriverForPlugAndPlayDevices is writing an install app that uses the apis used by UpdateDriverForPlugAndPlayDevices. See Devcon (and apparently inside MSFT it is considered ‘deprecated’, but it is the only sample that illustrates how to manage drivers and devices.)

The port names are integrated with the comm port database, and that is done by the Port Class itself when you add a new device. I’d suggest not messing with this mechanism. A simple app that enumerates your virtual ports would suffice.

I looked into devcon, and it also just calls UpdateDriverForPlugAndPlayDevices, altough it does it in a strange way, it manually locates the newdev.dll file, loads it, then searches for the address of the function and calls it.
Not sure if there is a reason to do it that way, I just linked against the newdev.dll to do it.

Also, I did just write a quick test code:

#define PROTS_ENUM_KEY "SYSTEM\\CurrentControlSet\\Enum\\ROOT\\PORTS"
#define PORT_PARAMETERS_KEY "\\Device Parameters"
#define PORT_HARDWARE_ID_KEY "HardwareID"
#define PORT_NAME_KEY "PortName"

bool tryNameFirstUnnamed(std::string portName, std::string vcomHardwareID)
{

	HKEY portsKeyHandle;
	LSTATUS status = RegOpenKeyA(HKEY_LOCAL_MACHINE, PROTS_ENUM_KEY, &portsKeyHandle);
	if (status != ERROR_SUCCESS) {
		printError(status, "error 0x%x in drvsetup:tryNameFirstUnnamed:RegOpenKeyA: %s");
		return false;
	}

	DWORD portKeyIndex = 0;
	do {

		char portIndexName[256] = {0};
		DWORD portIndexNameLen = 256;
		status = RegEnumKeyExA(portsKeyHandle, portKeyIndex++, portIndexName, &portIndexNameLen, NULL, NULL, NULL, NULL);
		if (status != ERROR_SUCCESS) continue;

		char hardwareId[256] = {0};
		DWORD hardwareIdLen = 256;
		status = RegGetValueA(portsKeyHandle, portIndexName, PORT_HARDWARE_ID_KEY, RRF_RT_REG_MULTI_SZ, NULL, hardwareId, &hardwareIdLen);
		if (status != ERROR_SUCCESS) continue;

		if (std::string(hardwareId) != vcomHardwareID) continue;

		HKEY portPropertiesHandle;
		char propertiesSubkey[strlen(portIndexName) + strlen(PORT_PARAMETERS_KEY) + 1] = {0};
		strcat(propertiesSubkey, portIndexName);
		strcat(propertiesSubkey, PORT_PARAMETERS_KEY);
		status = RegCreateKeyExA(portsKeyHandle, propertiesSubkey, 0, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &portPropertiesHandle, NULL);
		if (status != ERROR_SUCCESS) continue;

		char currentPortName[256] = {0};
		DWORD currentPortNameLen = 256;
		status = RegGetValueA(portPropertiesHandle, "", PORT_NAME_KEY, RRF_RT_REG_SZ, NULL, currentPortName, &currentPortNameLen);
		if (status == ERROR_SUCCESS) {
			RegCloseKey(portPropertiesHandle);
			continue; // skip if port already has a name
		}

		status = RegSetKeyValueA(portPropertiesHandle, "", PORT_NAME_KEY, REG_SZ, portName.c_str(), portName.length() + 1);
		if (status != ERROR_SUCCESS) {
			RegCloseKey(portPropertiesHandle);
			continue;
		}

		RegCloseKey(portPropertiesHandle);
		RegCloseKey(portsKeyHandle);
		return true;

	} while (status != ERROR_NO_MORE_ITEMS);

	RegCloseKey(portsKeyHandle);
	return false;

}

It essentially looks for the first port device that has not yet been given a name and sets the PortName property.
I call it directly after SetupDiCallClassInstaller and it seems to work.
I mean, it kinda makes sense, if I don’t set the variable and let windows decide, it gives it a random port (well not random, it increments the number each time).
But if I remove the driver, and re-install it, it reuses the name that was defined before, would be annoying if any port got a new name each time you reboot or update the driver.

So this seems like it would be a way to specify which name my port gets.
The only downside is that I have to follow the windows naming scheme “COM[0-9]+”.
Every other name gets just replaced by windows, but I am fine with that.
I just don’t want absurdly high port numbers like COM65, and the ability to configure which number I get in the graphical interface which I will write later.

I also toughed about just not using the PORTS class, this is what com0com seems to do, but this will cause problems with some software I intended to use it with, it seems like some programs are very picky about what is considered an serial port.

I also just remembered, you can change the port number in the settings iirc.
Which also makes sense, and I guess it probably does the same (changing that value and reloading the driver).

That just guarantees that the software is using the dll installed on the platform. In general this is not required, but also it does no harm. It also obviously reduces the size of your executable, but newdev is not exactly huge..

But linking against the dll does the same, doesn’t it ?
I mean not statically linking, I mean dynamic linking.

You should also confirm that the COM Port Database is working correctly. =

COM Port Database (Windows Drivers) | Microsoft Learn

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.