In my qt application I want to save some application output data to an file in my usb pen drive. I need to put following features in my qt application
Detect the usb drive insertion
I have only one usb slot.
After i insert it I want to know its drive number and letter and transfer a file at specific location in my PC to that usb drive.
Can anybody tell me which winapi .lib , .h and .dll file i hav to use to get all the above functionalities ?
If someone can provide some code snippets, it will very much helpful for me.
Handle WM_DEVICECHANGE - See http://lists.trolltech.com/qt-interest/2001-08/thread00698-0.html for how to handle windows messages in QT.
If wParam is DBT_DEVICEARRIVAL then cast lParam to a DEV_BROADCAST_HDR *
If the structures dbch_devicetype is DBT_DEVTYP_VOLUME cast lParam again, this time to a DEV_BROADCAST_VOLUME *
Now check the dbcv_unitmask bit field, iterate over bits 0..31 and check if the corresponding drive match your USB drive.
if (wParam == DBT_DEVICEARRIVAL) {
if (((DEV_BROADCAST_HDR *) lParam)->dbch_devicetype == DBT_DEVTYP_VOLUME) {
DWORD Mask = ((DEV_BROADCAST_VOLUME *) lParam)->dbcv_unitmask;
for (int i = 0; i < 32; ++i) {
if (Mask & (1 << i)) {
char RootPath[4] = "A:\\";
RootPath[0] += i;
// Check if the root path in RootPath is your USB drive.
}
}
}
}
The earlier answer is now out-of-date. The following worked for me with QT5 on Windows 10, where MainWindow is derived from QMainWindow:
#include <QByteArray>
#include <windows.h>
#include <dbt.h>
bool MainWindow::nativeEvent(const QByteArray& eventType, void* pMessage, long* pResult)
{
auto pWindowsMessage = static_cast<MSG*>(pMessage);
auto wParam = pWindowsMessage->wParam;
if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
auto lParam = pWindowsMessage->lParam;
auto deviceType = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam)->dbch_devicetype;
if (deviceType == DBT_DEVTYP_VOLUME) {
auto unitmask = reinterpret_cast<DEV_BROADCAST_VOLUME*>(lParam)->dbcv_unitmask;
for (int i = 0; i < 32; ++i) {
if ((unitmask & (1 << i)) != 0) {
setDriveChanged('A' + i, wParam == DBT_DEVICEARRIVAL);
}
}
}
}
return false;
}
Related
I have an application that is used as a control system for a presentation, under Linux and using X11. I have a USB presentation remote that acts as a very miniature keyboard (four buttons: Page Up, Page Down, and two others) which can be used to advance and go back in the presentation. I would like to have my presentation application to receive all of the events from this remote regardless of where the mouse focus is. But I would also like to be able to receive the normal mouse and keyboard events if the current window focus is on the presentation application. Using XIGrabDevice() I was able to receive all events from the remote in the presentation application regardless of the current focus but I was not able to receive any events from the mouse or keyboard while the grab was active.
I ended up setting up a separate program to capture the remote's keys, then I relay those keys to my main program. I did it this way because the original program was using the older XInput extension, and I needed to use the newer XInput2 extension, and they do not exist well together. Here's some C++ code (it doesn't do any error checking, but this should be done in a real program):
// Open connection to X Server
Display *dpy = XOpenDisplay(NULL);
// Get opcode for XInput Extension; we'll need it for processing events
int xi_opcode = -1, event, error;
XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error);
// Allow user to select a device
int num_devices;
XIDeviceInfo *info = XIQueryDevice(dpy, XIAllDevices, &num_devices);
for (int i = 0; i < num_devices; ++i)
{
XIDeviceInfo *dev = &info[i];
std::cout << dev->deviceid << " " << dev->name << "\n";
}
XIFreeDeviceInfo(info);
std::cout << "Enter the device number: ";
std::string input;
std::cin >> input;
int deviceid = -1;
std::istringstream istr(input);
istr >> deviceid;
// Create an InputOnly window that is just used to grab events from this device
XSetWindowAttributes attrs;
long attrmask = 0;
memset(&attrs, 0, sizeof(attrs));
attrs.override_redirect = True; // Required to grab device
attrmask |= CWOverrideRedirect;
Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, attrmask, &attrs);
// Make window without decorations
PropMotifWmHints hints;
hints.flags = 2;
hints.decorations = 0;
Atom property = XInternAtom(dpy, "_MOTIF_WM_HINTS", True);
XChangeProperty(dpy, win, property, property, 32, PropModeReplace, (unsigned char *)&hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
// We are interested in key presses and hierarchy changes. We also need to get key releases or else we get an infinite stream of key presses.
XIEventMask evmasks[1];
unsigned char mask0[XIMaskLen(XI_LASTEVENT)];
memset(mask0, 0, sizeof(mask0));
XISetMask(mask0, XI_KeyPress);
XISetMask(mask0, XI_KeyRelease);
XISetMask(mask0, XI_HierarchyChanged);
evmasks[0].deviceid = XIAllDevices;
evmasks[0].mask_len = sizeof(mask0);
evmasks[0].mask = mask0;
XISelectEvents(dpy, win, evmasks, 1);
XMapWindow(dpy, win);
XFlush(dpy);
XEvent ev;
bool grab_success = false, grab_changed;
while (1)
{
grab_changed = false;
if (!grab_success)
{
XIEventMask masks[1];
unsigned char mask0[XIMaskLen(XI_LASTEVENT)];
memset(mask0, 0, sizeof(mask0));
XISetMask(mask0, XI_KeyPress);
masks[0].deviceid = deviceid;
masks[0].mask_len = sizeof(mask0);
masks[0].mask = mask0;
XIGrabDevice(dpy, deviceid, win, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, XIOwnerEvents, masks);
}
XNextEvent(dpy, &ev);
XGenericEventCookie *cookie = &ev.xcookie;
if (cookie->type == GenericEvent && cookie->extension == xi_opcode && XGetEventData(dpy, cookie))
{
if (cookie->evtype == XI_KeyPress)
{
XIDeviceEvent *de = (XIDeviceEvent*)cookie->data;
std::cout << "found XI_KeyPress event: keycode " << de->detail << "\n";
}
else if (cookie->evtype == XI_HierarchyChanged)
{
// Perhaps a device was unplugged. The client is expected to re-read the list of devices to find out what changed.
std::cout << "found XI_HierarchyChanged event.\n";
grab_changed = true;
}
XFreeEventData(dpy, cookie);
}
if (grab_changed)
{
XIUngrabDevice(dpy, deviceid, CurrentTime);
grab_success = false;
break;
}
}
I found the following links helpful:
Peter Hutterer's 6-part blog on XInput2: 1 2 3 4 5 6
This blog entry was useful to determine which class to cast the cookie->data pointer to, depending on the cookie->evtype: 7
To test a corner case in our debugger, I need to come up with a program which has a DLL loaded above 2GB (0x80000000). Current test case is a multi-GB game which loads >700 DLLs, and I'd like to have something simpler and smaller. Is there a way to achieve it reliably without too much fiddling? I assume I need to use /LARGEADDRESSAWARE and somehow consume enough of the VA space to bump the new DLLs above 2GB but I'm fuzzy on the details...
Okay, it took me a few tries but I managed to come up with something working.
// cl /MT /Ox test.cpp /link /LARGEADDRESSAWARE
// occupy the 2 gigabytes!
#define ALLOCSIZE (64*1024)
#define TWOGB (2*1024ull*1024*1024)
#include <windows.h>
#include <stdio.h>
int main()
{
int nallocs = TWOGB/ALLOCSIZE;
for ( int i = 0; i < nallocs+200; i++ )
{
void * p = VirtualAlloc(NULL, ALLOCSIZE, MEM_RESERVE, PAGE_NOACCESS);
if ( i%100 == 0)
{
if ( p != NULL )
printf("%d: %p\n", i, p);
else
{
printf("%d: failed!\n", i);
break;
}
}
}
printf("finished VirtualAlloc. Loading a DLL.\n");
//getchar();
HMODULE hDll = LoadLibrary("winhttp");
printf("DLL base: %p.\n", hDll);
//getchar();
FreeLibrary(hDll);
}
On Win10 x64 produces:
0: 00D80000
100: 03950000
200: 03F90000
[...]
31800: 7FBC0000
31900: 00220000
32000: 00860000
32100: 80140000
32200: 80780000
32300: 80DC0000
32400: 81400000
32500: 81A40000
32600: 82080000
32700: 826C0000
32800: 82D00000
32900: 83340000
finished VirtualAlloc. Loading a DLL.
DLL base: 83780000.
for your own DLL you need set 3 linker option:
/LARGEADDRESSAWARE
/DYNAMICBASE:NO
/BASE:"0x********"
note that link.exe allow only image full located bellow 3GB (0xC0000000) for 32-bit image. in other word, he want that ImageBase + ImageSize <= 0xC0000000
so say /BASE:0xB0000000 will be ok, /BASE:0xBFFF0000 only if your image size <= 0x10000 and for /BASE:0xC0000000 and higher we always got error LNK1249 - image exceeds maximum extent with base address address and size size
also EXE mandatory must have /LARGEADDRESSAWARE too, because are all 4GB space available for wow64 process based only on EXE options.
if we want do this for external DLL - here question more hard. first of all - are this DLL can correct handle this situation (load base > 0x80000000) ? ok. let test this. any api (including most low level LdrLoadDll) not let specify base address, for DLL load. here only hook solution exist.
when library loaded, internal always called ZwMapViewOfSection and it 3-rd parameter BaseAddress - Pointer to a variable that receives the base address of the view. if we set this variable to 0 - system yourself select loaded address. if we set this to specific address - system map view (DLL image in our case) only at this address, or return error STATUS_CONFLICTING_ADDRESSES.
working solution - hook call to ZwMapViewOfSection and replace value of variable, to which point BaseAddress. for find address > 0x80000000 we can use VirtualAlloc with MEM_TOP_DOWN option. note - despite ZwMapViewOfSection also allow use MEM_TOP_DOWN in AllocationType parameter, here it will be have not needed effect - section anyway will be loaded by preferred address or top-down from 0x7FFFFFFF not from 0xFFFFFFFF. but with VirtualAlloc the MEM_TOP_DOWN begin search from 0xFFFFFFFF if process used 4Gb user space. for know - how many memory need for section - we can call ZwQuerySection with SectionBasicInformation - despite this is undocumented - for debug and test - this is ok.
for hook of course can be used some detour lib, but possible do hook with DRx breakpoint - set some Drx register to NtMapViewOfSection address. and set AddVectoredExceptionHandler - which handle exception. this will be perfect work, if process not under debugger. but under debugger it break - most debuggers alwas stop under single step exception and usually no option not handle it but pass to application. of course we can start program not under debugger, and attach it later - after dll load. or possible do this task in separate thread and hide this thread from debugger. disadvantage here - that debugger not got notify about dll load in this case and not load symbols for this. however for external (system dll) for which you have not src code - this in most case can be not a big problem. so solution exit, if we can implement it ). possible code:
PVOID pvNtMapViewOfSection;
LONG NTAPI OnVex(::PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP &&
ExceptionInfo->ExceptionRecord->ExceptionAddress == pvNtMapViewOfSection)
{
struct MapViewOfSection_stack
{
PVOID ReturnAddress;
HANDLE SectionHandle;
HANDLE ProcessHandle;
PVOID *BaseAddress;
ULONG_PTR ZeroBits;
SIZE_T CommitSize;
PLARGE_INTEGER SectionOffset;
PSIZE_T ViewSize;
SECTION_INHERIT InheritDisposition;
ULONG AllocationType;
ULONG Win32Protect;
} * stack = (MapViewOfSection_stack*)(ULONG_PTR)ExceptionInfo->ContextRecord->Esp;
if (stack->ProcessHandle == NtCurrentProcess())
{
SECTION_BASIC_INFORMATION sbi;
if (0 <= ZwQuerySection(stack->SectionHandle, SectionBasicInformation, &sbi, sizeof(sbi), 0))
{
if (PVOID pv = VirtualAlloc(0, (SIZE_T)sbi.Size.QuadPart, MEM_RESERVE|MEM_TOP_DOWN, PAGE_NOACCESS))
{
if (VirtualFree(pv, 0, MEM_RELEASE))
{
*stack->BaseAddress = pv;
}
}
}
}
// RESUME_FLAG ( 0x10000) not supported by xp, but anyway not exist 64bit xp
ExceptionInfo->ContextRecord->EFlags |= RESUME_FLAG;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
struct LOAD_DATA {
PCWSTR lpLibFileName;
HMODULE hmod;
ULONG dwError;
};
ULONG WINAPI HideFromDebuggerThread(LOAD_DATA* pld)
{
NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, 0, 0);
ULONG dwError = 0;
HMODULE hmod = 0;
if (PVOID pv = AddVectoredExceptionHandler(TRUE, OnVex))
{
::CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
ctx.Dr7 = 0x404;
ctx.Dr1 = (ULONG_PTR)pvNtMapViewOfSection;
if (SetThreadContext(GetCurrentThread(), &ctx))
{
if (hmod = LoadLibraryW(pld->lpLibFileName))
{
pld->hmod = hmod;
}
else
{
dwError = GetLastError();
}
ctx.Dr7 = 0x400;
ctx.Dr1 = 0;
SetThreadContext(GetCurrentThread(), &ctx);
}
else
{
dwError = GetLastError();
}
RemoveVectoredExceptionHandler(pv);
}
else
{
dwError = GetLastError();
}
pld->dwError = dwError;
return dwError;
}
HMODULE LoadLibHigh(PCWSTR lpLibFileName)
{
BOOL bWow;
HMODULE hmod = 0;
if (IsWow64Process(GetCurrentProcess(), &bWow) && bWow)
{
if (pvNtMapViewOfSection = GetProcAddress(GetModuleHandle(L"ntdll"), "NtMapViewOfSection"))
{
LOAD_DATA ld = { lpLibFileName };
if (IsDebuggerPresent())
{
if (HANDLE hThread = CreateThread(0, 0, (PTHREAD_START_ROUTINE)HideFromDebuggerThread, &ld, 0, 0))
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
}
else
{
HideFromDebuggerThread(&ld);
}
if (!(hmod = ld.hmod))
{
SetLastError(ld.dwError);
}
}
}
else
{
hmod = LoadLibrary(lpLibFileName);
}
return hmod;
}
I am writing C code, using WinAPI, that installs a global hook (WH_GETMESSAGE) which injects a DLL into all compatible processes in the system. The injected DLL monitors a certain messages passed to the message queue and creates a text file. The callback function in the DLL should create a text file using a randomly generated name in a specific directory. The methods are working when tested on main but fail when in the DLL (i.e no text file is created). NB: The following are working fine.
The DLL is successfully injected into all (32bit) processes, checked using
ProcessExplorer.
The Callback function is being called, checked using
MessageBeep(-1)
Is there something I am missing in the code? and are there any ways to debug my DLL code injected in a processes e.g in Firefox.
Here's my code in the DLL:
LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam){
if (code < 0) {
return CallNextHookEx(NULL, code, wParam, lParam);
}
if ((code == HC_ACTION) && (wParam == PM_REMOVE)){
//for any message create and write to text file
writeToLogFile();
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
Here's the code that creates the text file:
BOOL writeToLogFile() {
char fileName[10];
char fName[] = "C:\\Users\\MyDir\\";
char buffer[75]; //place concatenated string here
generateRandomStr(fileName); //methos to generate file name
snprintf(buffer, sizeof(buffer), "%s%s.txt", fName, fileName);
HANDLE fHandle =
CreateFile(buffer,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
//file handle code check ommited for brevity
DWORD bytesWritten;
WriteFile(fHandle, buffer, strlen(buffer), &bytesWritten, NULL);
CloseHandle(fHandle);
return TRUE;
}
/*Generate file name string (a-z characters)*/
VOID generateRandomStr(char holder[]) {
size_t i = 0;
srand(time(NULL)); //system clock seconds for seed
while (i < 9) {
int x = (97 + rand() % 97);
if (x > 96 && x < 122) {
holder[i] = ((char)x);
//puts((char)x);
i++;
}
}
holder[i++] = '\0'; //terminate the string
Check out the following http://www.codeproject.com/Articles/116253/Hot-Patching-Made-Easy. It talks about DLL injection.
Thanks everyone for the comments and tips. Finally managed to make it work, the issue was with the function CreateFile which was failing (some illegal characters in the file name generated). After using the OutputDebugString, I was able to view the output and debugged the generateRandomStr function responsible for generating the filename and it worked.
I'm working with libusb 0.1 and Qt in a Mac OS X v10.10 (Yosemite) environment. My goal is to get the serial numbers of all connected USB keys (usbclass = 8).
Usually the first reading occur properly, and I cannot understand why, but sometimes from subsequent readings, the device is no longer detected. Sometimes I get -60 error code on the usb_get_string_simple(handle, device->descriptor.iSerialNumber) function. How can I fix this problem?
I've tried USB keys with FAT, FAT32 and NTFS file systems, but they all had the same issue.
In the same project, I use libusb to read/write with printer devices connected to the Mac, and read serialnumbers too. No issue with this type of devices. So strange.
In the .pro file:
INCLUDEPATH += /Users/sborfedor/Desktop/root/current/includes
DEPENDPATH += /Users/sborfedor/Desktop/root/current/includes
LIBS += -L/Users/sborfedor/Desktop/root/current/bin_osx/libs/ -lusb-legacy
DESTDIR = /Users/sborfedor/Desktop/root/current/bin_osx
This is my code:
void MainWindow::run() {
struct usb_bus* bus = NULL;
struct usb_device* device = NULL;
const int MASSSTORAGE_CLASS = 8;
usb_init();
usb_set_debug(3);
usb_find_busses();
usb_find_devices();
int ret;
for ( bus = usb_get_busses(); bus; bus = bus->next ) {
for ( device = bus->devices; device; device = device->next ) {
if (device->descriptor.bDeviceClass != MASSSTORAGE_CLASS && device->descriptor.bDeviceClass != 0) {
continue;
}
for ( int cid = 0; cid < device->descriptor.bNumConfigurations; ++cid ) {
struct usb_config_descriptor* config = &( device->config[cid] );
if (config == NULL)
continue;
for ( int iid = 0; iid < config->bNumInterfaces; ++iid ) {
struct usb_interface* interface = &( config->interface[iid] );
if (interface == NULL)
continue;
usb_dev_handle* handle = usb_open( device );
if (handle == NULL)
continue;
for ( int sid = 0; sid < interface->num_altsetting; sid++ ) {
struct usb_interface_descriptor* settings = &( interface->altsetting[sid] );
if (settings == NULL)
continue;
int usbClass = device->descriptor.bDeviceClass;
if (usbClass == 0) {
usbClass = settings->bInterfaceClass;
}
if (usbClass != MASSSTORAGE_CLASS)
continue;
char device_serial_number[255];
ret = usb_get_string_simple( handle, device->descriptor.iSerialNumber, device_serial_number, sizeof(device_serial_number) );
if (ret > 0 ) {
qDebug() << "SERIAL_NUMBER="<<QString(device_serial_number);
} else {
qDebug() << "*** usb_get_string_simple( handle, device->descriptor.iSerialNumber, device_serial_number, USBX_DEVICE_SERIAL_NUMBER_MAX_SIZE ) RET="<<ret;
}
}
usb_close( handle );
}
}
}
}
}
I'm not especially familiar with libusb, but OSX already reads out serial numbers and stores them in the relevant property on IOKit device nubs. So there is no need to send requests to hardware, which might be confusing the active driver? (Libusb presumably creates a user client driver instance on the device)
The Disk Arbitration Framework is a good way of enumerating disks in the system and querying their properties - this is a bit easier than using IOKit directly.
I am developing a multi-platform game that runs on iOS as well as desktops (Windows, Mac, Linux). I want the game to be able to resize certain UI elements depending on the resolution of the screen in inches. The idea is that if a button should be, say, around 1/2 inch across in any interface, it will be scaled automatically that size.
Now for iOS devices this problem is reasonably well solvable using brute force techniques. You can look up the type of the device and use a hard-coded table to determine the screen size in inches for each device. Not the most elegant solution, but sufficient.
Desktops are the tricky ones. What I wish and hope exists is a mechanism by which (some?) monitors report to operating systems their actual screen size in inches. If that mechanism exists and I can access it somehow, I can get good numbers at least for some monitors. But I've never come across any such concept in any of the major OS APIs.
Is there a way to ask for the screen size in inches in Win32? If so, are there monitors that actually provide this information?
(And if the answer is no: Gosh, doesn't this seem awfully useful?)
For Windows, first see SetProcessDPIAware() for a discussion on turning off automatic scaling, and then call GetDeviceCaps( LOGPIXELSX ) and GetDeviceCaps( LOGPIXELSY ) on your HDC to determine the monitor's DPI. Divide the screen resolution on your active monitor by those settings and you've got the size.
Also see this article for a similar discussion on DPI aware apps.
Here is a method I found at the web address "https://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/".
Authour says that measurement is in millimeters.
Does not give you the precise width and height but better approximation than HORSIZE and VERTSIZE. In which I tried on two different monitors and got a max difference of 38 cm (measured screen size - calculated screen size).
#include <SetupApi.h>
#pragma comment(lib, "setupapi.lib")
#define NAME_SIZE 128
const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};
// Assumes hDevRegKey is valid
bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& HeightMm)
{
DWORD dwType, AcutalValueNameLength = NAME_SIZE;
TCHAR valueName[NAME_SIZE];
BYTE EDIDdata[1024];
DWORD edidsize=sizeof(EDIDdata);
for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i)
{
retValue = RegEnumValueA ( hDevRegKey, i, &valueName[0],
&AcutalValueNameLength, NULL, &dwType,
EDIDdata, // buffer
&edidsize); // buffer size
if (retValue != ERROR_SUCCESS || 0 != strcmp(valueName,"EDID"))
continue;
WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
return true; // valid EDID found
}
return false; // EDID not found
}
// strange! Authour requires TargetDevID argument but does not use it
bool GetSizeForDevID(const char *TargetDevID, short& WidthMm, short& HeightMm)
{
HDEVINFO devInfo = SetupDiGetClassDevsExA(
&GUID_CLASS_MONITOR, //class GUID
NULL, //enumerator
NULL, //HWND
DIGCF_PRESENT, // Flags //DIGCF_ALLCLASSES|
NULL, // device info, create a new one.
NULL, // machine name, local machine
NULL);// reserved
if (NULL == devInfo) return false;
bool bRes = false;
for (ULONG i=0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
{
SP_DEVINFO_DATA devInfoData;
memset(&devInfoData,0,sizeof(devInfoData));
devInfoData.cbSize = sizeof(devInfoData);
if (SetupDiEnumDeviceInfo(devInfo,i,&devInfoData))
{
HKEY hDevRegKey = SetupDiOpenDevRegKey(devInfo,&devInfoData,DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if(!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE)) continue;
bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
RegCloseKey(hDevRegKey);
}
}
SetupDiDestroyDeviceInfoList(devInfo);
return bRes;
}
int main(int argc, CHAR* argv[])
{
short WidthMm, HeightMm;
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
DWORD dev = 0; // device index
int id = 1; // monitor number, as used by Display Properties > Settings
char DeviceID[1024];
bool bFoundDevice = false;
while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice)
{
DISPLAY_DEVICE ddMon = {sizeof(ddMon)};
DWORD devMon = 0;
while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice)
{
if (ddMon.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP &&
!(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
{
sprintf(DeviceID,"%s", ddMon.DeviceID+8);
for(auto it=DeviceID; *it; ++it)
if(*it == '\\') { *it = 0; break; }
bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm);
}
devMon++;
ZeroMemory(&ddMon, sizeof(ddMon));
ddMon.cb = sizeof(ddMon);
}
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
dev++;
}
return 0;
}