Почему с пользовательским монитором порта я получаю "Недопустимые данные" после вызова EnumPorts?

Я реализую пользовательский монитор портов на основе образца LocalMon, но когда я возвращаюсь из своей реализации LcmEnumPorts, Я получаю сообщение об ошибке "Данные неверны", и список портов, установленных на моей машине, пуст. Удаление монитора устраняет ошибку, и все порты появляются снова.

Операция не может быть завершена (ошибка 0x0000000d). Данные неверны

Зачем? Я проверил, что структура, которую я возвращаю, является последовательной и вписывается в выделенный буфер.

Пример реализации LcmEnumPorts:

_Success_(return != FALSE)
BOOL WINAPI LcmEnumPorts(
    _In_                        HANDLE  hMonitor,
    _In_opt_                    LPWSTR  pName,
                                DWORD   Level,
    _Out_writes_bytes_opt_(cbBuf)
                                LPBYTE  pPorts,
                                DWORD   cbBuf,
    _Out_                       LPDWORD pcbNeeded,
    _Out_                       LPDWORD pcReturned
    )
{
    UNREFERENCED_PARAMETER(pName);
    UNREFERENCED_PARAMETER(hMonitor);

    if (!pcbNeeded || !pcReturned || (!pPorts && (cbBuf > 0)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    else if ((1 != Level) && (2 != Level))
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    const wchar_t szMonitorName[] = L"WDK Sample Port";
    const wchar_t szPortName[] = L"NUL:";

    size_t cbPortName = wcslen(szPortName) * sizeof(wchar_t) + sizeof(wchar_t);
    size_t cbPortDesc = wcslen(szMonitorName) * sizeof(wchar_t) + sizeof(wchar_t);
    size_t cbText = cbPortName + cbPortDesc;
    size_t cbStruct = Level == 1 ? sizeof(PORT_INFO_1) : sizeof(PORT_INFO_2);

    *pcbNeeded = (DWORD)(cbText + cbStruct);
    *pcReturned = 0;

    if (*pcbNeeded > cbBuf)
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    if (Level == 1)
    {
        PPORT_INFO_1 pPort1 = (PPORT_INFO_1)pPorts;
        LPWSTR pPortName = (LPWSTR)(pPorts + cbStruct);
        StringCbCopy(pPortName, cbPortName, szPortName);
        pPort1->pName = pPortName;
    }
    else if (Level == 2)
    {
        PPORT_INFO_2 pPort2 = (PPORT_INFO_2)pPorts;

        LPWSTR pPortName = (LPWSTR)(pPorts + cbStruct);
        StringCbCopy(pPortName, cbPortName, szPortName);
        pPort2->pPortName = pPortName;

        LPWSTR pPortDesc = (LPWSTR)(pPorts + cbStruct + cbPortName);
        StringCbCopy(pPortDesc, cbPortDesc, szMonitorName);
        pPort2->pMonitorName = pPortDesc;
        pPort2->pDescription = pPortDesc;

        pPort2->fPortType = PORT_TYPE_READ | PORT_TYPE_WRITE;
        pPort2->Reserved = 0;
    }
    *pcReturned = 1;

    return TRUE;
}

1 ответ

Решение

Хотя это, похоже, нигде не задокументировано, спулер, похоже, повторно использует один и тот же буфер для нескольких вызовов драйверов.

Документы говорят:

Функция EnumPorts должна заполнять буфер, на который указывает pPort, массивом структур PORT_INFO_1 или PORT_INFO_2. Затем, начиная с ячейки памяти, следующей за последним элементом массива, функция должна загрузить все строки, на которые указывают элементы структуры массива. Обратитесь к localmon.dll, образцу монитора портов, для примера того, как это сделать. Функция также должна возвращать количество предоставленных структур (то есть количество поддерживаемых портов), помещая число в местоположение, на которое указывает pcReturned.

Но в действительности кажется, что один и тот же буфер используется повторно для всех мониторов печати. Каждый раз верхний указатель перемещается вниз для каждого PORT_INFO_1/2 структура, а нижний указатель перемещается вверх для каждой выделенной строки.

Диаграмма, показывающая верхний и нижний указатели

Обзор localmon показывает, что он исключительно выделяет строки из нижней части буфера. Изменение сэмпла таким же образом позволяет выполнить его без ошибок.

_Success_(return != FALSE)
BOOL
LcmEnumPorts(
    _In_                        HANDLE  hMonitor,
    _In_opt_                    LPWSTR  pName,
                                DWORD   Level,
    _Out_writes_bytes_opt_(cbBuf)
                                LPBYTE  pPorts,
                                DWORD   cbBuf,
    _Out_                       LPDWORD pcbNeeded,
    _Out_                       LPDWORD pcReturned
    )
{
    UNREFERENCED_PARAMETER(pName);
    UNREFERENCED_PARAMETER(hMonitor);

    if (!pcbNeeded || !pcReturned || (!pPorts && (cbBuf > 0)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    else if ((1 != Level) && (2 != Level))
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    const wchar_t szMonitorName[] = L"WDK Sample Port";
    const wchar_t szPortName[] = L"NUL:";

    size_t cbPortName = wcslen(szPortName) * sizeof(wchar_t) + sizeof(wchar_t);
    size_t cbPortDesc = wcslen(szMonitorName) * sizeof(wchar_t) + sizeof(wchar_t);
    size_t cbText = cbPortName + cbPortDesc;
    size_t cbStruct = Level == 1 ? sizeof(PORT_INFO_1) : sizeof(PORT_INFO_2);

    *pcbNeeded = (DWORD)(cbText + cbStruct);
    *pcReturned = 0;

    if (*pcbNeeded > cbBuf)
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    if (Level == 1)
    {
        PPORT_INFO_1 pPort1 = (PPORT_INFO_1)pPorts;
        // Changed                  vvvvvvvvvvvvvvvvvvvvvvvvvvv
        LPWSTR pPortName = (LPWSTR)(pPorts + cbBuf - cbPortName);
        StringCbCopy(pPortName, cbPortName, szPortName);
        pPort1->pName = pPortName;
    }
    else if (Level == 2)
    {
        PPORT_INFO_2 pPort2 = (PPORT_INFO_2)pPorts;

        // Changed                  vvvvvvvvvvvvvvvvvvvvvvvvvvv
        LPWSTR pPortName = (LPWSTR)(pPorts + cbBuf - cbPortName);
        StringCbCopy(pPortName, cbPortName, szPortName);
        pPort2->pPortName = pPortName;

        // Changed                  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
        LPWSTR pPortDesc = (LPWSTR)(pPorts + cbBuf - cbPortName - cbPortDesc);
        StringCbCopy(pPortDesc, cbPortDesc, szMonitorName);
        pPort2->pMonitorName = pPortDesc;
        pPort2->pDescription = pPortDesc;

        pPort2->fPortType = PORT_TYPE_READ | PORT_TYPE_WRITE;
        pPort2->Reserved = 0;
    }
    *pcReturned = 1;

    return TRUE;
}
Другие вопросы по тегам