Working with SetupDiGetDriverInfoDetailA

When using the Windows setupapi function SetupDiGetDriverInfoDetailA, it can set the thread local error variable to ERROR_INSUFFICIENT_BUFFER or ERROR_INVALID_USER_BUFFER.

The two mean basically the same thing: Your buffer is too small. The SP_DRVINFO_DETAIL_DATA_A struct has a dynamically sized tail that contains the Hardware IDs. ERROR_INVALID_USER_BUFFER means that that dynamically sized area is too small.

You will get ERROR_INSUFFICIENT_BUFFER, if the buffer is smaller than sizeof(SP_DRVINFO_DETAIL_DATA_A).

In order to make correct use of the SetupDiGetDriverInfoDetailA function, the buffer needs to be sizeof(SP_DRVINFO_DETAIL_DATA_A) + *RequiredSize large!

(RequiredSize is a PDWORD (DWORD *) type variable that is set by SetupDiGetDriverInfoDetailA when you ask it for the right size of the buffer.

Complete C code snippet that shows how to use it:

bool windows_get_driver_info_data_a(
	HDEVINFO *dev_info_set,
	SP_DEVINFO_DATA *dev_info_data,
	SP_DRVINFO_DATA_A *drv_info_data,
	SP_DRVINFO_DETAIL_DATA_A *drv_info_detail_data,
	DWORD *property_buffer_length,
	DWORD *required_length
	)
{
    DWORD error;
    char buf[512];
    while(TRUE)
    {
	if (!SetupDiGetDriverInfoDetailA(
	    *dev_info_set,
	    dev_info_data,
	    drv_info_data,
	    drv_info_detail_data,
	    *property_buffer_length,
	    required_length
	))
	{
	    DBG0(DBG_LIB, "required_length: %u", *required_length);
	    DBG0(DBG_LIB, "buffer length: %u", *property_buffer_length);
	    error = GetLastError();
	    if(!error)
	    {
		return TRUE;
	    }
	    else if (error == ERROR_INSUFFICIENT_BUFFER)
	    {
		// allocate memory
		drv_info_detail_data = realloc(
			drv_info_detail_data,
			*required_length + sizeof(SP_DRVINFO_DETAIL_DATA_A));
		drv_info_detail_data->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_A);
		*property_buffer_length = *required_length + sizeof(SP_DRVINFO_DETAIL_DATA_A);
		DBG0(DBG_LIB, "required_length: %u", *required_length);
		if (!SetupDiGetDriverInfoDetailA(
			*dev_info_set,
			dev_info_data,
			drv_info_data,
			drv_info_detail_data,
			*property_buffer_length,
			required_length
		))
		{
			error = GetLastError();
			if (error)
			{
				// previous returned length was bogus, something
				// is fishy. Log error message and skip item.
				DBG1(DBG_LIB, "Previous required length was bogus. New error is: %s", dlerror_mt(buf, sizeof(buf)));
			}
		}
	    } else {
		// other error occured. Log error and skip item.
		DBG1(DBG_LIB, "A different error occured: %s", dlerror_mt(buf, sizeof(buf)));
	    }
	}
    }
    return FALSE;
}