xref: /reactos/dll/cpl/desk/devsett.c (revision 994e2f28)
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