为什么使用自定义端口监视器在调用 EnumPorts 后我会收到 "The data is invalid"?
Why with a custom port monitor do I receive "The data is invalid" after a call to EnumPorts?
我正在实施基于 LocalMon sample 的自定义端口监视器,但是当我 return 实施 LcmEnumPorts
时,我收到错误 "The data is invalid",我机器上安装的端口列表是空的。删除监视器消除错误,所有端口重新出现。
为什么?我已经检查过我正在 return 的结构是连贯的并且适合分配的缓冲区。
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;
}
虽然它似乎没有在任何地方记录,但假脱机程序似乎为多个驱动程序调用重用相同的缓冲区。
文档说:
The EnumPorts function should fill the buffer pointed to by pPort with an array of PORT_INFO_1 or PORT_INFO_2 structures. Then starting in a memory location following the last array element, the function must load all the strings pointed to by the array's structure members. Refer to localmon.dll, a sample port monitor, for an example of how to do this. The function must also return the number of structures supplied (that is, the number of supported ports) by placing the number in the location pointed to by 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;
}
我正在实施基于 LocalMon sample 的自定义端口监视器,但是当我 return 实施 LcmEnumPorts
时,我收到错误 "The data is invalid",我机器上安装的端口列表是空的。删除监视器消除错误,所有端口重新出现。
为什么?我已经检查过我正在 return 的结构是连贯的并且适合分配的缓冲区。
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;
}
虽然它似乎没有在任何地方记录,但假脱机程序似乎为多个驱动程序调用重用相同的缓冲区。
文档说:
The EnumPorts function should fill the buffer pointed to by pPort with an array of PORT_INFO_1 or PORT_INFO_2 structures. Then starting in a memory location following the last array element, the function must load all the strings pointed to by the array's structure members. Refer to localmon.dll, a sample port monitor, for an example of how to do this. The function must also return the number of structures supplied (that is, the number of supported ports) by placing the number in the location pointed to by 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;
}