【问题标题】:How can I change a serial COM port number programmatically?如何以编程方式更改串行 COM 端口号?
【发布时间】:2017-07-13 21:41:41
【问题描述】:

这是我的问题: 我开发了一个自定义串行驱动程序,它依赖于 Microsoft serenum.sys 来枚举串行端口。现在,我在一个多功能设备上应用了这个串行驱动程序,该设备将单个设备拆分为六个不同的串行端口 - 当这种情况发生时,端口编号完全被打乱,所以我需要 Serial0 获取 COM1,例如,等等。

我搜索了一下,发现了这个: Change COM port via registry, command line or software?

我尝试使用 ComDB 来获取我需要的端口名称,但是虽然我可以使端口名称可用,但我似乎没有一种有效的方法来将特定串行端口(即 Serial0)与特定串行端口相关联COM 端口号(即 COM1)。

有人已经解决了这个问题吗?

【问题讨论】:

  • 在固定串行端口很少见且使用 USB 串行适配器的时代,这很难实现......仍然很难看,但可能最好的解决方案是实现某种“在所有(当前未使用的)串行端口上扫描您的设备(或者让用户选择正确的)
  • 那是我试图避免的。我的驱动程序与 SerialPropPages 完全兼容,因此您可以像往常一样通过 UI 更改端口名称。我的问题是,例如,我需要将 COM5 重命名为 COM1,因为名称太可怕了。
  • 使用 IoCreateSymbolicLink() 创建遗留 Dos 名称(如“COM1”)的是串行端口驱动程序。重命名不是一种选择。如果您无法修复驱动程序,请考虑查找它。背景:osronline.com/article.cfm?id=381
  • 这就是我在驱动程序中所做的,如果可能的话,我应该在以后修改。否则,替代方法就是创建一个共同安装程序以在 DIF_INSTALLDEVICE 之前设置 PortName。问题是我必须确保这种情况总是会发生,并且该设备绑定到很多硬件 ID。所以是的,您的评论方向正确,但我需要解决这个问题。

标签: c windows serial-port


【解决方案1】:

分两部分解决,还有一个未解决的问题。 从 GitHub 上的 WDK 驱动程序示例开始,https://github.com/Microsoft/Windows-driver-samples

  1. SerialReadSymName() 函数上,我将其更改为返回我想要的符号名称,而不是让它使用 ComDB 提供的索引。请注意,我必须从 inf 文件中的 UpperFilters 键中删除 serenum 才能使用它。

  2. 现在我有了可以使用的工作设备名称(即 COM1、COM2 等),但在这个阶段我还是将串行端口“友好名称”重命名了。为了解决这个问题,我用以下代码更新了SerialCreateOpen()

    if (!extension->FriendlyNameSet) //Added this guard to the device extension { DWORD f_set = 1; HANDLE keyHandle; MySerialSetFriendlyName(extension); extension->FriendlyNameSet = TRUE; }

函数MySerialSetFriendlyName()定义如下:

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_... 是存储我选择的友好名称前缀(比如L"Communications Port (COM")和后缀(比如L")")的宏。您可以根据需要制作它们,但您确实需要将它们的长度单独存储宏,然后)

这样,友好名称将在第一个设备打开时设置。为了确保这意味着设备会立即获得正确的名称,我刚刚编写了一个设备协同安装程序,它在DIF_INSTALLDEVICE 命令上进行后处理,快速打开和关闭端口。这本身就值得一篇文章,而且很简单,所以我现在不会发布。

唯一未解决的问题是: - 这样,ComDB 仍然会记住错误的编号顺序。您可以通过更改 Windows 注册表中的 Com Name Arbiter 位掩码来强制它按您的意愿运行,如我在问题中提供的链接中指定的那样,但您不能对仍然绑定到某些 I/ 的 COM1 和 COM2 执行此操作O 范围和 IRQ。

所以,只要您永远不需要在应用程序中访问 ComDB,它就可以工作!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多