How to call DeviceIoControl to retrieve the amount of memory it needs?

后端 未结 3 909
抹茶落季
抹茶落季 2021-01-27 17:09

I\'m trying to call DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) API, as shown here, but I need it to first \"tell me\" how much memory it needs (unlike th

3条回答
  •  误落风尘
    2021-01-27 17:42

    Following @IInspectable's advice, here's what I came up with for a more general case:

    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;
    }
    

    and then to call it, say for 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);
    

    which can be later used as such:

    //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;
    }
    

提交回复
热议问题