Solved in two parts, with still an open issue.
Starting from the WDK Driver samples on GitHub, https://github.com/Microsoft/Windows-driver-samples
On the SerialReadSymName()
function, I changed it to return the symbolic name I wanted, rather than have it use the index provided from ComDB. Note that I had to remove serenum from UpperFilters key in the inf file to have it work with this.
Now I had the working device name (ie. COM1, COM2 etc.) that could be used, but at this stage I got the serial port "Friendly Name" renamed anyway. To solve this contextually, I updated the SerialCreateOpen()
with the following code:
if (!extension->FriendlyNameSet) //Added this guard to the device extension
{
DWORD f_set = 1;
HANDLE keyHandle;
MySerialSetFriendlyName(extension);
extension->FriendlyNameSet = TRUE;
}
With the function MySerialSetFriendlyName()
defined as follows:
NTSTATUS MySerialSetFriendlyName(PSERIAL_DEVICE_EXTENSION pDevExt)
{
NTSTATUS status = STATUS_SUCCESS;
WCHAR *FriendlyName, *fnprefix, *fnsuffix, *instanceId;
ULONG FriendlyNameLength, instanceLength, temp, i;
fnprefix = BASE_FRIENDLY_NAME_PREFIX_STR;
fnsuffix = BASE_FRIENDLY_NAME_SUFFIX_STR;
temp = pDevExt->InstanceIdentifier;
instanceLength = 0;
while (temp)
{
instanceLength++;
if (temp < 10)
temp = 0;
else
{
temp /= 10;
if (!temp)
instanceLength++;
}
}
if (instanceLength)
{
instanceId = ExAllocatePool(NonPagedPool, instanceLength);
temp = pDevExt->InstanceIdentifier;
WCHAR digit = L'X';
for (i = 0; i < instanceLength; i++)
{
switch (temp % 10)
{
case 0: digit = L'0'; break;
case 1: digit = L'1'; break;
case 2: digit = L'2'; break;
case 3: digit = L'3'; break;
case 4: digit = L'4'; break;
case 5: digit = L'5'; break;
case 6: digit = L'6'; break;
case 7: digit = L'7'; break;
case 8: digit = L'8'; break;
case 9: digit = L'9'; break;
default: digit = L'X'; break;
}
instanceId[instanceLength - i - 1] = digit;
temp /= 10;
}
FriendlyNameLength = BASE_FRIENDLY_NAME_PREFIX_LEN + instanceLength + BASE_FRIENDLY_NAME_SUFFIX_LEN + 1;
FriendlyName = ExAllocatePool(NonPagedPool, FriendlyNameLength * sizeof(WCHAR));
RtlZeroMemory(FriendlyName, FriendlyNameLength * sizeof(WCHAR));
for (i = 0; i < BASE_FRIENDLY_NAME_PREFIX_LEN; i++)
{
FriendlyName[i] = fnprefix[i];
}
for (i = 0; i < instanceLength; i++)
{
FriendlyName[i + BASE_FRIENDLY_NAME_PREFIX_LEN] = instanceId[i];
}
for (i = 0; i < BASE_FRIENDLY_NAME_SUFFIX_LEN; i++)
{
FriendlyName[i + BASE_FRIENDLY_NAME_PREFIX_LEN + instanceLength] = fnsuffix[i];
}
if (pDevExt->PnpRegistryKey)
status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, (PCWSTR)pDevExt->PnpRegistryKey, (PCWSTR)L"FriendlyName", REG_SZ, FriendlyName, FriendlyNameLength * sizeof(WCHAR));
else
status = STATUS_INSUFFICIENT_RESOURCES;
KdPrintEx((DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "%s :: Changed friendly name to %S (NTSTATUS %08X)\n", __FUNCTION__, FriendlyName, status));
if (!NT_SUCCESS(status))
{
status = STATUS_SUCCESS; //it is enough that we came through here
}
}
return status;
}
(BASE_FRIENDLY_NAME_...
are macros that store my chosen friendly name prefix (let's say L"Communications Port (COM"
) and suffix (let's say L")"
. You can make them whatever you need, but you really need to store their length in separate macros, then)
That way, friendly name would be set at first device open. To be sure that this means that device gets the right name right away, I just wrote a Device Coinstaller that on the DIF_INSTALLDEVICE
command, on post-processing, quickly opened and closed the port. This deserves an article in itself and it is simple enough, so I won't publish this now.
The only open issue is this:
- This way, ComDB still memorizes the wrong numbering sequence. You can force it to behave as you wish by changing the Com Name Arbiter
bitmasks in the Windows Registry, as specified in the link I provided in the question, but you cannot do that for COM1 and COM2, that are still bound to certain I/O ranges and IRQs.
So, it works, provided you will never need to access ComDB in your applications!