Почему с пользовательским монитором порта я получаю "Недопустимые данные" после вызова EnumPorts?
Я реализую пользовательский монитор портов на основе образца LocalMon, но когда я возвращаюсь из своей реализации LcmEnumPorts
, Я получаю сообщение об ошибке "Данные неверны", и список портов, установленных на моей машине, пуст. Удаление монитора устраняет ошибку, и все порты появляются снова.
Зачем? Я проверил, что структура, которую я возвращаю, является последовательной и вписывается в выделенный буфер.
Пример реализации 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;
}