Sending audio data to a user mode application from Virtual Audio Driver - c++

I have my current task to send audio data from virtual audio driver to user mode application.
First I need to create an instance of that virtual audio driver from an user mode application ...
Please see the code snippet below
//Generating the device info
//That works
SetupDiGetClassDevs( &KSCATEGORY_AUDIO, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE );
//Enumerating device interface
//That works
SetupDiEnumDeviceInterfaces(dev_info, &DeviceInfoData, &KSCATEGORY_AUDIO, i, &did);
//Getting the path to device (pdd)
//That works
bRes = SetupDiGetDeviceInterfaceDetail(dev_info, &did, pdd, required_size, NULL, NULL);
//Handle to the device
//That works
HANDLE hHandle = CreateFile( pdd->DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
//Passing the Handle
//That fails
//PData is the Out buffer where we get the driver sending value
bool bRc = DeviceIoControl ( hHandle,
(DWORD)IOCTL_SIOCTL_METHOD_OUT_DIRECT, NULL, 0, &PData,
sizeof( PData), &bytesReturned, NULL );
Could you please point out the reason?
For that above user mode application I used to write a piece of IOCTL code as follows...
NTSTATUS IoCtlHandler(PDEVICE_OBJECT pDeviceObject, PIRP Irp)
{
PAGED_CODE();
UINT dwDataSize = 0;
PBYTE pReturnData ;
pReturnData = (PBYTE)"IOCTL - Direct In I/O From Kernel Welcome to the world of Portcl";
dwDataSize = sizeof("IOCTL - Direct In I/O From Kernel! Welcome to the world of Portcl");
//Point to a certain IRP location
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
if(pIoStackIrp)
{
switch(pIoStackIrp->Parameters.DeviceIoControl.IoControlCode)
{
//that ioctl i pass inside the DeviceIoControl function
case IOCTL_SIOCTL_METHOD_OUT_DIRECT:
{
pOutputBuffer = NULL;
NtStatus = STATUS_UNSUCCESSFUL;
if(Irp->MdlAddress)
{
pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp- >MdlAddress, NormalPagePriority);
}
RtlCopyBytes(pOutputBuffer, pReturnData, dwDataSize);
break;
}
}
}
Irp->IoStatus.Information = dwDataSize;
Irp->IoStatus.Status = NtStatus;
//After all complete the IRP structure
//As it is a adapter driver we pass this information to handle by adapter itself
NtStatus = PcDispatchIrp(pDeviceObject, Irp);
return NtStatus;
}
Could you please point out my mistake.For my testing purpose I am just passing a string (IOCTL - Direct In I/O From Kernel Welcome to the world of Portcl") to the output buffer later on I would replace it by audio data...Why deviceIoControl fails though I get the string whatever I passes in driver but the bytesReturn value always coming as a random value with bRes value always false...

Related

Irp information return PVOID

I'm writing Windows kernel driver in C++ and I have to return PVOID which has information about address in memory. Unfortunately, Irp->IoStatus.Information is only able to handle ULONG which results in shortened address for example: 0x2e341990000 is shortened to 0x41990000. It is very important to keep the address full otherwise user mode client would not be able to find address in memory. Is there any way to return full PVOID to client?
Driver code:
NTSTATUS status = STATUS_SUCCESS;
ULONG bytesIO = 0;
auto stack = IoGetCurrentIrpStackLocation(Irp);
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SHELL:
{
auto len = stack->Parameters.DeviceIoControl.InputBufferLength;
if (len < sizeof(Data))
{
DbgPrint("[-] Received too small buffer\n");
status = STATUS_BUFFER_TOO_SMALL;
break;
}
auto data = (Data*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
if (data == nullptr)
{
DbgPrint("[-] Received empty buffer\n");
status = STATUS_INVALID_PARAMETER;
break;
}
PVOID buf = SetMemoryAddress(data);
bytesIO = (ULONG)buf; // Buffer is shortened here
DbgBreakPoint();
break;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = bytesIO;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
Client code:
HANDLE hDevice;
BOOL success;
hDevice = CreateFile(L"\\\\.\\Driver", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
return FALSE;
Data data;
// Fill data structure here
PVOID retn;
PVOID buffer = { 0 };
success = DeviceIoControl(hDevice, IOCTL_SHELL, &data, sizeof(data), NULL, 0, (LPDWORD)&retn, NULL);
printf("0x%x\n", retn); // Shortened address
return success;
I tried using buffered IOCTL methods.
Your retn variable is not a memory address it is the number of bytes returned from the DeviceIoControl call. It is also of double word size (32bits) not equivalent with a void pointer on a modern 64bit machine.
The output data is written into the fifth argument which is optional and you seem to have provided NULL to.
You might want to initialize the value at retn so you can see if it is changed by your DeviceIoControl call, even if you provide nowhere to write output to.
DWORD bufffer_size_ouf = 0;
PVOID retn = (PVOID)&buff_size_out;

time out for USB ReadFile() replacing Serial Port code used with serial communications device

I'm working on extending a receipt printing Serial Port (COM) interface for a thermal receipt printer to use a USB interface without needing a Virtual Serial Port. I have a working prototype that will enumerate over the attached USB devices, locate the USB path for a device with a specific vendor id and product id, and open a connection to the device using CreateFile().
The existing Serial Port code uses the Windows API wrapped in a set of functions. The approach I'm taking is to add additional code using the same set of functions but that depend on a USB connection rather than a Serial Port connection. I have previously used the same approach to allow the use of a kitchen printer over either a Serial Port or over a WiFi/LAN connection with minimal changes to existing code successfully.
Unfortunately the existing code that uses the function library depends on the functions to use ReadFile() with a time out specified so that if the thermal printer does not respond to a status request within a reasonable time, the application can mark it as down and allow operations to continue or to use a backup or secondary printer.
How do I specify a time out for a ReadFile() on a file handle from CreateFile() that opens a connection to a communications devices using a USB pathname?
A consideration is this is multi-threaded code used for more than one serial communications device (receipt printer, kitchen printer, scale, etc.) however a thread will have exclusive access to a particular device (kitchen printing functionality opens serial port to kitchen printer only, scale reading functionality opens serial port to scale only, etc.).
In the existing Serial Port code, the function used to set timeouts, SetCommTimeouts(), for a Serial Port connection opened with CreateFile() does not work for a USB connection opened with CreateFile() (see SetupComm, SetCommState, SetCommTimeouts fail with USB device). This means some other mechanism is needed to provide a way to allow for an I/O failure due to a time out when using a USB device pathname.
We are using the following code segment to open a Serial Port, whether to a hardware COM port or a Virtual Serial Port emulating a hardware COM port:
// see Microsoft document HOWTO: Specify Serial Ports Larger than COM9.
// https://support.microsoft.com/en-us/kb/115831
// CreateFile() can be used to get a handle to a serial port. The "Win32 Programmer's Reference" entry for "CreateFile()"
// mentions that the share mode must be 0, the create parameter must be OPEN_EXISTING, and the template must be NULL.
//
// CreateFile() is successful when you use "COM1" through "COM9" for the name of the file;
// however, the value INVALID_HANDLE_VALUE is returned if you use "COM10" or greater.
//
// If the name of the port is \\.\COM10, the correct way to specify the serial port in a call to
// CreateFile() is "\\\\.\\COM10".
//
// NOTES: This syntax also works for ports COM1 through COM9. Certain boards will let you choose
// the port names yourself. This syntax works for those names as well.
wsprintf(wszPortName, TEXT("\\\\.\\COM%d"), usPortId);
/* Open the serial port. */
/* avoid to failuer of CreateFile */
for (i = 0; i < 10; i++) {
hHandle = CreateFile (wszPortName, /* Pointer to the name of the port, PifOpenCom() */
GENERIC_READ | GENERIC_WRITE, /* Access (read-write) mode */
0, /* Share mode */
NULL, /* Pointer to the security attribute */
OPEN_EXISTING,/* How to open the serial port */
0, /* Port attributes */
NULL); /* Handle to port with attribute */
/* to copy */
/* If it fails to open the port, return FALSE. */
if ( hHandle == INVALID_HANDLE_VALUE ) { /* Could not open the port. */
dwError = GetLastError ();
if (dwError == ERROR_FILE_NOT_FOUND || dwError == ERROR_INVALID_NAME || dwError == ERROR_ACCESS_DENIED) {
LeaveCriticalSection(&g_SioCriticalSection);
// the COM port does not exist. probably a Virtual Serial Communications Port
// from a USB device which was either unplugged or turned off.
// or the COM port or Virtual Serial Communications port is in use by some other application.
return PIF_ERROR_COM_ACCESS_DENIED;
}
PifLog (MODULE_PIF_OPENCOM, LOG_ERROR_PIFSIO_CODE_01);
PifLog (MODULE_ERROR_NO(MODULE_PIF_OPENCOM), (USHORT)dwError);
PifLog(MODULE_DATA_VALUE(FAULT_AT_PIFOPENCOM), usPortId);
PifSleep(500);
} else {
break;
}
}
if ( hHandle == INVALID_HANDLE_VALUE ) { /* Could not open the port. */
wsprintf(wszDisplay, TEXT("CreateFile, COM%d, Last Error =%d\n"), usPortId, dwError);
OutputDebugString(wszDisplay);
LeaveCriticalSection(&g_SioCriticalSection);
return PIF_ERROR_COM_ERRORS;
}
/* clear the error and purge the receive buffer */
dwError = (DWORD)(~0); // set all error code bits on
ClearCommError(hHandle, &dwError, NULL);
PurgeComm( hHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
The ReadFile() is wrapped within a function and looks like:
fResult = ReadFile(hHandle, pBuffer, (DWORD)usBytes, &dwBytesRead, NULL);
if (PifSioCheckPowerDown(usPort, aPifSioTable) == TRUE) {
return PIF_ERROR_COM_POWER_FAILURE;
}
if (fResult) {
if (!dwBytesRead) return PIF_ERROR_COM_TIMEOUT;
return (SHORT)dwBytesRead;
} else {
SHORT sErrorCode = 0; // error code from PifSubGetErrorCode(). must call after GetLastError().
dwError = GetLastError();
PifLog (MODULE_PIF_READCOM, LOG_ERROR_PIFSIO_CODE_06);
PifLog (MODULE_ERROR_NO(MODULE_PIF_READCOM), (USHORT)dwError);
sErrorCode = PifSubGetErrorCode(hHandle);
PifLog (MODULE_ERROR_NO(MODULE_PIF_READCOM), (USHORT)abs(sErrorCode));
PifLog (MODULE_DATA_VALUE(MODULE_PIF_READCOM), usPort);
return (sErrorCode);
}
I found a number of similar posted questions which involved pipes however the same approach of using overlapped I/O applies.
Breaking ReadFile() blocking - Named Pipe (Windows API)
Win32 API: ReadFile timeout
Device driver: Windows ReadFile function timeout
I also found the following article on the web Peter's blog: Getting a handle on usbprint.sys which provides code and a description of how to find a USB pathname for a USB connected device. I have used some of that code sample in the class below.
I also found an article on codeproject.com, Enumerating windows device by Chuan-Liang Teng which contained an example of enumerating over the connected USB devices and interrogating various settings and details about the devices. The code from that article, though old, was helpful though not necessary for this particular application.
I have a prototype C++ class using overlapped I/O which seems to be replicating the behavior seen with a Serial Port connection using a USB connection to a thermal printer. The full source and Visual Studio 2017 solution and project files are in my GitHub repository https://github.com/RichardChambers/utilities_tools/tree/main/UsbWindows as this snip has the most pertinent parts.
I have done a simple test with modified code in the point of sale application and am now in the process of integrating this into the existing thermal receipt printer source code which already works with a Serial Port.
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <iostream>
// This is the GUID for the USB device class.
// It is defined in the include file Usbiodef.h of the Microsoft Windows Driver Kit.
// See also https://msdn.microsoft.com/en-us/library/windows/hardware/ff545972(v=vs.85).aspx which
// provides basic documentation on this GUID.
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
class UsbSerialDevice
{
public:
// See https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-overlapped-i-o?redirectedfrom=MSDN
// to implement time outs for Write and for Read.
UsbSerialDevice(const wchar_t* wszVendorIdIn = nullptr);
~UsbSerialDevice();
int CreateEndPoint(const wchar_t* wszVendorId = nullptr, DWORD dwDesiredAccess = (GENERIC_READ | GENERIC_WRITE));
void CloseEndPoint(void);
int ListEndPoint(const wchar_t* wszVendorIdIn);
int ReadStream(void* bString, size_t nBytes);
int WriteStream(void* bString, size_t nBytes);
DWORD SetWriteTimeOut(DWORD msTimeout);
DWORD SetReadTimeOut(DWORD msTimeout);
DWORD m_dwError; // GetLastError() for last action
DWORD m_dwErrorWrite; // GetLastError() for last write
DWORD m_dwErrorRead; // GetLastError() for last read
DWORD m_dwBytesWritten; // number of bytes last write
DWORD m_dwBytesRead; // number of bytes last read
DWORD m_dwWait; // WaitForSingleObject() return value
private:
HANDLE m_hFile;
OVERLAPPED m_oOverlap;
COMMTIMEOUTS m_timeOut;
const unsigned short m_idLen = 255;
wchar_t m_wszVendorId[255 + 1] = { 0 };
};
UsbSerialDevice::UsbSerialDevice(const wchar_t* wszVendorIdIn) :
m_dwError(0),
m_dwErrorWrite(0),
m_dwErrorRead(0),
m_dwBytesWritten(0),
m_dwBytesRead(0),
m_dwWait(0),
m_hFile(INVALID_HANDLE_VALUE)
{
memset(&m_oOverlap, 0, sizeof(m_oOverlap));
m_oOverlap.hEvent = INVALID_HANDLE_VALUE;
if (wszVendorIdIn != nullptr) ListEndPoint(wszVendorIdIn);
}
void UsbSerialDevice::CloseEndPoint(void )
{
if (m_hFile && m_hFile != INVALID_HANDLE_VALUE) CloseHandle(m_hFile);
if (m_oOverlap.hEvent && m_oOverlap.hEvent != INVALID_HANDLE_VALUE) CloseHandle(m_oOverlap.hEvent);
}
UsbSerialDevice::~UsbSerialDevice()
{
CloseEndPoint();
}
/*
* Returns: -1 - file handle is invalid
* 0 - write failed. See m_dwErrorWrite for GetLastError() value
* 1 - write succedded.
*/
int UsbSerialDevice::WriteStream(void* bString, size_t nBytes)
{
SetLastError(0);
m_dwError = m_dwErrorWrite = 0;
m_dwBytesWritten = 0;
m_dwWait = WAIT_FAILED;
if (m_hFile && m_hFile != INVALID_HANDLE_VALUE) {
BOOL bWrite = WriteFile(m_hFile, bString, nBytes, 0, &m_oOverlap);
m_dwError = m_dwErrorWrite = GetLastError();
if (!bWrite && m_dwError == ERROR_IO_PENDING) {
SetLastError(0);
m_dwError = m_dwErrorWrite = 0;
m_dwWait = WaitForSingleObject(m_oOverlap.hEvent, m_timeOut.WriteTotalTimeoutConstant);
BOOL bCancel = FALSE;
switch (m_dwWait) {
case WAIT_OBJECT_0: // The state of the specified object is signaled.
break;
case WAIT_FAILED: // The function has failed. To get extended error information, call GetLastError.
m_dwError = m_dwErrorWrite = GetLastError();
bCancel = CancelIo(m_hFile);
break;
case WAIT_TIMEOUT: // The time-out interval elapsed, and the object's state is nonsignaled.
case WAIT_ABANDONED: // thread owning mutex terminated before releasing or signaling object.
bCancel = CancelIo(m_hFile);
m_dwError = m_dwErrorRead = ERROR_COUNTER_TIMEOUT;
break;
}
bWrite = GetOverlappedResult(m_hFile, &m_oOverlap, &m_dwBytesRead, FALSE);
}
return bWrite; // 0 or FALSE if failed, 1 or TRUE if succeeded.
}
return -1;
}
/*
* Returns: -1 - file handle is invalid
* 0 - read failed. See m_dwErrorRead for GetLastError() value
* 1 - read succedded.
*/
int UsbSerialDevice::ReadStream(void* bString, size_t nBytes)
{
SetLastError(0);
m_dwError = m_dwErrorRead = 0;
m_dwBytesRead = 0;
m_dwWait = WAIT_FAILED;
if (m_hFile && m_hFile != INVALID_HANDLE_VALUE) {
BOOL bRead = ReadFile(m_hFile, bString, nBytes, &m_dwBytesRead, &m_oOverlap);
m_dwError = m_dwErrorRead = GetLastError();
if (!bRead && m_dwError == ERROR_IO_PENDING) {
SetLastError(0);
m_dwError = m_dwErrorRead = 0;
m_dwWait = WaitForSingleObject(m_oOverlap.hEvent, m_timeOut.ReadTotalTimeoutConstant);
BOOL bCancel = FALSE;
switch (m_dwWait) {
case WAIT_OBJECT_0: // The state of the specified object is signaled.
break;
case WAIT_FAILED: // The function has failed. To get extended error information, call GetLastError.
m_dwError = m_dwErrorWrite = GetLastError();
bCancel = CancelIo(m_hFile);
break;
case WAIT_TIMEOUT: // The time-out interval elapsed, and the object's state is nonsignaled.
case WAIT_ABANDONED: // thread owning mutex terminated before releasing or signaling object.
bCancel = CancelIo(m_hFile);
m_dwError = m_dwErrorRead = ERROR_COUNTER_TIMEOUT;
break;
}
bRead = GetOverlappedResult(m_hFile, &m_oOverlap, &m_dwBytesRead, FALSE);
}
return bRead; // 0 or FALSE if failed, 1 or TRUE if succeeded.
}
return -1;
}
int UsbSerialDevice::ListEndPoint(const wchar_t* wszVendorIdIn)
{
m_dwError = ERROR_INVALID_HANDLE;
if (wszVendorIdIn == nullptr) return 0;
HDEVINFO hDevInfo;
// we need to make sure the vendor and product id codes are in lower case
// as this is needed for the CreateFile() function to open the connection
// to the USB device correctly. this lower case conversion applies to
// any alphabetic characters in the identifier.
//
// for example "VID_0FE6&PID_811E" must be converted to "vid_0fe6&pid_811e"
wchar_t wszVendorId[256] = { 0 };
for (unsigned short i = 0; i < 255 && (wszVendorId[i] = towlower(wszVendorIdIn[i])); i++);
// We will try to get device information set for all USB devices that have a
// device interface and are currently present on the system (plugged in).
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (hDevInfo != INVALID_HANDLE_VALUE)
{
DWORD dwMemberIdx;
BOOL bContinue = TRUE;
SP_DEVICE_INTERFACE_DATA DevIntfData;
// Prepare to enumerate all device interfaces for the device information
// set that we retrieved with SetupDiGetClassDevs(..)
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;
// Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this
// function causes GetLastError() to return ERROR_NO_MORE_ITEMS. With each
// call the dwMemberIdx value needs to be incremented to retrieve the next
// device interface information.
for (BOOL bContinue = TRUE; bContinue; ) {
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
SP_DEVINFO_DATA DevData;
DWORD dwSize;
dwMemberIdx++;
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, dwMemberIdx, &DevIntfData);
if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
// As a last step we will need to get some more details for each
// of device interface information we are able to retrieve. This
// device interface detail gives us the information we need to identify
// the device (VID/PID), and decide if it's useful to us. It will also
// provide a DEVINFO_DATA structure which we can use to know the serial
// port name for a virtual com port.
DevData.cbSize = sizeof(DevData);
// Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with
// a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize
// of zero, and a valid RequiredSize variable. In response to such a call,
// this function returns the required buffer size at dwSize.
SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
// Allocate memory for the DeviceInterfaceDetail struct. Don't forget to
// deallocate it later!
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, DevIntfDetailData, dwSize, &dwSize, &DevData))
{
if (wcsstr(DevIntfDetailData->DevicePath, wszVendorId)) {
wcscpy_s(m_wszVendorId, DevIntfDetailData->DevicePath);
}
}
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
return 0;
}
int UsbSerialDevice::CreateEndPoint(const wchar_t* wszVendorIdIn, DWORD dwDesiredAccess)
{
if (wszVendorIdIn) {
ListEndPoint(wszVendorIdIn);
}
m_dwError = ERROR_INVALID_HANDLE;
// Finally we can start checking if we've found a useable device,
// by inspecting the DevIntfDetailData->DevicePath variable.
//
// The DevicePath looks something like this for a Brecknell 67xx Series Serial Scale
// \\?\usb#vid_1a86&pid_7523#6&28eaabda&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
//
// The VID for a particular vendor will be the same for a particular vendor's equipment.
// The PID is variable for each device of the vendor.
//
// As you can see it contains the VID/PID for the device, so we can check
// for the right VID/PID with string handling routines.
// See https://github.com/Microsoft/Windows-driver-samples/blob/master/usb/usbview/vndrlist.h
// See https://blog.peter.skarpetis.com/archives/2005/04/07/getting-a-handle-on-usbprintsys/
// which describes a sample USB thermal receipt printer test application.
SetLastError(0);
m_hFile = CreateFile(m_wszVendorId, dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, 0);
if (m_hFile == INVALID_HANDLE_VALUE) {
m_dwError = GetLastError();
// wprintf(_T(" CreateFile() failed. GetLastError() = %d\n"), m_dwError);
}
else {
m_oOverlap.hEvent = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
m_timeOut.ReadIntervalTimeout = 0;
m_timeOut.ReadTotalTimeoutMultiplier = 0;
m_timeOut.ReadTotalTimeoutConstant = 5000;
m_timeOut.WriteTotalTimeoutMultiplier = 0;
m_timeOut.WriteTotalTimeoutConstant = 5000;
m_dwError = 0; // GetLastError();
return 1;
}
return 0;
}

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 connect to the bluetooth low energy device

I am writing program for Win 8 tablet. I need to connect an external BLE device.
The device is already paired with Windows and I can see it in Device Manager. But I can not figure out how to connect it.
With SetupDiEnumDeviceInfo and SetupDiGetDeviceProperty I can get some information about the BLE-device, but to perform, e.g. BluetoothGATTGetServices
Handle device requires. I do not know where to take it. Perhaps i can use CreateFile, but it is not clear that the substitute as the first argument lpFileName.
Here's a piece of code with which I'm looking for my device.
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
&BluetoothClassGUID, /* 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;
}
}
/* Here i just compare by name is this my device or not */
...
/* Here i just compare by name is this my device or not */
if (buffer) delete(buffer);
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return; //1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
return;// 0;
I moved a little further, but still i can't get the data from device.
To obtain "Device Interface Path" had to use the other functions:
SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces and SetupDiGetDeviceInterfaceDetail.
Next, with CreateFile I get HANDLE BLE-device.
hComm = CreateFile(pInterfaceDetailData->DevicePath, GENERIC_WRITE | GENERIC_READ,NULL,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
Next using WinAPI BluetoothGATTGetServices and BluetoothGATTGetCharacteristics I get the appropriate structures.
But when trying to get the property value with BluetoothGATTGetCharacteristicsValue, I get ERROR_ACCESS_DENIED.
And then I do not know what to do. What could be wrong?
Andrey, I think the problem is that your device is not connected and BluetoothGATTGetCharacteristicsValue is not triggering a connection.
Try manually to connect your device using Windows tools. I've the following flow that helps me: Unpair device, Pair device -> It should appear as connected ( it worked in my case ;) )
Anyway, If this is not helping, try to run "As Administrator", this helps in some cases.
Good luck!!!
Note: Would be very interested to know how to retrievethe device path for the BTLE device in order to call BluetoothGATTGetServices?
gattServiceGUID is any long form BLE UUID supported by your device.
"{00001803-0000-1000-8000-00805F9B34FB"} can be used to open a handle to the Link Loss service if supported by the device you are trying to open
HDEVINFO hDevInfo = SetupDiGetClassDevs(&gattServiceGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo != INVALID_HANDLE_VALUE)
{
SP_DEVICE_INTERFACE_DATA interfaceData;
ZeroMemory(&interfaceData,sizeof(SP_DEVICE_INTERFACE_DATA));
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (DWORD dwDeviceIndex = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &gattServiceGUID, dwDeviceIndex, &interfaceData); dwDeviceIndex++)
{
dwDeviceCount++;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, NULL, 0, &dwBytesNeeded, NULL);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pInterfaceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) new byte[dwBytesNeeded];
SP_DEVINFO_DATA spDeviceInfoData = { sizeof(SP_DEVINFO_DATA) };
ZeroMemory(pInterfaceDetail, sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA));
pInterfaceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
// grab the interface detail
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, pInterfaceDetail, dwBytesNeeded, NULL, &spDeviceInfoData) == TRUE)
{
// request a handle to the GATT service path
m_hGattServiceHandle = CreateFile(pInterfaceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (m_hGattServiceHandle != INVALID_HANDLE_VALUE)
{
now you can drill down the characteristics and descriptors with the m_hGattServiceHandle
}
}
}
}
}

WriteFile to Parallel port hangs

I am writing an activex control that will access the parallel port and write the bytes to it. I am able to open the port succesfully but when i write it hangs at WriteFile function. Did i miss anything here? I am using Windows 7
HANDLE portHwd = CreateFile( _T("\\\\.\\LPT1" ),
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (portHwd)
{
char outBuffer[] = _T("This is a test\r\n");
int sz_buffer = strlen(outBuffer);
DWORD bytes_written;
if (!WriteFile( portHwd,
outBuffer,
sz_buffer ,
&bytes_written,
NULL))
{
CloseHandle(portHwd);
GetLastError();
return 1;
}
CloseHandle(portHwd);
}
If the port's output buffer is full then WriteFile will hang until there is room to complete your request. Is there something attached to the port and reading from it?