cannot display the receive byte - c++

I have to do serial communication in win32.
I can read no of bytes but cannot display the read data.
I have used following function to read the data
DWORD serial::ReadTest()
{
char inBuffer[BUF_SIZE];
DWORD nBytesToRead = BUF_SIZE;
DWORD dwBytesRead = 0;
ReadFile(serialHandle,
inBuffer,
nBytesToRead,
&dwBytesRead,
NULL);
MessageBox(NULL,"ReadFile completed", _T("Read"), NULL);
return dwBytesRead;
}
I have done following things that displays the number of read bytes
{
DWORD dwReturnValue;
dwReturnValue = serialObj.ReadTest();
printf( "\nRead complete. Bytes read: %d\n", dwReturnValue);
char temp[255];
sprintf(temp,_T("%X"),dwReturnValue);
//to display in the static text
HWND hWnd = GetDlgItem(hDlg, IDC_STATIC_READ); //
if ( hWnd )
{
SetWindowText(hWnd, temp);
}
}
I have to display the actual read data. I have searched and tried different options like char* but it is not working . Can someone suggest me the appropriate way of displaying the actual data. I am using hex mode for sending in other side and i should receive hex data here in this program.

First you need to change the ReadTest function, to have the buffer and buffer length as arguments:
WORD serial::ReadTest(char* inBuffer, const DWORD nBytesToRead)
{
DWORD dwBytesRead = 0;
...
}
Then you declare the buffer in the code calling the ReadTest function:
char inBuffer[BUF_SIZE];
DWORD nBytesRead = serialObj.ReadTest(inBuffer, sizeof(inBuffer));
Now the inBuffer array contains nBytesRead bytes.

for example if
inBuffer[BUF_SIZE] = {0x24, 0x21, 0x23, 0x24}
I am trying to send hex value from the other side.
then i can print these values one by one using:
for (int i=0; i<nBytesRead; i++)
{
char temp[255];
sprintf(temp,_T("%X"),inBuffer[i]);
//to display in the static text
HWND hWnd = GetDlgItem(hDlg, IDC_STATIC_READ); //
if ( hWnd )
{
SetWindowText(hWnd, temp);
}
}
here i have printed each item. how to print it as a single block.

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.

Recovering Detoured Library Functions

The question is fairly straight forward, what I'm trying to do is restore my process' detoured functions.
When I say detoured I mean the usual jmp instruction to an unknown location.
For example, when the ntdll.dll export NtOpenProcess() is not detoured, the first 5 bytes of the instruction of the function are along the lines of mov eax, *.
(The * offset depending on the OS version.)
When it gets detoured, that mov eax, * turns into a jmp.
What I'm trying to do is restore their bytes to what they were originally before any memory modifications.
My idea was to try and read the information I need from the disk, not from memory, however I do not know how to do that as I'm just a beginner.
Any help or explanation is greatly welcomed, if I did not explain my problem correctly please tell me!
I ended up figuring it out.
Example on NtOpenProcess.
Instead of restoring the bytes I decided to jump over them instead.
First we have to define the base of ntdll.
/* locate ntdll */
#define NTDLL _GetModuleHandleA("ntdll.dll")
Once we've done that, we're good to go. GetOffsetFromRva will calculate the offset of the file based on the address and module header passed to it.
DWORD GetOffsetFromRva(IMAGE_NT_HEADERS * nth, DWORD RVA)
{
PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(nth);
for (unsigned i = 0, sections = nth->FileHeader.NumberOfSections; i < sections; i++, sectionHeader++)
{
if (sectionHeader->VirtualAddress <= RVA)
{
if ((sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) > RVA)
{
RVA -= sectionHeader->VirtualAddress;
RVA += sectionHeader->PointerToRawData;
return RVA;
}
}
}
return 0;
}
We call this to get us the file offset that we need in order to find the original bytes of the function.
DWORD GetExportPhysicalAddress(HMODULE hmModule, char* szExportName)
{
if (!hmModule)
{
return 0;
}
DWORD dwModuleBaseAddress = (DWORD)hmModule;
IMAGE_DOS_HEADER* pHeaderDOS = (IMAGE_DOS_HEADER *)hmModule;
if (pHeaderDOS->e_magic != IMAGE_DOS_SIGNATURE)
{
return 0;
}
IMAGE_NT_HEADERS * pHeaderNT = (IMAGE_NT_HEADERS *)(dwModuleBaseAddress + pHeaderDOS->e_lfanew);
if (pHeaderNT->Signature != IMAGE_NT_SIGNATURE)
{
return 0;
}
/* get the export virtual address through a custom GetProcAddress function. */
void* pExportRVA = GetProcedureAddress(hmModule, szExportName);
if (pExportRVA)
{
/* convert the VA to RVA... */
DWORD dwExportRVA = (DWORD)pExportRVA - dwModuleBaseAddress;
/* get the file offset and return */
return GetOffsetFromRva(pHeaderNT, dwExportRVA);
}
return 0;
}
Using the function that gets us the file offset, we can now read the original export bytes.
size_t ReadExportFunctionBytes(HMODULE hmModule, char* szExportName, BYTE* lpBuffer, size_t t_Count)
{
/* get the offset */
DWORD dwFileOffset = GetExportPhysicalAddress(hmModule, szExportName);
if (!dwFileOffset)
{
return 0;
}
/* get the path of the targetted module */
char szModuleFilePath[MAX_PATH];
GetModuleFileNameA(hmModule, szModuleFilePath, MAX_PATH);
if (strnull(szModuleFilePath))
{
return 0;
}
/* try to open the file off the disk */
FILE *fModule = fopen(szModuleFilePath, "rb");
if (!fModule)
{
/* we couldn't open the file */
return 0;
}
/* go to the offset and read it */
fseek(fModule, dwFileOffset, SEEK_SET);
size_t t_Read = 0;
if ((t_Read = fread(lpBuffer, t_Count, 1, fModule)) == 0)
{
/* we didn't read anything */
return 0;
}
/* close file and return */
fclose(fModule);
return t_Read;
}
And we can retrieve the syscall index from the mov instruction originally placed in the first 5 bytes of the export on x86.
DWORD GetSyscallIndex(char* szFunctionName)
{
BYTE buffer[5];
ReadExportFunctionBytes(NTDLL, szFunctionName, buffer, 5);
if (!buffer)
{
return 0;
}
return BytesToDword(buffer + 1);
}
Get the NtOpenProcess address and add 5 to trampoline over it.
DWORD _ptrNtOpenProcess = (DWORD) GetProcAddress(NTDLL, "NtOpenProcess") + 5;
DWORD _oNtOpenProcess = GetSyscallIndex("NtOpenProcess");
The recovered/reconstructed NtOpenProcess.
__declspec(naked) NTSTATUS NTAPI _NtOpenProcess
(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
) {
__asm
{
mov eax, [_oNtOpenProcess]
jmp dword ptr ds : [_ptrNtOpenProcess]
}
}
Let's call it.
int main()
{
printf("NtOpenProcess %x index: %x\n", _ptrNtOpenProcess, _oNtOpenProcess);
uint32_t pId = 0;
do
{
pId = GetProcessByName("notepad.exe");
Sleep(200);
} while (pId == 0);
OBJECT_ATTRIBUTES oa;
CLIENT_ID cid;
cid.UniqueProcess = (HANDLE)pId;
cid.UniqueThread = 0;
InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
HANDLE hProcess;
NTSTATUS ntStat;
ntStat = _NtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, &cid);
if (!NT_SUCCESS(ntStat))
{
printf("Couldn't open the process. NTSTATUS: %d", ntStat);
return 0;
}
printf("Successfully opened the process.");
/* clean up. */
NtClose(hProcess);
getchar();
return 0;
}

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.

Enumerating all subkeys and values in a Windows registry key

I am trying to write a Windows application that gives me back all the subkeys and values given a certain key. I've written code that appears to work as far as providing subkeys within the given key, but doesn't work at properly enumerating the values; it successfully enumerates subkeys without values and returns the results in kind of tabbed tree arrangement. However, when enumerating values, the program gives back a random value for each value present (the same random value each time), and then crashes afterwards with a debug error.
It's intended output is basically:
(1) KEY
(1) SUBKEY
(1) SUBKEYWITHINSUBKEY
Code: value1data
Code: value2data
Code: value3data
(2) SUBKEY
(1) SUBKEYWITHINSUBKEY
(3) SUBKEY
...and so on.
The output I get instead is something like:
(1) KEY
(1) SUBKEY
(1) SUBKEYWITHINSUBKEY
Code: someValue
Code: someValue
Code: someValue
(...and then the crash)
This is followed with the following error:
"Debug Error! "Run-Time Check Failure #2 - Stack around the variable 'valNameLen' was corrupted."
The code is a bit messy currently (I'm a Windows API newbie), but if anyone could show me what I'm doing wrong, or critique my coding style in anyway they feel fit, that would be great.
Thanks!
-R
/*
Windows Registry Subkey Enumeration Example
Based on example found at code-blue.org
*/
#include <windows.h>
#include <stdio.h>
void EnumerateValues(HKEY hKey, DWORD numValues)
{
DWORD dwIndex = 0;
LPSTR valueName = new CHAR[64];
DWORD valNameLen;
DWORD dataType;
DWORD data;
DWORD dataSize;
for (int i = 0; i < numValues; i++)
{
RegEnumValue(hKey,
dwIndex,
valueName,
&valNameLen,
NULL,
&dataType,
(BYTE*)&data,
&dataSize);
dwIndex++;
printf("Code: 0x%08X\n", data);
}
}
void EnumerateSubKeys(HKEY RootKey, char* subKey, unsigned int tabs = 0)
{
HKEY hKey;
DWORD cSubKeys; //Used to store the number of Subkeys
DWORD maxSubkeyLen; //Longest Subkey name length
DWORD cValues; //Used to store the number of Subkeys
DWORD maxValueLen; //Longest Subkey name length
DWORD retCode; //Return values of calls
RegOpenKeyEx(RootKey, subKey, 0, KEY_ALL_ACCESS, &hKey);
RegQueryInfoKey(hKey, // key handle
NULL, // buffer for class name
NULL, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&maxSubkeyLen, // longest subkey length
NULL, // longest class string
&cValues, // number of values for this key
&maxValueLen, // longest value name
NULL, // longest value data
NULL, // security descriptor
NULL); // last write time
if(cSubKeys>0)
{
char currentSubkey[MAX_PATH];
for(int i=0;i < cSubKeys;i++){
DWORD currentSubLen=MAX_PATH;
retCode=RegEnumKeyEx(hKey, // Handle to an open/predefined key
i, // Index of the subkey to retrieve.
currentSubkey, // buffer to receives the name of the subkey
&currentSubLen, // size of that buffer
NULL, // Reserved
NULL, // buffer for class string
NULL, // size of that buffer
NULL); // last write time
if(retCode==ERROR_SUCCESS)
{
for (int i = 0; i < tabs; i++)
printf("\t");
printf("(%d) %s\n", i+1, currentSubkey);
char* subKeyPath = new char[currentSubLen + strlen(subKey)];
sprintf(subKeyPath, "%s\\%s", subKey, currentSubkey);
EnumerateSubKeys(RootKey, subKeyPath, (tabs + 1));
}
}
}
else
{
EnumerateValues(hKey, cValues);
}
RegCloseKey(hKey);
}
int main()
{
EnumerateSubKeys(HKEY_CURRENT_USER,"SOFTWARE\\MyKeyToSearchIn");
return 0;
}
Enumerating the keys this way is overkill. This would simply waste the system resources, memory, call-stack and put pressure on registry sub-system. Do not do unless needed.
Are you going to have "search registry" in your application? If yes, enumerate only when user demands so. Or, if you are developing "Registry Viewer/Editor", do expand and open sub-keys only when needed.
If you absolutely need to retrieve and store all keys/values, you can use multiple threads to enumerate the keys. The number of threads would initially be the HKEY-major-keys, and then you can have more threads, depending on number of sub keys and runtime heuristics you perform while enumerating the keys.
Recursion may or may not be good approach for "recursive-enumeration" of sub-keys - you must keep number of arguments to recursive implementation minimum - put the arguments into one struct or put them in class. You may also like to use std::stack for the same.
It appears that you are calling RegEnumValue() without setting the lpcchValueName parameter to a proper value. This parameter is an [in] parameter as well as an [out] parameter. Try this:
for (int i = 0; i < numValues; i++)
 {
DWORD valNameLen = 64; //added this line to match valueName buffer size
  RegEnumValue(hKey,
     dwIndex,
     valueName,
     &valNameLen,
     NULL,
     &dataType,
     (BYTE*)&data,
     &dataSize);
Documentation for RegEnumValue() : http://msdn.microsoft.com/en-us/library/ms724865(v=vs.85).aspx
I tried your code on a key with a very small number of subkeys.
In the function EnumerateValues after calling RegEnumValue once, the value of numValues is getting filled with some random junk.
The solution is to change the parameters of RegEnumValue to
RegEnumValueA(hKey,
dwIndex,
valueName,
&valNameLen,
nullptr,
nullptr,
nullptr,
nullptr);
This is the function finally,
void EnumerateValues(HKEY hKey, DWORD numValues)
{
DWORD dwIndex = 0;
LPSTR valueName = new CHAR[64];
DWORD valNameLen;
DWORD numback = numValues;
for (int i = 0; i < numValues; i++)
{
// printf("%lu, %d\n", numValues, i);
RegEnumValueA(hKey,
dwIndex,
valueName,
&valNameLen,
nullptr,
nullptr,
nullptr,
nullptr);
dwIndex++;
if (i > numback)
{
RegCloseKey(hKey);
printf("Inf loop exiting...\n");
exit(-1);
}
// printf("Code: 0x%08X, %lu, %d\n", data, numValues, i);
// printf("Code: %lu, %d\n", numValues, i);
}
RegCloseKey(hKey);
}
I fixed your code and posted it on github.
Main problem was that you need to make your own buffer and point data to it, and it is also required that dataSize is set before calling RegEnumValue. Among other things, the program won't run without user elevation(admin) if you call RegOpenKeyEx with all permissions, so I changed them to read only. I bet this detail gave many people a headache.

how to get vendor id and product id of a plugged usb device on windows

I am using Qt on windows platform.
i want to get and display vendor id and product id of a plugged usb device from my local system.
Below is my full source code to get the vendor id and product id from the usb device.
when i run the my qt application it does not throw me any errors .
so i plug the usb device into the system.
but my print statement displays the result as below
qDebug ()<<pDetData->DevicePath;
i get the result as 0x4
Whether i have any implementation mistakes in my source code ?
if so please guide me what i am doing wrong..
Have i missed out any other functions ?
Is it possible to get the vendor id and product id from the usb device based on my source code .( my implementation of the code ) ?
kindly find my source code below
static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10L, 0x6530, 0x11D2,
{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
HANDLE hInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE,NULL,NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if ( hInfo == INVALID_HANDLE_VALUE )
{
qDebug ()<<"invalid";
}
else
{
qDebug ()<<"valid handle";
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
SP_INTERFACE_DEVICE_DATA Interface_Info;
Interface_Info.cbSize = sizeof(Interface_Info);
BYTE Buf[1024];
DWORD i;
DWORD InterfaceNumber= 0;
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd =
(PSP_DEVICE_INTERFACE_DETAIL_DATA) Buf;
for (i=0;SetupDiEnumDeviceInfo(hInfo,i,&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( hInfo,
&DeviceInfoData,
SPDRP_DEVICEDESC,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR,buffersize);
}
else
{
// Insert error handling here.
break;
}
qDebug ()<<(TEXT("Device Number %i is: %s\n"),i, buffer);
if (buffer) LocalFree(buffer);
if ( GetLastError() != NO_ERROR
&& GetLastError() != ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
qDebug ()<<"return false";
}
InterfaceNumber = 0; // this just returns the first one, you can iterate on this
if (SetupDiEnumDeviceInterfaces(hInfo,
NULL,
&GUID_DEVINTERFACE_USB_DEVICE,
InterfaceNumber,
&Interface_Info))
{
printf("Got interface");
DWORD needed;
pspdidd->cbSize = sizeof(*pspdidd);
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)
+ 256;
SetupDiGetDeviceInterfaceDetail(hInfo,
&Interface_Info, pDetData,dwDetDataSize, NULL,
&DeviceInfoData);
qDebug ()<<pDetData->DevicePath;
//qDebug ()<<QString::fromWCharArray(pDetData->DevicePath);
}
else
{
printf("\nNo interface");
//ErrorExit((LPTSTR) "SetupDiEnumDeviceInterfaces");
if ( GetLastError() == ERROR_NO_MORE_ITEMS)
printf(", since there are no more items found.");
else
printf(", unknown reason.");
}
// Cleanup
SetupDiDestroyDeviceInfoList(hInfo);
qDebug ()<<"return true";
}
}
}
--------------- Edited to add: -----------------
Hi... the application comes and prints this
\?\usb#vid_04f2&pid_0111#5&1ba5a77f&0&2#{a5dcbf1 0-6530-11d2-901f-00c04fb951ed}
again it goes to while loop .... here it gets breaked in the else statement...
Qt Code:
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Change the buffer size.
if (buffer) LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR,buffersize);
} else {
qDebug ()<<"Here it quits the application";
// Insert error handling here. break;
}
Any ideas in this....
After this line:
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
Add this:
DWORD dwDetDataSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
pDetData = (_SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc (dwDetDataSize);
pDetData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
After this line:
qDebug ()<<pDetData->DevicePath;
Add this:
free(pDetData);
But eventually you're going to have to read the docs for SetupDiGetDeviceInterfaceDetail(). Do it, there are lots of functions that work like this, with pointers to variable-size structs.
-------- Edited to add: --------
You're really going about this the wrong way. I see you're following the advice you got here, and it's taken you down the wrong path. idVendor and idProduct can only be found in the USB_DEVICE_DESCRIPTOR (MSDN).
It looks like you already know how to get the device handle (using CreateFile()). After that, you call WinUsb_Initialize() (MSDN). That gets you a WINUSB_INTERFACE_HANDLE.
Once you have that handle, you want to call WinUsb_GetDescriptor() (MSDN), with the DescriptorType set to URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. I can't test code now, but it will look something like this:
USB_DEVICE_DESCRIPTOR udd;
memset(&udd, 0, sizeof(udd));
ULONG LengthTransferred = 0;
WinUsb_GetDescriptor(
winusb_interface_handle, // returned by WinUsbInitialize
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
0, // not sure if we need this
0x409, // not sure if we need this
&udd,
sizeof(udd),
&LengthTransferred);
After that, udd->idVendor and udd->idProduct should have what you want.
Microsoft used to supply sample code for all this in the DDK, and probably still does, but I don't have access to one.
---------- Edited to add: ----------
Daniel K writes that the code should really be:
USB_DEVICE_DESCRIPTOR udd;
memset(&udd, 0, sizeof(udd));
ULONG LengthTransferred = 0;
WinUsb_GetDescriptor(
winusb_interface_handle, // returned by WinUsbInitialize
USB_DEVICE_DESCRIPTOR_TYPE, // Daniel K's suggestion
0,
0x409, // asks for English
&udd,
sizeof(udd),
&LengthTransferred);
See the comments for further details.
An alternative is to obtain the hardwareID which includes the VID and PID.
Call SetupDiGetDeviceRegistryProperty with SPDRP_HARDWAREID like so:
wchar_t *hardwareID;
// First get requiredLength
SetupDiGetDeviceRegistryProperty(deviceInfoList, &deviceInfoData, SPDRP_HARDWAREID, NULL, NULL, 0, &requiredLength);
hardwareID = (wchar_t*)(new char[requiredLength]());
// Second call to populate hardwareID
SetupDiGetDeviceRegistryProperty(deviceInfoList, &deviceInfoData, SPDRP_HARDWAREID, NULL, (PBYTE)hardwareID, requiredLength, NULL);
// Display the string
qDebug() << "hardwareID =" << QString::fromWCharArray(hardwareID);
This will give you a string like USB\ROOT_HUB20&VID1002&PID4396&REV0000 which you can parse.
*Note: not all devices will have a VID and PID, such as non-USB devices.
You are enumerating the device "interface". Interfaces do not have a VID or PID - device instances do. I am not sure whether you are enumerating the interfaces to narrow down the devices you are interested in, for because it's an error.
If you just enumerate the device instances, then you can call SetupDiGetDeviceProperty with DEVPKEY_Device_HardwareIds and then grep the resulting hardware id for the VID and PID.
If you are using the device interfaces on purpose, then you need to call SetupDiGetDeviceInterfaceDetail once with a NULL PSP_DEVICE_INTERFACE_DETAIL parameter and a valid requiredSize pointer to get the required size of memory to allocate, allocate that memory and then call the function again. In that call, the last parameter is a SP_DEVINFO_DATA structure, which once retrieved, you can use in the call to SetupDiGetDeviceProperty as I mentioned above.