1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Display Control Panel
4 * FILE: dll/cpl/desk/devsett.c
5 * PURPOSE: ReactOS Display Control Panel Shell Extension Support
6 */
7
8 #include "desk.h"
9
10 #include <cfgmgr32.h>
11
12 #define NDEBUG
13 #include <debug.h>
14
15 #define DEBUG_DEVSETTINGS
16
17 typedef struct _CDevSettings
18 {
19 const struct IDataObjectVtbl *lpIDataObjectVtbl;
20 DWORD ref;
21
22 CLIPFORMAT cfExtInterface; /* "Desk.cpl extension interface" */
23 CLIPFORMAT cfDisplayDevice; /* "Display Device" */
24 CLIPFORMAT cfDisplayName; /* "Display Name" */
25 CLIPFORMAT cfDisplayId; /* "Display ID" */
26 CLIPFORMAT cfMonitorName; /* "Monitor Name" */
27 CLIPFORMAT cfMonitorDevice; /* "Monitor Device" */
28 CLIPFORMAT cfDisplayKey; /* "Display Key" */
29 CLIPFORMAT cfDisplayStateFlags; /* "Display State Flags" */
30 CLIPFORMAT cfPruningMode; /* "Pruning Mode" */
31
32 PWSTR pDisplayDevice;
33 PWSTR pDisplayName;
34 PWSTR pDisplayKey;
35 PWSTR pDisplayId;
36 PWSTR pMonitorName;
37 PWSTR pMonitorDevice;
38
39 DESK_EXT_INTERFACE ExtInterface;
40
41 PDEVMODEW lpOrigDevMode;
42 PDEVMODEW lpCurDevMode;
43 PDEVMODEW * DevModes;
44 DWORD nDevModes;
45 DWORD StateFlags;
46
47 union
48 {
49 DWORD Flags;
50 struct
51 {
52 DWORD bModesPruned : 1;
53 DWORD bKeyIsReadOnly : 1;
54 DWORD bPruningOn : 1;
55 };
56 };
57 } CDevSettings, *PCDevSettings;
58
59 #define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
60
61 static __inline PCDevSettings
impl_from_IDataObject(struct IDataObject * iface)62 impl_from_IDataObject(struct IDataObject *iface)
63 {
64 return (PCDevSettings)((ULONG_PTR)iface - FIELD_OFFSET(CDevSettings,
65 lpIDataObjectVtbl));
66 }
67
68 static __inline VOID
pCDevSettings_FreeString(PWCHAR * psz)69 pCDevSettings_FreeString(PWCHAR *psz)
70 {
71 if (*psz != NULL)
72 {
73 LocalFree((HLOCAL)*psz);
74 *psz = NULL;
75 }
76 }
77
78 static PWSTR
pCDevSettings_AllocAndCopyString(const TCHAR * pszSrc)79 pCDevSettings_AllocAndCopyString(const TCHAR *pszSrc)
80 {
81 SIZE_T c;
82 PWSTR str;
83
84 c = _tcslen(pszSrc) + 1;
85 str = (PWSTR)LocalAlloc(LMEM_FIXED,
86 c * sizeof(WCHAR));
87 if (str != NULL)
88 {
89 #ifdef UNICODE
90 StringCbCopyW(str, c * sizeof(WCHAR),
91 pszSrc);
92 #else
93 MultiByteToWideChar(CP_ACP,
94 0,
95 pszSrc,
96 -1,
97 str,
98 c);
99 #endif
100 }
101
102 return str;
103 }
104
105 static PWSTR
pCDevSettings_GetMonitorName(const WCHAR * pszDisplayDevice)106 pCDevSettings_GetMonitorName(const WCHAR *pszDisplayDevice)
107 {
108 DISPLAY_DEVICEW dd, dd2;
109 PWSTR str = NULL;
110
111 dd.cb = sizeof(dd);
112 if (EnumDisplayDevicesW(pszDisplayDevice,
113 0,
114 &dd,
115 0))
116 {
117 dd2.cb = sizeof(dd2);
118 if (EnumDisplayDevicesW(pszDisplayDevice,
119 1,
120 &dd2,
121 0))
122 {
123 /* There's more than one monitor connected... */
124 LoadStringW(hApplet,
125 IDS_MULTIPLEMONITORS,
126 dd.DeviceString,
127 sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
128 }
129 }
130 else
131 {
132 /* We can't enumerate a monitor, make sure this fact is reported
133 to the user! */
134 LoadStringW(hApplet,
135 IDS_UNKNOWNMONITOR,
136 dd.DeviceString,
137 sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
138 }
139
140 str = LocalAlloc(LMEM_FIXED,
141 (wcslen(dd.DeviceString) + 1) * sizeof(WCHAR));
142 if (str != NULL)
143 {
144 wcscpy(str,
145 dd.DeviceString);
146 }
147
148 return str;
149 }
150
151 static PWSTR
pCDevSettings_GetMonitorDevice(const WCHAR * pszDisplayDevice)152 pCDevSettings_GetMonitorDevice(const WCHAR *pszDisplayDevice)
153 {
154 DISPLAY_DEVICEW dd;
155 PWSTR str = NULL;
156
157 dd.cb = sizeof(dd);
158 if (EnumDisplayDevicesW(pszDisplayDevice,
159 0,
160 &dd,
161 0))
162 {
163 str = LocalAlloc(LMEM_FIXED,
164 (wcslen(dd.DeviceName) + 1) * sizeof(WCHAR));
165 if (str != NULL)
166 {
167 wcscpy(str,
168 dd.DeviceName);
169 }
170 }
171
172 return str;
173 }
174
175 /**
176 * @brief
177 * Converts a Hardware ID (DeviceID from EnumDisplayDevices)
178 * to an unique Device Instance ID.
179 *
180 * @param[in] pszDevice
181 * A pointer to a null-terminated Unicode string
182 * containing a Hardware ID.
183 * e.g. "PCI\VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00"
184 *
185 * @return
186 * A pointer to a null-terminated Unicode string
187 * containing an unique Device Instance ID
188 * or NULL in case of error.
189 * e.g. "PCI\VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00\3&267A616A&0&10"
190 *
191 * @remarks
192 * The caller must free the returned string with LocalFree.
193 */
194 static PWSTR
pCDevSettings_GetDeviceInstanceId(const WCHAR * pszDevice)195 pCDevSettings_GetDeviceInstanceId(const WCHAR *pszDevice)
196 {
197 HDEVINFO DevInfo;
198 SP_DEVINFO_DATA InfoData;
199 ULONG BufLen;
200 LPWSTR lpDevInstId = NULL;
201
202 DPRINT("CDevSettings::GetDeviceInstanceId(%ws)!\n", pszDevice);
203
204 DevInfo = SetupDiGetClassDevsW(NULL, pszDevice, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
205 if (DevInfo == INVALID_HANDLE_VALUE)
206 {
207 DPRINT1("SetupDiGetClassDevsW(\"%ws\") failed: %d\n", pszDevice, GetLastError());
208 return NULL;
209 }
210
211 ZeroMemory(&InfoData, sizeof(InfoData));
212 InfoData.cbSize = sizeof(InfoData);
213
214 /* Try to enumerate the first matching device */
215 if (!SetupDiEnumDeviceInfo(DevInfo, 0, &InfoData))
216 {
217 DPRINT1("SetupDiEnumDeviceInfo failed: %d\n", GetLastError());
218 return NULL;
219 }
220
221 if (SetupDiGetDeviceInstanceId(DevInfo, &InfoData, NULL, 0, &BufLen) ||
222 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
223 {
224 DPRINT1("SetupDiGetDeviceInstanceId failed: %d\n", GetLastError());
225 return NULL;
226 }
227
228 lpDevInstId = LocalAlloc(LMEM_FIXED,
229 (BufLen + 1) * sizeof(WCHAR));
230
231 if (lpDevInstId == NULL)
232 {
233 DPRINT1("LocalAlloc failed\n");
234 return NULL;
235 }
236
237 if (!SetupDiGetDeviceInstanceId(DevInfo, &InfoData, lpDevInstId, BufLen, NULL))
238 {
239 DPRINT1("SetupDiGetDeviceInstanceId failed: %d\n", GetLastError());
240 LocalFree((HLOCAL)lpDevInstId);
241 lpDevInstId = NULL;
242 }
243 else
244 {
245 DPRINT("instance id: %ws\n", lpDevInstId);
246 }
247
248 return lpDevInstId;
249 }
250
251
252 static HKEY
pCDevSettings_OpenDeviceKey(PCDevSettings This,BOOL ReadOnly)253 pCDevSettings_OpenDeviceKey(PCDevSettings This,
254 BOOL ReadOnly)
255 {
256 static const WCHAR szRegPrefix[] = L"\\Registry\\Machine\\";
257 PWSTR lpRegKey;
258 REGSAM Access = KEY_READ;
259 HKEY hKey;
260
261 lpRegKey = This->pDisplayKey;
262 if (lpRegKey != NULL)
263 {
264 if (wcslen(lpRegKey) >= wcslen(szRegPrefix) &&
265 !_wcsnicmp(lpRegKey,
266 szRegPrefix,
267 wcslen(szRegPrefix)))
268 {
269 lpRegKey += wcslen(szRegPrefix);
270 }
271
272 if (!ReadOnly)
273 Access |= KEY_WRITE;
274
275 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
276 lpRegKey,
277 0,
278 Access,
279 &hKey) == ERROR_SUCCESS)
280 {
281 return hKey;
282 }
283 }
284
285 return NULL;
286 }
287
288 PDEVMODEW DESK_EXT_CALLBACK
CDevSettings_EnumAllModes(PVOID Context,DWORD Index)289 CDevSettings_EnumAllModes(PVOID Context,
290 DWORD Index)
291 {
292 PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
293 DWORD i, idx = 0;
294
295 DPRINT1("CDevSettings::EnumAllModes(%u)\n", Index);
296
297 if (!This->DevModes)
298 return NULL;
299
300 for (i = 0; i < This->nDevModes; i++)
301 {
302 /* FIXME: Add more sanity checks */
303 if (!This->DevModes[i])
304 continue;
305
306 if (idx == Index)
307 return This->DevModes[i];
308
309 idx++;
310 }
311
312 return NULL;
313 }
314
315 PDEVMODEW DESK_EXT_CALLBACK
CDevSettings_GetCurrentMode(PVOID Context)316 CDevSettings_GetCurrentMode(PVOID Context)
317 {
318 PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
319
320 DPRINT1("CDevSettings::GetCurrentMode\n");
321 return This->lpCurDevMode;
322 }
323
324 BOOL DESK_EXT_CALLBACK
CDevSettings_SetCurrentMode(PVOID Context,DEVMODEW * pDevMode)325 CDevSettings_SetCurrentMode(PVOID Context,
326 DEVMODEW *pDevMode)
327 {
328 PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
329 DWORD i;
330
331 DPRINT1("CDevSettings::SetCurrentMode(0x%p)\n", pDevMode);
332
333 if (!This->DevModes)
334 return FALSE;
335
336 for (i = 0; i < This->nDevModes; i++)
337 {
338 /* FIXME: Add more sanity checks */
339 if (!This->DevModes[i])
340 continue;
341
342 if (This->DevModes[i] == pDevMode)
343 {
344 This->lpCurDevMode = pDevMode;
345 return TRUE;
346 }
347 }
348
349 return FALSE;
350 }
351
352 VOID DESK_EXT_CALLBACK
CDevSettings_GetPruningMode(PVOID Context,PBOOL pbModesPruned,PBOOL pbKeyIsReadOnly,PBOOL pbPruningOn)353 CDevSettings_GetPruningMode(PVOID Context,
354 PBOOL pbModesPruned,
355 PBOOL pbKeyIsReadOnly,
356 PBOOL pbPruningOn)
357 {
358 PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
359
360 DPRINT1("CDevSettings::GetPruningMode(%p,%p,%p)\n", pbModesPruned, pbKeyIsReadOnly, pbPruningOn);
361
362 *pbModesPruned = This->bModesPruned;
363 *pbKeyIsReadOnly = This->bKeyIsReadOnly;
364 *pbPruningOn = This->bPruningOn;
365 }
366
367 VOID DESK_EXT_CALLBACK
CDevSettings_SetPruningMode(PVOID Context,BOOL PruningOn)368 CDevSettings_SetPruningMode(PVOID Context,
369 BOOL PruningOn)
370 {
371 HKEY hKey;
372 DWORD dwValue;
373 PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
374
375 DPRINT1("CDevSettings::SetPruningMode(%d)\n", PruningOn);
376
377 if (This->bModesPruned && !This->bKeyIsReadOnly &&
378 PruningOn != This->bPruningOn)
379 {
380 This->bPruningOn = (PruningOn != FALSE);
381
382 hKey = pCDevSettings_OpenDeviceKey(This,
383 FALSE);
384 if (hKey != NULL)
385 {
386 dwValue = (DWORD)This->bPruningOn;
387
388 RegSetValueEx(hKey,
389 TEXT("PruningMode"),
390 0,
391 REG_DWORD,
392 (const BYTE *)&dwValue,
393 sizeof(dwValue));
394
395 RegCloseKey(hKey);
396 }
397 }
398 }
399
400 static VOID
pCDevSettings_ReadHardwareInfo(HKEY hKey,LPCTSTR lpValueName,LPWSTR lpBuffer)401 pCDevSettings_ReadHardwareInfo(HKEY hKey,
402 LPCTSTR lpValueName,
403 LPWSTR lpBuffer)
404 {
405 DWORD type = REG_BINARY;
406 DWORD size = 128 * sizeof(WCHAR);
407 RegQueryValueEx(hKey,
408 lpValueName,
409 NULL,
410 &type,
411 (PBYTE)lpBuffer,
412 &size);
413 }
414
415 static VOID
pCDevSettings_InitializeExtInterface(PCDevSettings This)416 pCDevSettings_InitializeExtInterface(PCDevSettings This)
417 {
418 PDESK_EXT_INTERFACE Interface = &This->ExtInterface;
419 HKEY hKeyDev;
420
421 ZeroMemory(Interface,
422 sizeof(*Interface));
423 Interface->cbSize = sizeof(*Interface);
424
425 /* Initialize the callback table */
426 Interface->Context = impl_to_interface(This, IDataObject);
427 Interface->EnumAllModes = CDevSettings_EnumAllModes;
428 Interface->SetCurrentMode = CDevSettings_SetCurrentMode;
429 Interface->GetCurrentMode = CDevSettings_GetCurrentMode;
430 Interface->SetPruningMode = CDevSettings_SetPruningMode;
431 Interface->GetPruningMode = CDevSettings_GetPruningMode;
432
433 /* Read the HardwareInformation.* values from the registry key */
434 hKeyDev = pCDevSettings_OpenDeviceKey(This,
435 TRUE);
436 if (hKeyDev != NULL)
437 {
438 DWORD dwType, dwMemSize = 0;
439 DWORD dwSize = sizeof(dwMemSize);
440
441 if (RegQueryValueEx(hKeyDev,
442 TEXT("HardwareInformation.MemorySize"),
443 NULL,
444 &dwType,
445 (PBYTE)&dwMemSize,
446 &dwSize) == ERROR_SUCCESS &&
447 (dwType == REG_BINARY || dwType == REG_DWORD) &&
448 dwSize == sizeof(dwMemSize))
449 {
450 dwMemSize /= 1024;
451
452 if (dwMemSize > 1024)
453 {
454 dwMemSize /= 1024;
455 if (dwMemSize > 1024)
456 {
457 wsprintf(Interface->MemorySize,
458 _T("%u GB"),
459 dwMemSize / 1024);
460 }
461 else
462 {
463 wsprintf(Interface->MemorySize,
464 _T("%u MB"),
465 dwMemSize);
466 }
467 }
468 else
469 {
470 wsprintf(Interface->MemorySize,
471 _T("%u KB"),
472 dwMemSize);
473 }
474 }
475
476 pCDevSettings_ReadHardwareInfo(hKeyDev,
477 TEXT("HardwareInformation.ChipType"),
478 Interface->ChipType);
479 pCDevSettings_ReadHardwareInfo(hKeyDev,
480 TEXT("HardwareInformation.DacType"),
481 Interface->DacType);
482 pCDevSettings_ReadHardwareInfo(hKeyDev,
483 TEXT("HardwareInformation.AdapterString"),
484 Interface->AdapterString);
485 pCDevSettings_ReadHardwareInfo(hKeyDev,
486 TEXT("HardwareInformation.BiosString"),
487 Interface->BiosString);
488 RegCloseKey(hKeyDev);
489 }
490 }
491
492 static HRESULT
pCDevSettings_Initialize(PCDevSettings This,PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)493 pCDevSettings_Initialize(PCDevSettings This,
494 PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
495 {
496 HKEY hKey;
497 DWORD i = 0, dwSize;
498 DEVMODEW devmode;
499
500 This->lpOrigDevMode = NULL;
501 This->lpCurDevMode = NULL;
502 This->DevModes = NULL;
503 This->nDevModes = 0;
504 This->Flags = 0;
505 This->StateFlags = DisplayDeviceInfo->DeviceStateFlags;
506 DPRINT1("This->StateFlags: %x\n", This->StateFlags);
507
508 /* Register clipboard formats */
509 This->cfExtInterface = RegisterClipboardFormat(DESK_EXT_EXTINTERFACE);
510 This->cfDisplayDevice = RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE);
511 This->cfDisplayName = RegisterClipboardFormat(DESK_EXT_DISPLAYNAME);
512 This->cfDisplayId = RegisterClipboardFormat(DESK_EXT_DISPLAYID);
513 This->cfDisplayKey = RegisterClipboardFormat(DESK_EXT_DISPLAYKEY);
514 This->cfDisplayStateFlags = RegisterClipboardFormat(DESK_EXT_DISPLAYSTATEFLAGS);
515 This->cfMonitorName = RegisterClipboardFormat(DESK_EXT_MONITORNAME);
516 This->cfMonitorDevice = RegisterClipboardFormat(DESK_EXT_MONITORDEVICE);
517 This->cfPruningMode = RegisterClipboardFormat(DESK_EXT_PRUNINGMODE);
518
519 /* Copy the device name */
520 This->pDisplayDevice = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceName);
521 DPRINT1("This->pDisplayDevice: %ws\n", This->pDisplayDevice);
522 This->pDisplayName = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceDescription);
523 DPRINT1("This->pDisplayName: %ws\n", This->pDisplayName);
524 This->pDisplayKey = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceKey);
525 DPRINT1("This->pDisplayKey: %ws\n", This->pDisplayKey);
526 This->pDisplayId = pCDevSettings_GetDeviceInstanceId(DisplayDeviceInfo->DeviceID);
527 DPRINT1("This->pDisplayId: %ws\n", This->pDisplayId);
528 This->pMonitorName = pCDevSettings_GetMonitorName(This->pDisplayDevice);
529 DPRINT1("This->pMonitorName: %ws\n", This->pMonitorName);
530 This->pMonitorDevice = pCDevSettings_GetMonitorDevice(This->pDisplayDevice);
531 DPRINT1("This->pMonitorDevice: %ws\n", This->pMonitorDevice);
532
533 /* Check pruning mode */
534 This->bModesPruned = ((DisplayDeviceInfo->DeviceStateFlags & DISPLAY_DEVICE_MODESPRUNED) != 0);
535 hKey = pCDevSettings_OpenDeviceKey(This,
536 FALSE);
537 if (hKey == NULL)
538 {
539 hKey = pCDevSettings_OpenDeviceKey(This,
540 FALSE);
541 This->bKeyIsReadOnly = TRUE;
542 }
543 if (hKey != NULL)
544 {
545 DWORD dw = 0;
546 DWORD dwType;
547
548 dwSize = sizeof(dw);
549 if (RegQueryValueEx(hKey,
550 TEXT("PruningMode"),
551 NULL,
552 &dwType,
553 (PBYTE)&dw,
554 &dwSize) == ERROR_SUCCESS)
555 {
556 if (dwType == REG_DWORD && dwSize == sizeof(dw))
557 This->bPruningOn = (dw != 0);
558 }
559
560 RegCloseKey(hKey);
561 }
562
563 /* Initialize display modes */
564 ZeroMemory(&devmode, sizeof(devmode));
565 devmode.dmSize = (WORD)sizeof(devmode);
566 while (EnumDisplaySettingsExW(This->pDisplayDevice, i, &devmode, EDS_RAWMODE))
567 {
568 dwSize = devmode.dmSize + devmode.dmDriverExtra;
569 PDEVMODEW pDevMode = LocalAlloc(LMEM_FIXED, dwSize);
570 PDEVMODEW * DevModesNew = NULL;
571
572 if (pDevMode)
573 {
574 CopyMemory(pDevMode,
575 &devmode,
576 dwSize);
577
578 dwSize = (This->nDevModes + 1) * sizeof(pDevMode);
579 DevModesNew = LocalAlloc(LMEM_FIXED, dwSize);
580 if (DevModesNew)
581 {
582 if (This->DevModes)
583 {
584 CopyMemory(DevModesNew,
585 This->DevModes,
586 This->nDevModes * sizeof(pDevMode));
587
588 LocalFree(This->DevModes);
589 }
590
591 This->DevModes = DevModesNew;
592 This->DevModes[This->nDevModes++] = pDevMode;
593 }
594 else
595 {
596 DPRINT1("LocalAlloc failed to allocate %d bytes\n", dwSize);
597 return E_OUTOFMEMORY;
598 }
599 }
600 else
601 {
602 DPRINT1("LocalAlloc failed to allocate %d bytes\n", dwSize);
603 return E_OUTOFMEMORY;
604 }
605
606 devmode.dmDriverExtra = 0;
607 i++;
608 }
609
610 /* FIXME: Detect duplicated modes and mark them.
611 * Enumeration functions should check these marks
612 * and skip corresponding array entries. */
613
614 /* Get current display mode */
615 ZeroMemory(&devmode, sizeof(devmode));
616 devmode.dmSize = (WORD)sizeof(devmode);
617 if (EnumDisplaySettingsExW(This->pDisplayDevice, ENUM_CURRENT_SETTINGS, &devmode, 0))
618 {
619 for (i = 0; i < This->nDevModes; i++)
620 {
621 PDEVMODEW CurMode = This->DevModes[i];
622
623 if (!CurMode)
624 continue;
625
626 if (((CurMode->dmFields & DM_PELSWIDTH) && devmode.dmPelsWidth == CurMode->dmPelsWidth) &&
627 ((CurMode->dmFields & DM_PELSHEIGHT) && devmode.dmPelsHeight == CurMode->dmPelsHeight) &&
628 ((CurMode->dmFields & DM_BITSPERPEL) && devmode.dmBitsPerPel == CurMode->dmBitsPerPel) &&
629 ((CurMode->dmFields & DM_DISPLAYFREQUENCY) && devmode.dmDisplayFrequency == CurMode->dmDisplayFrequency))
630 {
631 This->lpOrigDevMode = This->lpCurDevMode = CurMode;
632 break;
633 }
634 }
635 }
636
637 /* Initialize the shell extension interface */
638 pCDevSettings_InitializeExtInterface(This);
639
640 return S_OK;
641 }
642
643 static VOID
pCDevSettings_Free(PCDevSettings This)644 pCDevSettings_Free(PCDevSettings This)
645 {
646 This->lpOrigDevMode = NULL;
647 This->lpCurDevMode = NULL;
648 while (This->nDevModes)
649 {
650 LocalFree(This->DevModes[--This->nDevModes]);
651 }
652 LocalFree(This->DevModes);
653 This->DevModes = NULL;
654
655 pCDevSettings_FreeString(&This->pDisplayDevice);
656 pCDevSettings_FreeString(&This->pDisplayName);
657 pCDevSettings_FreeString(&This->pDisplayKey);
658 pCDevSettings_FreeString(&This->pDisplayId);
659 pCDevSettings_FreeString(&This->pMonitorName);
660 pCDevSettings_FreeString(&This->pMonitorDevice);
661 }
662
663 static HRESULT STDMETHODCALLTYPE
CDevSettings_QueryInterface(IDataObject * iface,REFIID riid,void ** ppvObject)664 CDevSettings_QueryInterface(IDataObject* iface,
665 REFIID riid,
666 void** ppvObject)
667 {
668 PCDevSettings This = impl_from_IDataObject(iface);
669
670 *ppvObject = NULL;
671
672 if (IsEqualGUID(riid,
673 &IID_IUnknown) ||
674 IsEqualGUID(riid,
675 &IID_IDataObject))
676 {
677 *ppvObject = (PVOID)impl_to_interface(This, IDataObject);
678 return S_OK;
679 }
680 else
681 {
682 DPRINT1("CDevSettings::QueryInterface: Queried unknown interface\n");
683 }
684
685 return E_NOINTERFACE;
686 }
687
688 static ULONG STDMETHODCALLTYPE
CDevSettings_AddRef(IDataObject * iface)689 CDevSettings_AddRef(IDataObject* iface)
690 {
691 PCDevSettings This = impl_from_IDataObject(iface);
692 return (ULONG)InterlockedIncrement((PLONG)&This->ref);
693 }
694
695 static ULONG STDMETHODCALLTYPE
CDevSettings_Release(IDataObject * iface)696 CDevSettings_Release(IDataObject* iface)
697 {
698 ULONG refs;
699 PCDevSettings This = impl_from_IDataObject(iface);
700 refs = (ULONG)InterlockedDecrement((PLONG)&This->ref);
701 if (refs == 0)
702 pCDevSettings_Free(This);
703
704 return refs;
705 }
706
707 static HRESULT STDMETHODCALLTYPE
CDevSettings_GetData(IDataObject * iface,FORMATETC * pformatetcIn,STGMEDIUM * pmedium)708 CDevSettings_GetData(IDataObject* iface,
709 FORMATETC* pformatetcIn,
710 STGMEDIUM* pmedium)
711 {
712 static const WCHAR szEmpty[] = {0};
713 HRESULT hr;
714 PCWSTR pszRet = NULL;
715 PWSTR pszBuf;
716 PCDevSettings This = impl_from_IDataObject(iface);
717
718 ZeroMemory(pmedium,
719 sizeof(STGMEDIUM));
720
721 hr = IDataObject_QueryGetData(iface,
722 pformatetcIn);
723 if (SUCCEEDED(hr))
724 {
725 /* Return the requested data back to the shell extension */
726
727 if (pformatetcIn->cfFormat == This->cfDisplayDevice)
728 {
729 pszRet = This->pDisplayDevice;
730 DPRINT1("CDevSettings::GetData returns display device %ws\n", pszRet);
731 }
732 else if (pformatetcIn->cfFormat == This->cfDisplayName)
733 {
734 pszRet = This->pDisplayName;
735 DPRINT1("CDevSettings::GetData returns display name %ws\n", pszRet);
736 }
737 else if (pformatetcIn->cfFormat == This->cfDisplayKey)
738 {
739 pszRet = This->pDisplayKey;
740 DPRINT1("CDevSettings::GetData returns display key %ws\n", pszRet);
741 }
742 else if (pformatetcIn->cfFormat == This->cfDisplayId)
743 {
744 pszRet = This->pDisplayId;
745 DPRINT1("CDevSettings::GetData returns display id %ws\n", pszRet);
746 }
747 else if (pformatetcIn->cfFormat == This->cfMonitorName)
748 {
749 pszRet = This->pMonitorName;
750 DPRINT1("CDevSettings::GetData returns monitor name %ws\n", pszRet);
751 }
752 else if (pformatetcIn->cfFormat == This->cfMonitorDevice)
753 {
754 pszRet = This->pMonitorDevice;
755 DPRINT1("CDevSettings::GetData returns monitor device %ws\n", pszRet);
756 }
757 else if (pformatetcIn->cfFormat == This->cfExtInterface)
758 {
759 PDESK_EXT_INTERFACE pIface;
760
761 pIface = GlobalAlloc(GPTR,
762 sizeof(*pIface));
763 if (pIface != NULL)
764 {
765 CopyMemory(pIface,
766 &This->ExtInterface,
767 sizeof(This->ExtInterface));
768
769 DPRINT1("CDevSettings::GetData returns the desk.cpl extension interface\n");
770
771 pmedium->tymed = TYMED_HGLOBAL;
772 pmedium->hGlobal = pIface;
773
774 return S_OK;
775 }
776 else
777 return E_OUTOFMEMORY;
778 }
779 else if (pformatetcIn->cfFormat == This->cfDisplayStateFlags)
780 {
781 PDWORD pdw;
782
783 pdw = GlobalAlloc(GPTR,
784 sizeof(*pdw));
785 if (pdw != NULL)
786 {
787 *pdw = This->StateFlags;
788
789 DPRINT1("CDevSettings::GetData returns the display state flags %x\n", This->StateFlags);
790
791 pmedium->tymed = TYMED_HGLOBAL;
792 pmedium->hGlobal = pdw;
793
794 return S_OK;
795 }
796 else
797 return E_OUTOFMEMORY;
798 }
799 else if (pformatetcIn->cfFormat == This->cfPruningMode)
800 {
801 PBYTE pb;
802
803 pb = GlobalAlloc(GPTR,
804 sizeof(*pb));
805 if (pb != NULL)
806 {
807 *pb = (This->bModesPruned && This->bPruningOn);
808
809 pmedium->tymed = TYMED_HGLOBAL;
810 pmedium->hGlobal = pb;
811
812 return S_OK;
813 }
814 else
815 return E_OUTOFMEMORY;
816 }
817
818 /* NOTE: This only returns null-terminated strings! */
819 if (pszRet == NULL)
820 pszRet = szEmpty;
821
822 pszBuf = GlobalAlloc(GPTR,
823 (wcslen(pszRet) + 1) * sizeof(WCHAR));
824 if (pszBuf != NULL)
825 {
826 hr = StringCbCopyW(pszBuf, (wcslen(pszRet) + 1) * sizeof(WCHAR), pszRet);
827 if (FAILED(hr))
828 {
829 GlobalFree(pszBuf);
830 return hr;
831 }
832
833 pmedium->tymed = TYMED_HGLOBAL;
834 pmedium->hGlobal = pszBuf;
835
836 hr = S_OK;
837 }
838 else
839 hr = E_OUTOFMEMORY;
840 }
841
842 return hr;
843 }
844
845 static HRESULT STDMETHODCALLTYPE
CDevSettings_GetDataHere(IDataObject * iface,FORMATETC * pformatetc,STGMEDIUM * pmedium)846 CDevSettings_GetDataHere(IDataObject* iface,
847 FORMATETC* pformatetc,
848 STGMEDIUM* pmedium)
849 {
850 ZeroMemory(pformatetc,
851 sizeof(*pformatetc));
852 return E_NOTIMPL;
853 }
854
855 static HRESULT STDMETHODCALLTYPE
CDevSettings_QueryGetData(IDataObject * iface,FORMATETC * pformatetc)856 CDevSettings_QueryGetData(IDataObject* iface,
857 FORMATETC* pformatetc)
858 {
859 #if DEBUG
860 TCHAR szFormatName[255];
861 #endif
862 PCDevSettings This = impl_from_IDataObject(iface);
863
864 if (pformatetc->dwAspect != DVASPECT_CONTENT)
865 return DV_E_DVASPECT;
866
867 if (pformatetc->lindex != -1)
868 return DV_E_LINDEX;
869
870 if (!(pformatetc->tymed & TYMED_HGLOBAL))
871 return DV_E_TYMED;
872
873 /* Check if the requested data can be provided */
874 if (pformatetc->cfFormat == This->cfExtInterface ||
875 pformatetc->cfFormat == This->cfDisplayDevice ||
876 pformatetc->cfFormat == This->cfDisplayName ||
877 pformatetc->cfFormat == This->cfDisplayId ||
878 pformatetc->cfFormat == This->cfDisplayKey ||
879 pformatetc->cfFormat == This->cfDisplayStateFlags ||
880 pformatetc->cfFormat == This->cfMonitorDevice ||
881 pformatetc->cfFormat == This->cfMonitorName ||
882 pformatetc->cfFormat == This->cfPruningMode)
883 {
884 return S_OK;
885 }
886 #if DEBUG
887 else
888 {
889 if (GetClipboardFormatName(pformatetc->cfFormat,
890 szFormatName,
891 sizeof(szFormatName) / sizeof(szFormatName[0])))
892 {
893 DPRINT1("CDevSettings::QueryGetData(\"%ws\")\n", szFormatName);
894 }
895 else
896 {
897 DPRINT1("CDevSettings::QueryGetData(Format %u)\n", (unsigned int)pformatetc->cfFormat);
898 }
899 }
900 #endif
901
902 return DV_E_FORMATETC;
903 }
904
905 static HRESULT STDMETHODCALLTYPE
CDevSettings_GetCanonicalFormatEtc(IDataObject * iface,FORMATETC * pformatectIn,FORMATETC * pformatetcOut)906 CDevSettings_GetCanonicalFormatEtc(IDataObject* iface,
907 FORMATETC* pformatectIn,
908 FORMATETC* pformatetcOut)
909 {
910 HRESULT hr;
911
912 DPRINT1("CDevSettings::GetCanonicalFormatEtc\n");
913
914 hr = IDataObject_QueryGetData(iface,
915 pformatectIn);
916 if (SUCCEEDED(hr))
917 {
918 CopyMemory(pformatetcOut,
919 pformatectIn,
920 sizeof(FORMATETC));
921
922 /* Make sure the data is target device independent */
923 if (pformatectIn->ptd == NULL)
924 hr = DATA_S_SAMEFORMATETC;
925 else
926 {
927 pformatetcOut->ptd = NULL;
928 hr = S_OK;
929 }
930 }
931 else
932 {
933 ZeroMemory(pformatetcOut,
934 sizeof(FORMATETC));
935 }
936
937 return hr;
938 }
939
940 static HRESULT STDMETHODCALLTYPE
CDevSettings_SetData(IDataObject * iface,FORMATETC * pformatetc,STGMEDIUM * pmedium,BOOL fRelease)941 CDevSettings_SetData(IDataObject* iface,
942 FORMATETC* pformatetc,
943 STGMEDIUM* pmedium,
944 BOOL fRelease)
945 {
946 DPRINT1("CDevSettings::SetData UNIMPLEMENTED\n");
947 return E_NOTIMPL;
948 }
949
950 static __inline VOID
pCDevSettings_FillFormatEtc(FORMATETC * pFormatEtc,CLIPFORMAT cf)951 pCDevSettings_FillFormatEtc(FORMATETC *pFormatEtc,
952 CLIPFORMAT cf)
953 {
954 pFormatEtc->cfFormat = cf;
955 pFormatEtc->ptd = NULL;
956 pFormatEtc->dwAspect = DVASPECT_CONTENT;
957 pFormatEtc->lindex = -1;
958 pFormatEtc->tymed = TYMED_HGLOBAL;
959 }
960
961 static HRESULT STDMETHODCALLTYPE
CDevSettings_EnumFormatEtc(IDataObject * iface,DWORD dwDirection,IEnumFORMATETC ** ppenumFormatEtc)962 CDevSettings_EnumFormatEtc(IDataObject* iface,
963 DWORD dwDirection,
964 IEnumFORMATETC** ppenumFormatEtc)
965 {
966 HRESULT hr;
967 FORMATETC fetc[9];
968 PCDevSettings This = impl_from_IDataObject(iface);
969
970 *ppenumFormatEtc = NULL;
971
972 if (dwDirection == DATADIR_GET)
973 {
974 pCDevSettings_FillFormatEtc(&fetc[0],
975 This->cfExtInterface);
976 pCDevSettings_FillFormatEtc(&fetc[1],
977 This->cfDisplayDevice);
978 pCDevSettings_FillFormatEtc(&fetc[2],
979 This->cfDisplayName);
980 pCDevSettings_FillFormatEtc(&fetc[3],
981 This->cfDisplayId);
982 pCDevSettings_FillFormatEtc(&fetc[4],
983 This->cfDisplayKey);
984 pCDevSettings_FillFormatEtc(&fetc[5],
985 This->cfDisplayStateFlags);
986 pCDevSettings_FillFormatEtc(&fetc[6],
987 This->cfMonitorName);
988 pCDevSettings_FillFormatEtc(&fetc[7],
989 This->cfMonitorDevice);
990 pCDevSettings_FillFormatEtc(&fetc[8],
991 This->cfPruningMode);
992
993 hr = SHCreateStdEnumFmtEtc(sizeof(fetc) / sizeof(fetc[0]),
994 fetc,
995 ppenumFormatEtc);
996 }
997 else
998 hr = E_NOTIMPL;
999
1000 return hr;
1001 }
1002
1003 static HRESULT STDMETHODCALLTYPE
CDevSettings_DAdvise(IDataObject * iface,FORMATETC * pformatetc,DWORD advf,IAdviseSink * pAdvSink,DWORD * pdwConnection)1004 CDevSettings_DAdvise(IDataObject* iface,
1005 FORMATETC* pformatetc,
1006 DWORD advf,
1007 IAdviseSink* pAdvSink,
1008 DWORD* pdwConnection)
1009 {
1010 *pdwConnection = 0;
1011 return OLE_E_ADVISENOTSUPPORTED;
1012 }
1013
1014 static HRESULT STDMETHODCALLTYPE
CDevSettings_DUnadvise(IDataObject * iface,DWORD dwConnection)1015 CDevSettings_DUnadvise(IDataObject* iface,
1016 DWORD dwConnection)
1017 {
1018 return OLE_E_ADVISENOTSUPPORTED;
1019 }
1020
1021 static HRESULT STDMETHODCALLTYPE
CDevSettings_EnumDAdvise(IDataObject * iface,IEnumSTATDATA ** ppenumAdvise)1022 CDevSettings_EnumDAdvise(IDataObject* iface,
1023 IEnumSTATDATA** ppenumAdvise)
1024 {
1025 *ppenumAdvise = NULL;
1026 return OLE_E_ADVISENOTSUPPORTED;
1027 }
1028
1029 static const struct IDataObjectVtbl vtblIDataObject = {
1030 CDevSettings_QueryInterface,
1031 CDevSettings_AddRef,
1032 CDevSettings_Release,
1033 CDevSettings_GetData,
1034 CDevSettings_GetDataHere,
1035 CDevSettings_QueryGetData,
1036 CDevSettings_GetCanonicalFormatEtc,
1037 CDevSettings_SetData,
1038 CDevSettings_EnumFormatEtc,
1039 CDevSettings_DAdvise,
1040 CDevSettings_DUnadvise,
1041 CDevSettings_EnumDAdvise,
1042 };
1043
1044 IDataObject *
CreateDevSettings(PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)1045 CreateDevSettings(PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
1046 {
1047 PCDevSettings This;
1048
1049 This = HeapAlloc(GetProcessHeap(),
1050 0,
1051 sizeof(*This));
1052 if (This != NULL)
1053 {
1054 This->lpIDataObjectVtbl = &vtblIDataObject;
1055 This->ref = 1;
1056
1057 if (SUCCEEDED(pCDevSettings_Initialize(This,
1058 DisplayDeviceInfo)))
1059 {
1060 return impl_to_interface(This, IDataObject);
1061 }
1062
1063 CDevSettings_Release(impl_to_interface(This, IDataObject));
1064 }
1065
1066 return NULL;
1067 }
1068
1069 LONG WINAPI
DisplaySaveSettings(PVOID pContext,HWND hwndPropSheet)1070 DisplaySaveSettings(PVOID pContext,
1071 HWND hwndPropSheet)
1072 {
1073 PCDevSettings This = impl_from_IDataObject((IDataObject *)pContext);
1074 LONG rc = DISP_CHANGE_SUCCESSFUL;
1075
1076 if (This->lpCurDevMode != This->lpOrigDevMode)
1077 {
1078 SETTINGS_ENTRY seOrig, seCur;
1079 BOOL Ret;
1080
1081 seOrig.dmPelsWidth = This->lpOrigDevMode->dmPelsWidth;
1082 seOrig.dmPelsHeight = This->lpOrigDevMode->dmPelsHeight;
1083 seOrig.dmBitsPerPel = This->lpOrigDevMode->dmBitsPerPel;
1084 seOrig.dmDisplayFrequency = This->lpOrigDevMode->dmDisplayFrequency;
1085
1086 seCur.dmPelsWidth = This->lpCurDevMode->dmPelsWidth;
1087 seCur.dmPelsHeight = This->lpCurDevMode->dmPelsHeight;
1088 seCur.dmBitsPerPel = This->lpCurDevMode->dmBitsPerPel;
1089 seCur.dmDisplayFrequency = This->lpCurDevMode->dmDisplayFrequency;
1090
1091 Ret = SwitchDisplayMode(hwndPropSheet,
1092 This->pDisplayDevice,
1093 &seOrig,
1094 &seCur,
1095 &rc);
1096
1097 if (rc == DISP_CHANGE_SUCCESSFUL)
1098 {
1099 if (Ret)
1100 This->lpOrigDevMode = This->lpCurDevMode;
1101 else
1102 This->lpCurDevMode = This->lpOrigDevMode;
1103 }
1104 }
1105
1106 return rc;
1107 }
1108