FILE_FLAG_NO_BUFFERING with overlapped I/O - bytes read zero - c++

I observe a weird behavior while using the flag FILE_FLAG_NO_BUFFERING with overlapped I/O.
I invoke a series of ReadFile() function calls and query their statuses later using GetOverlappedResult().
The weird behavior that I am speaking of is that even though file handles were good and ReadFile() calls returned without any bad error(except ERROR_IO_PENDING which is expected), the 'bytes read' value returned from GetOverlappedResult() call is zero for some of the files, and each time I run the code - it is a different set of files.
If I remove the FILE_FLAG_NO_BUFFERING, things start working properly and no bytes read value is zero.
Here is how I have implemented overlapped I/O code with FILE_FLAG_NO_BUFFERING.
long overlappedIO(std::vector<std::string> &filePathNameVectorRef)
{
long totalBytesRead = 0;
DWORD bytesRead = 0;
DWORD bytesToRead = 0;
std::map<HANDLE, OVERLAPPED> handleMap;
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD accessMode = GENERIC_READ;
DWORD shareMode = 0;
DWORD createDisposition = OPEN_EXISTING;
DWORD flags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
DWORD fileSize;
LARGE_INTEGER li;
char * buffer;
BOOL success = false;
for(unsigned int i=0; i<filePathNameVectorRef.size(); i++)
{
const char* filePathName = filePathNameVectorRef[i].c_str();
handle = CreateFile(filePathName, accessMode, shareMode, NULL, createDisposition, flags, NULL);
if(handle == INVALID_HANDLE_VALUE){
fprintf(stdout, "\n Error occured: %d", GetLastError());
fprintf(stdout," getting handle: %s",filePathName);
continue;
}
GetFileSizeEx(handle, &li);
fileSize = (DWORD)li.QuadPart;
bytesToRead = (fileSize/g_bytesPerPhysicalSector)*g_bytesPerPhysicalSector;
buffer = static_cast<char *>(VirtualAlloc(0, bytesToRead, MEM_COMMIT, PAGE_READWRITE));
OVERLAPPED overlapped;
ZeroMemory(&overlapped, sizeof(overlapped));
OVERLAPPED * lpOverlapped = &overlapped;
success = ReadFile(handle, buffer, bytesToRead, &bytesRead, lpOverlapped);
if(!success && GetLastError() != ERROR_IO_PENDING){
fprintf(stdout, "\n Error occured: %d", GetLastError());
fprintf(stdout, "\n reading file %s",filePathName);
CloseHandle(handle);
continue;
}
else
handleMap[handle] = overlapped;
}
// Status check and bytes Read value
for(std::map<HANDLE, OVERLAPPED>::iterator iter = handleMap.begin(); iter != handleMap.end(); iter++)
{
HANDLE handle = iter->first;
OVERLAPPED * overlappedPtr = &(iter->second);
success = GetOverlappedResult(handle, overlappedPtr, &bytesRead, TRUE);
if(success)
{
/* bytesRead value in some cases is unexpectedly zero */
/* no file is of size zero or lesser than 512 bytes(physical volume sector size) */
totalBytesRead += bytesRead;
CloseHandle(handle);
}
}
return totalBytesRead;
}
With FILE_FLAG_NO_BUFFERING absent, totalBytesRead value is 57 MB. With the flag present, totalBytesRead value is much lower than 57 MB and keeps changing each time I run the code ranging from 2 MB to 15 MB.

Your calculation of bytesToRead will produce 0 as a result when the file size is less than g_bytesPerPhysicalSector. So for small files you are requesting 0 bytes.

Related

Invalid access to memory location with ReadFile

I have a file, C:\demo\Demo.txt, that has a simple "Hello, world" on it. I want to pass the path as argument to my app, open it with CreateFile, read it with ReadFile and show that line out on console. However, I am receiving an error code 998:
Invalid access to memory location.
This is my code:
int wmain(int argc, WCHAR **argv)
{
if (argc != 2)
{
fwprintf(stderr, L"\nWrong arguments. \n");
return 1;
}
// CreateFile function variables
HANDLE hSourceFile;
LPCWSTR fileName = (LPCWSTR)argv[1];
DWORD desiredAccess = FILE_GENERIC_READ;
DWORD shareMode = FILE_SHARE_READ;
DWORD creationDisposition = OPEN_EXISTING;
DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
//---------------------------------------------------------------
// Opening file for reading data
hSourceFile = CreateFileW(
fileName,
desiredAccess,
shareMode,
NULL,
creationDisposition,
flagsAndAttributes,
NULL);
if (hSourceFile != INVALID_HANDLE_VALUE)
{
wprintf(L"\nThe source file, %s, is open. \n", fileName);
}
else
{
wprintf(L"Error code: %u\n", GetLastError());
}
// ReadFile function variables
LPVOID dataRead=NULL;
DWORD bytesToRead = 100;
DWORD bytesWritten = 0;
//-----------------------------------------------------------------
// Reading data from file
if (!ReadFile(
hSourceFile,
dataRead,
bytesToRead,
&bytesWritten,
NULL))
{
wprintf(L"Error code: %u\n", GetLastError());
return 1;
}
wprintf(L"%s. \n", (LPWSTR)dataRead);
CloseHandle(hSourceFile);
return 0;
}
First time I use ReadFile, so no idea what I am doing wrong.
Can you help me?
ReadFile wants a pointer to a buffer into which it can write the data. You are passing NULL, so you get the error you see.
I would change the code to
// ReadFile function variables
static const DWORD bytesToRead = 100;
unsigned char dataRead[bytesToRead];
DWORD bytesWritten = 0;
//-----------------------------------------------------------------
// Reading data from file
if (!ReadFile(
hSourceFile,
dataRead,
bytesToRead,
&bytesWritten,
NULL))
{
wprintf(L"Error code: %u\n", GetLastError());
return 1;
}
The next problem you have, is that you are casting your pointer to LPWSTR, that is a pointer to a null-terminated wide string. Does your file contain that null termination? or do you need to add it yourself? Assuming the file doesn't contain the termination, you probably want:
// ReadFile function variables
static const DWORD bufferSize = 50;
WCHAR buffer[bufferSize+1]; // Leave room for null.
DWORD bytesWritten = 0;
//-----------------------------------------------------------------
// Reading data from file
if (!ReadFile(
hSourceFile,
buffer,
bufferSize*sizeof(WCHAR),
&bytesWritten,
NULL))
{
wprintf(L"Error code: %u\n", GetLastError());
return 1;
}
buffer[bytesWritten/sizeof(WCHAR)] = 0; // Null terminate.
wprintf(L"%s. \n", buffer); // Look ma! No cast needed.
You must allocate a memory buffer where you want to place read bytes to. Now you pointer dataRead points to nullptr, in other words nowhere, but you pass the size 100, that states your pointer refers to 100 byte allocated buffer, it is not truth.

Getting Connection-State from Bluetooth Low Energy Device from Device Manager

i'm developing on a Bluetooth Low Energy Device and i need to see in code if the device is connected or not.
First thing i noticed was that there is in the Devicemanager a Attribute "Verbunden"-> English: Connected and it says true or false if my device is connected or not. So i need to read that Attribute in my program.
What i have tried till now:
Getting all Devices with SetupDiGetClassDevs
Getting the FriendlyName with SetupDiGetDeviceRegistryProperty
Searching for my Device with the name.
That works.
Now i wanted to get that Connected-Attribute but i didn't find out what i have to use at SetupDiGetDeviceRegistryProperty.
SetupDiGetDeviceRegistryProperty is described here https://msdn.microsoft.com/en-us/library/windows/hardware/ff551967(v=vs.85).aspx
Maybe someone knows what is the right value for Property.
My Code:
int get_device_info( void )
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
FILE * devices = fopen("devices.txt", "a+");
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
//SPDRP_DEVICEDESC,
//SPDRP_CAPABILITIES,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
if(buffer)
{
if( strcmp("Name of Device",AnsiString(buffer).c_str())==0)
{
fprintf(devices,"Result:[%s]",AnsiString(buffer).c_str());
if (buffer) LocalFree(buffer);
}
}
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
fclose(devices);
return 0;
}
Instead of using SetupDiEnumDeviceInfo, you would try:
1. using SetupDiEnumDeviceInterfaces
2. using SetupDiGetDeviceInterfaceProperty
3. using SetupDiGetDeviceInterfacePropertyKeys to get a list of all Property Keys available for the interface
4. using SetupDiGetDeviceProperty and/or SetupDiGetDeviceRegistryProperty
Instead of using SPDRP_XXX constants, you would use DEVPROP, as defined in 'devpkey.h' ...
Below are a few examples taken from the log of a test prog I wrote to discover the whole thing:
DEVPROPNAME: DEVPKEY_DeviceInterface_Bluetooth_DeviceAddress
DEVPROPGUID: {2BD67D8B-8BEB-48D5-87E0-6CDA3428040A}
DEVPROPPID: 1
DEVPROPTYPE: DEVPROP_TYPE_STRING
Value: c026df001017
DEVPROPNAME: DEVPKEY_Device_Children
DEVPROPGUID: {4340A6C5-93FA-4706-972C-7B648008A5A7}
DEVPROPPID: 9
DEVPROPTYPE: DEVPROP_TYPE_STRING_LIST
Value:
BTHLEDevice\{00001800-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0001
BTHLEDevice\{00001801-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0008
BTHLEDevice\{00001809-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&000c
BTHLEDevice\{0000180f-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0010
BTHLEDevice\{0000180a-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0014
BTHLEDevice\{00001523-1212-efde-1523-785feabcd123}_c026df001017\8&2fd07168&1&0019
On a second subject, you are 'working' on the 'device' itself ( SetupDiGetClassDevs(&BluetoothInterfaceGUID...) [and then working on the \BTHLE\ tree in Registry].
After listing all GattServices of this device and getting their uuids, you could restart that iteration on the device_guid itself SetupDiGetClassDevs(&GattServiceGUID...) [and then working on the \BTHLEDevice\ tree in Registry].
Now, to answer your question, I'm still searching myself :) But I'm not really sure:
1) that it is a working (dynamic) information to know the connection state
2) that it is a 'Property' you can access by the above methods
I have found out a solution.
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);//DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);//DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
LPTSTR buffer1 = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( // Get Name
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
{
if(strcmp("Your Device",AnsiString(buffer).c_str())==0) //Found your device
{
//########
DEVPROPTYPE ulPropertyType;
DWORD dwSize;
ULONG devst;
// memset(devst,0,sizeof(devst));
bool err = SetupDiGetDeviceProperty( //Checking Connection State
hDevInfo,
&DeviceInfoData,
&DEVPKEY_Device_DevNodeStatus, //Connected(0x02000000)
&ulPropertyType,
(BYTE *) &devst,
sizeof(devst),
&dwSize,
0);
DWORD error;
error = GetLastError();
if (devst &0x02000000) {
//"Status: Getrennt "
}
else
{
//"Status: Verbunden"
}
Hope this snippet helps.

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

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 the code I linked to.)
So I call it as such:
//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' ...
}
}
but it always returns error code 87, or ERROR_INVALID_PARAMETER and my bytesReturned is always 0.
So what am I doing wrong?
The instructions for getting all disk volume extents are documented under the VOLUME_DISK_EXTENTS structure:
When the number of extents returned is greater than one (1), the error code ERROR_MORE_DATA is returned. You should call DeviceIoControl again, allocating enough buffer space based on the value of NumberOfDiskExtents after the first DeviceIoControl call.
The behavior, if you pass an output buffer, that is smaller than sizeof(VOLUME_DISK_EXTENTS) is also documented at IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS control code:
If the output buffer is less than sizeof(VOLUME_DISK_EXTENTS), the call fails, GetLastError returns ERROR_INSUFFICIENT_BUFFER, and lpBytesReturned is 0 (zero).
While this explains the returned value in lpBytesReturned, it doesn't explain the error code 87 (ERROR_INVALID_PARAMETER)1).
The following code will return the disk extents for all volumes:
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.
}
Additional references:
Why do some structures end with an array of size 1?
offsetof Macro
1) At a guess I would assume, that BYTE[1] begins at a memory address, that is not sufficiently aligned for the alignment requirements of VOLUME_DISK_EXTENTS.
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;
}
You are getting error code ERROR_INVALID_PARAMETER when you have invalid parameter, like its name says. In your case it should be bad handle because all others looks fine, if we expect that dwIoControlCode argument is IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, lpInBuffer and nInBufferSize are ignored.
In insufficient buffer you will get another error code mentioned in above comments.
Lets check what is saying documentation:
DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive A: with CreateFile, specify \.\a:. Alternatively, you can use the names \.\PhysicalDrive0, \.\PhysicalDrive1, and so on, to open handles to the physical drives on a system.
In other words, when you open handle with "C:\" instead of "\\.\c:" argument in CreateFile and use it in DeviceIoControl, the result is ERROR_INVALID_PARAMETER.

Windows 8.1 Bluetooth LE can't get device interface

I am trying to retrieve the name and handle of all paired bluetooth devices on a windows 8.1 machine.
I can get the name, but SetupDiEnumDeviceInterfaces always returns false. I read somewhere that I need to include DIGCF_DEVICEINTERFACE in the SetupDIGetClassDevs function, but it still doesn't work.
Here is my code:
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
&GUID_DEVCLASS_BLUETOOTH,
0, 0, DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return;//1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i,
&DeviceInfoData); i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
// Change the buffer size.
if (buffer) delete(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = new wchar_t[buffersize * 2];
}
else{
// Insert error handling here.
break;
}
}
HWND deviceList = GetDlgItem(GetActiveWindow(), LIST_BOX);
if (deviceList && buffersize > 0)
{
SendMessage(deviceList, LB_ADDSTRING, 0, (LPARAM)buffer);
}
if (buffer) delete(buffer);
// WORKS UNTIL HERE BUT ENUMERATING THROUGH INTERFACES ALWAYS RETURNS FALSE
SP_DEVICE_INTERFACE_DATA devIntData;
HDEVINFO hDevInfo2 = SetupDiGetClassDevs(
&GUID_DEVCLASS_BLUETOOTH,
0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (SetupDiEnumDeviceInterfaces(hDevInfo2,
&DeviceInfoData,
&GUID_BLUETOOTHLE_DEVICE_INTERFACE,
i,
&devIntData))
{
DWORD reqSize;
SP_DEVINFO_DATA buffer;
while (SetupDiGetDeviceInterfaceDetail(hDevInfo2,
&devIntData,
NULL,
NULL,
&reqSize,
&buffer))
{
OutputDebugString(L"DeviceINTERFACE");
}
}
}
I have tried putting the device enumeration outside of the name enumeration loop, but it still returns false also I would like the handle and the name to be associated so I would like them to be found in the same context.
If anyone has any sample code on a full bluetooth LE workflow in windows 8.1 (find name, find handles, find services, find characteristics, write to characteristics) and could share that with me I would greatly appreciate it. Thanks.
Figured it out, wasn't allocating memory for my buffers properly.
EDIT: Adding code
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
&GUID_DEVCLASS_BLUETOOTH,
0, 0, DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return;//1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i,
&DeviceInfoData); i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//This loop gets the name with SPDRP_FRIENDLYNAME
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
// Change the buffer size.
if (buffer) delete(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = new wchar_t[buffersize * 2];
}
else{
// Insert error handling here.
break;
}
}
DWORD DataT2;
LPTSTR buffer2 = NULL;
DWORD buffersize2 = 0;
//This Loop gets the Bluetooth Address with SPDRP_HARDWAREID
// NOTE: there is more information than just the address you will have
// to do some string manipulation to have just the address
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_HARDWAREID,
&DataT2,
(PBYTE)buffer2,
buffersize2,
&buffersize2))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
// Change the buffer size.
if (buffer2) delete(buffer2);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer2 = new wchar_t[buffersize2 * 2];
}
else{
// Insert error handling here.
break;
}
}
if (buffersize > 0)
{
//do what you need with the info
//name is in buffer
//address is in buffer2
}
}
Next i get the handles in a different function because you need to enumerate over Interfaces and not info with SetupDiEnumDeviceInterfaces instead of SetupDiEnumDeviceInfo in the for loop
Using the bluetooth address I match the two and store appropriately

Win32 API: how to read the serial, or exit within a timeout if wasn't a data

I need a function to read data from a serial port or return if none came within a time interval. E.g. on GNU/Linux you could use poll()orselect()+read(). Anything analogous in Windows?
Below is something I tried: it ought to work, but the function GetOverlappedResult() is either buggy or not well documented; instead of number of bytes read it reports nothing (not even zero, e.g. if I don't initialize the lpNumberOfBytesTransferred argument, it just contains junk).
// NOTE: the `file` should be opened in non-blocking mode
long ReadData(HANDLE file, char* buf, long szbuf, int msTimeout){
OVERLAPPED ReadState = {0};
ReadState.hEvent = CreateEvent(0, true, false, "☠");
if (!ReadState.hEvent){
PrintLastErr();
return -1;
}
unsigned long BytesRead = 0; //number of bytes was read
if (!ReadFile(file, buf, szbuf, &BytesRead, &ReadState)){
// This creates a reading event. Below we wait for it to complete, and cancel on timeout
if (GetLastError() != ERROR_IO_PENDING){
PrintLastErr();
return -1;
}
// No, I can't use WaitForSingleObject(), it exits with disregard to whether
// reading is ongoing or no more data left (e.g. in case of a serial port).
while(1) {
std::this_thread::sleep_for(std::chrono::milliseconds( msTimeout ));
unsigned long ret;
puts("Getting result");
if (!GetOverlappedResult(file, &ReadState, &ret, false)
&& GetLastError() != ERROR_IO_INCOMPLETE){
PrintLastErr();
return -1;
}
printf("result is %lu\n",ret);
if(ret == BytesRead){
return BytesRead;
}
BytesRead = ret;
}
} else { //completed immediately
printf("Bytes read %i\n");
assert(BytesRead <= LONG_MAX);
return (long)BytesRead;
}
}
Call SetCommTimeouts after you open the COM port. This sets ReadFile to return after an interval if no data is received. Then you simply call ReadFile. No overlap or event or GetOverlappedResult is needed.
It was really hard to figure; but thanks to a forum and lots of experimentation I finally found a way. Here's the code:
/**
* Opens a file in overlapped mode.
* \returns HANDLE to a file, or 0 on fail
*/
HANDLE OpenFile(const char* FileName){
HANDLE file = CreateFile( FileName,
GENERIC_READ | GENERIC_WRITE,
0, //we're greedy(or just lazy)
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (file == INVALID_HANDLE_VALUE){
return 0;
}
if(!SetCommMask(file, EV_RXCHAR)){ // set a mask for incoming characters event.
return 0;
}
return file;
}
/**
* Waits for data to arrive
* \param file a file opened in overlapped mode
* \param msTimeout is a maximum time to wait
* \returns -1 on system error, 0 on success, and 1 if time out
*/
int WaitForData(HANDLE file, unsigned long msTimeout){
int ret;
unsigned long Occured;//returns the type of an occured event
OVERLAPPED FileEvent = {0};
FileEvent.hEvent = CreateEvent(0, true, false, 0);
do{
if(!WaitCommEvent(file, &Occured, &FileEvent)){
if(GetLastError() != ERROR_IO_PENDING){
ret = -1;
break;
}
}
switch(WaitForSingleObject(FileEvent.hEvent, msTimeout)){
case WAIT_OBJECT_0: //the requested event happened
ret = 0; //a success
break;
case WAIT_TIMEOUT://time out
ret = 1;
break;
default://error in WaitForSingleObject
ret = -1;
break;
}
break;
}while(0);
CloseHandle(FileEvent.hEvent);
return ret;
}
/**
* Reads data from a file
* \param file a file opened in overlapped mode
* \param buf a buf for data
* \param szbuf size of buffer
* \returns number of bytes read or -1 on fail
*/
unsigned long ReadData(HANDLE file, char* buf, unsigned long szbuf){
int ret;
unsigned long BytesRead = 0; //number of bytes was read
OVERLAPPED ReadState = {0};
do{
ReadState.hEvent = CreateEvent(0, true, false, 0);
if (!GetOverlappedResult(file, &ReadState, &BytesRead, false)){//get how many bytes incame
ret = ULONG_MAX;
break;
}
if (ReadFile( file,
buf,
(BytesRead<=szbuf) ? BytesRead : szbuf,
0,
&ReadState) == 0){
ret = ULONG_MAX;
break;
}
ret = BytesRead;
}while(0);
CloseHandle(ReadState.hEvent);
return ret;
}
So, how does it work?
Open the port with FILE_FLAG_OVERLAPPED flag
Use SetCommMask() to set EV_RXCHAR as the only events we're interested in is incoming data.
In a cycle wait for data for a specified amount of time, read if anything came.
Also #ScottMcP-MVP's answer is wrong: there's no use to SetCommTimeouts(). On the first sight this function and COMMTIMEOUTS looks like exactly what you'd want though, which drove me to hours of confusion. ☺