Как вызвать DeviceIoControl для получения необходимого объема памяти?

Я пытаюсь позвонить DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) API, а показано здесь, но мне нужно, чтобы он сначала «сказал мне», сколько памяти ему нужно (в отличие от кода, на который я ссылался).

Я называю это так:

//First determine how much data do we need?
BYTE dummyBuff[1];
DWORD bytesReturned = 0;
if(!::DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,
dummyBuff, sizeof(dummyBuff), &bytesReturned, NULL))
{
//Check last error
int nError = ::GetLastError();
if(nOSError == ERROR_INSUFFICIENT_BUFFER ||
nOSError == ERROR_MORE_DATA)
{
//Alloc memory from 'bytesReturned' ...
}
}

но всегда возвращает код ошибки 87, или же ERROR_INVALID_PARAMETER и мой bytesReturned всегда 0.

Так что я делаю не так?

0

Решение

Инструкции по получению всех экстентов дисковых томов описаны в разделе Структура VOLUME_DISK_EXTENTS:

Когда количество возвращаемых экстентов больше одного (1), код ошибки ERROR_MORE_DATA возвращается Вам следует позвонить DeviceIoControl опять же, выделив достаточно буферного пространства на основе значения NumberOfDiskExtents после первого DeviceIoControl вызов.

Поведение, если вы передаете выходной буфер, который меньше, чем sizeof(VOLUME_DISK_EXTENTS) также задокументировано на IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS контрольный код:

Если выходной буфер меньше sizeof(VOLUME_DISK_EXTENTS)вызов не проходит, GetLastError возвращается ERROR_INSUFFICIENT_BUFFER, а также lpBytesReturned 0 (ноль).

Хотя это объясняет возвращаемое значение в lpBytesReturned, это не объясняет код ошибки 87 (ERROR_INVALID_PARAMETER)1).

Следующий код вернет экстенты диска для всех томов:

VOLUME_DISK_EXTENTS vde = { 0 };
DWORD bytesReturned = 0;
if ( !::DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
(void*)&vde, sizeof(vde), &bytesReturned, NULL ) )
{
// Check last error
int nError = ::GetLastError();
if ( nError != ERROR_MORE_DATA )
{
// Unexpected error -> error out
throw std::runtime_error( "DeviceIoControl() failed." );
}

size_t size = offsetof( VOLUME_DISK_EXTENTS, Extents[vde.NumberOfDiskExtents] );
std::vector<BYTE> buffer( size );
if ( !::DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
(void*)buffer.data(), size, &bytesReturned, NULL ) )
{
// Unexpected error -> error out
throw std::runtime_error( "DeviceIoControl() failed." );
}
// At this point we have a fully populated VOLUME_DISK_EXTENTS structure
const VOLUME_DISK_EXTENTS& result =
*reinterpret_cast<const VOLUME_DISK_EXTENTS*>( buffer.data() );
}
else
{
// Call succeeded; vde is populated with single disk extent.
}

Дополнительные ссылки:


1) На предположение я бы предположил, что BYTE[1] начинается с адреса памяти, который недостаточно выровнен для требований выравнивания VOLUME_DISK_EXTENTS,

4

Другие решения

После @Совет IInspectable, вот что я придумал для более общего случая:

BYTE* DeviceIoControl_Dynamic(HANDLE hDevice, DWORD dwIoControlCode, DWORD dwszCbInitialSuggested, LPVOID lpInBuffer, DWORD nInBufferSize, DWORD* pncbOutDataSz)
{
//Calls DeviceIoControl() API by pre-allocating buffer internally
//'dwIoControlCode' = control code, see DeviceIoControl() API
//'dwszCbInitialSuggested' = suggested initial size of the buffer in BYTEs, must be set depending on the description of 'dwIoControlCode'
//'lpInBuffer' = input buffer, see DeviceIoControl() API
//'nInBufferSize' = size of 'lpInBuffer', see DeviceIoControl() API
//'pncbOutDataSz' = if not NULL, receives the size of returned data in BYTEs
//RETURN:
//      = Data obtained from DeviceIoControl() API -- must be removed with delete[]!
//      = NULL if error -- check GetLastError() for info
BYTE* pData = NULL;
int nOSError = NO_ERROR;

DWORD ncbSzData = 0;

if((int)dwszCbInitialSuggested > 0)
{
//Initially go with suggested memory size
DWORD dwcbMemSz = dwszCbInitialSuggested;

//Try no more than 10 times
for(int t = 0; t < 10; t++)
{
//Reserve mem
ASSERT(!pData);
pData = new (std::nothrow) BYTE[dwcbMemSz];
if(!pData)
{
//Memory fault
nOSError = ERROR_NOT_ENOUGH_MEMORY;
break;
}

//And try calling with that size
DWORD bytesReturned = 0;
if(::DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,
pData, dwcbMemSz, &bytesReturned, NULL))
{
//Got it
ncbSzData = bytesReturned;
nOSError = NO_ERROR;

break;
}

//Check last error
nOSError = ::GetLastError();

//Knowing how badly Windows drivers are written, don't rely on the last error code!

//Alloc more memory (we'll just "wing it" on the amount)
dwcbMemSz += 1024;

//Free old mem
delete[] pData;
pData = NULL;
}
}
else
{
//Bad initial size
nOSError = ERROR_INVALID_MINALLOCSIZE;
}

if(pncbOutDataSz)
*pncbOutDataSz = ncbSzData;

::SetLastError(nOSError);
return pData;
}

а затем позвонить, скажем, для IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:

DWORD bytesReturned;
VOLUME_DISK_EXTENTS* p_vde = (VOLUME_DISK_EXTENTS*)DeviceIoControl_Dynamic(hDsk,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, sizeof(VOLUME_DISK_EXTENTS), NULL, NULL, &bytesReturned);

который позже можно использовать как таковой:

//Ensure that driver returned the correct data
if(p_vde &&
offsetof(VOLUME_DISK_EXTENTS, Extents[p_vde->NumberOfDiskExtents]) <= bytesReturned)
{
//All good
for(int x = 0; x < p_vde->NumberOfDiskExtents; x++)
{
DWORD diskNumber = p_vde->Extents[x].DiskNumber;
//...
}
}

//Remember to free mem when not needed!
if(p_vde)
{
delete[] (BYTE*)p_vde;
p_vde = NULL;
}
0

Вы получаете код ошибки ERROR_INVALID_PARAMETER, когда у вас есть неверный параметр, как его имя говорит. В вашем случае это должен быть плохой дескриптор, потому что все остальные выглядят хорошо, если мы ожидаем, что аргумент dwIoControlCode — IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, lpInBuffer и nInBufferSize игнорируются

В недостаточном буфере вы получите другой код ошибки, упомянутый в комментариях выше.

Давайте посмотрим, что говорит документация:

DeviceIoControl может принимать дескриптор определенного устройства. Например, чтобы открыть дескриптор логического диска A: с помощью CreateFile, укажите \. \ A :. Кроме того, вы можете использовать имена \. \ PhysicalDrive0, \. \ PhysicalDrive1 и т. Д., Чтобы открывать дескрипторы физических дисков в системе.

Другими словами, когда вы открываете дескриптор с аргументом «C: \» вместо «\\. \ C:» в CreateFile и используете его в DeviceIoControl, в результате получается ERROR_INVALID_PARAMETER.

0
По вопросам рекламы [email protected]