xref: /reactos/dll/cpl/desk/devsett.c (revision ebaf247c)
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     DWORD StateFlags;
42 
43     union
44     {
45         DWORD Flags;
46         struct
47         {
48             DWORD bModesPruned : 1;
49             DWORD bKeyIsReadOnly : 1;
50             DWORD bPruningOn : 1;
51         };
52     };
53 } CDevSettings, *PCDevSettings;
54 
55 #define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
56 
57 static __inline PCDevSettings
58 impl_from_IDataObject(struct IDataObject *iface)
59 {
60     return (PCDevSettings)((ULONG_PTR)iface - FIELD_OFFSET(CDevSettings,
61                                                            lpIDataObjectVtbl));
62 }
63 
64 static __inline VOID
65 pCDevSettings_FreeString(PWCHAR *psz)
66 {
67     if (*psz != NULL)
68     {
69         LocalFree((HLOCAL)*psz);
70         *psz = NULL;
71     }
72 }
73 
74 static PWSTR
75 pCDevSettings_AllocAndCopyString(const TCHAR *pszSrc)
76 {
77     SIZE_T c;
78     PWSTR str;
79 
80     c = _tcslen(pszSrc) + 1;
81     str = (PWSTR)LocalAlloc(LMEM_FIXED,
82                             c * sizeof(WCHAR));
83     if (str != NULL)
84     {
85 #ifdef UNICODE
86         StringCbCopyW(str, c * sizeof(WCHAR),
87                pszSrc);
88 #else
89         MultiByteToWideChar(CP_ACP,
90                             0,
91                             pszSrc,
92                             -1,
93                             str,
94                             c);
95 #endif
96     }
97 
98     return str;
99 }
100 
101 static PWSTR
102 pCDevSettings_GetMonitorName(const WCHAR *pszDisplayDevice)
103 {
104     DISPLAY_DEVICEW dd, dd2;
105     PWSTR str = NULL;
106 
107     dd.cb = sizeof(dd);
108     if (EnumDisplayDevicesW(pszDisplayDevice,
109                             0,
110                             &dd,
111                             0))
112     {
113         dd2.cb = sizeof(dd2);
114         if (EnumDisplayDevicesW(pszDisplayDevice,
115                                 1,
116                                 &dd2,
117                                 0))
118         {
119             /* There's more than one monitor connected... */
120             LoadStringW(hApplet,
121                         IDS_MULTIPLEMONITORS,
122                         dd.DeviceString,
123                         sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
124         }
125     }
126     else
127     {
128         /* We can't enumerate a monitor, make sure this fact is reported
129            to the user! */
130         LoadStringW(hApplet,
131                     IDS_UNKNOWNMONITOR,
132                     dd.DeviceString,
133                     sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
134     }
135 
136     str = LocalAlloc(LMEM_FIXED,
137                      (wcslen(dd.DeviceString) + 1) * sizeof(WCHAR));
138     if (str != NULL)
139     {
140         wcscpy(str,
141                dd.DeviceString);
142     }
143 
144     return str;
145 }
146 
147 static PWSTR
148 pCDevSettings_GetMonitorDevice(const WCHAR *pszDisplayDevice)
149 {
150     DISPLAY_DEVICEW dd;
151     PWSTR str = NULL;
152 
153     dd.cb = sizeof(dd);
154     if (EnumDisplayDevicesW(pszDisplayDevice,
155                             0,
156                             &dd,
157                             0))
158     {
159         str = LocalAlloc(LMEM_FIXED,
160                          (wcslen(dd.DeviceName) + 1) * sizeof(WCHAR));
161         if (str != NULL)
162         {
163             wcscpy(str,
164                    dd.DeviceName);
165         }
166     }
167 
168     return str;
169 }
170 
171 static PWSTR
172 pCDevSettings_GetDeviceInstanceId(const WCHAR *pszDevice)
173 {
174     DEVINST DevInst;
175     CONFIGRET cr;
176     ULONG BufLen;
177     LPWSTR lpDevInstId = NULL;
178 
179     DPRINT1("CDevSettings::GetDeviceInstanceId(%ws) UNIMPLEMENTED!\n", pszDevice);
180 
181     cr = CM_Locate_DevNodeW(&DevInst,
182                             (DEVINSTID_W)pszDevice,
183                             CM_LOCATE_DEVNODE_NORMAL);
184     if (cr == CR_SUCCESS)
185     {
186         DPRINT1("Success1\n");
187         cr = CM_Get_Device_ID_Size(&BufLen,
188                                    DevInst,
189                                    0);
190         if (cr == CR_SUCCESS)
191         {
192             DPRINT1("Success2\n");
193             lpDevInstId = LocalAlloc(LMEM_FIXED,
194                                      (BufLen + 1) * sizeof(WCHAR));
195 
196             if (lpDevInstId != NULL)
197             {
198                 DPRINT1("Success3\n");
199                 cr = CM_Get_Device_IDW(DevInst,
200                                        lpDevInstId,
201                                        BufLen,
202                                        0);
203 
204                 if (cr != CR_SUCCESS)
205                 {
206                     LocalFree((HLOCAL)lpDevInstId);
207                     lpDevInstId = NULL;
208                 }
209                 DPRINT1("instance id: %ws\n", lpDevInstId);
210             }
211         }
212     }
213 
214     return lpDevInstId;
215 }
216 
217 
218 static HKEY
219 pCDevSettings_OpenDeviceKey(PCDevSettings This,
220                             BOOL ReadOnly)
221 {
222     static const WCHAR szRegPrefix[] = L"\\Registry\\Machine\\";
223     PWSTR lpRegKey;
224     REGSAM Access = KEY_READ;
225     HKEY hKey;
226 
227     lpRegKey = This->pDisplayKey;
228     if (lpRegKey != NULL)
229     {
230         if (wcslen(lpRegKey) >= wcslen(szRegPrefix) &&
231             !_wcsnicmp(lpRegKey,
232                       szRegPrefix,
233                       wcslen(szRegPrefix)))
234         {
235             lpRegKey += wcslen(szRegPrefix);
236         }
237 
238         if (!ReadOnly)
239             Access |= KEY_WRITE;
240 
241         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
242                          lpRegKey,
243                          0,
244                          Access,
245                          &hKey) == ERROR_SUCCESS)
246         {
247             return hKey;
248         }
249     }
250 
251     return NULL;
252 }
253 
254 PDEVMODEW DESK_EXT_CALLBACK
255 CDevSettings_EnumAllModes(PVOID Context,
256                           DWORD Index)
257 {
258     //PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
259     /* FIXME: Implement */
260     DPRINT1("CDevSettings::EnumAllModes(%u)\n", Index);
261     return NULL;
262 }
263 
264 PDEVMODEW DESK_EXT_CALLBACK
265 CDevSettings_GetCurrentMode(PVOID Context)
266 {
267     //PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
268     /* FIXME: Implement */
269     DPRINT1("CDevSettings::GetCurrentMode\n");
270     return NULL;
271 }
272 
273 BOOL DESK_EXT_CALLBACK
274 CDevSettings_SetCurrentMode(PVOID Context,
275                             const DEVMODEW *pDevMode)
276 {
277     //PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
278     /* FIXME: Implement */
279     DPRINT1("CDevSettings::SetCurrentMode(0x%p)\n", pDevMode);
280     return FALSE;
281 }
282 
283 VOID DESK_EXT_CALLBACK
284 CDevSettings_GetPruningMode(PVOID Context,
285                             PBOOL pbModesPruned,
286                             PBOOL pbKeyIsReadOnly,
287                             PBOOL pbPruningOn)
288 {
289     PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
290 
291     DPRINT1("CDevSettings::GetPruningMode(%p,%p,%p)\n", pbModesPruned, pbKeyIsReadOnly, pbPruningOn);
292 
293     *pbModesPruned = This->bModesPruned;
294     *pbKeyIsReadOnly = This->bKeyIsReadOnly;
295     *pbPruningOn = This->bPruningOn;
296 }
297 
298 VOID DESK_EXT_CALLBACK
299 CDevSettings_SetPruningMode(PVOID Context,
300                             BOOL PruningOn)
301 {
302     HKEY hKey;
303     DWORD dwValue;
304     PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
305 
306     DPRINT1("CDevSettings::SetPruningMode(%d)\n", PruningOn);
307 
308     if (This->bModesPruned && !This->bKeyIsReadOnly &&
309         PruningOn != This->bPruningOn)
310     {
311         This->bPruningOn = (PruningOn != FALSE);
312 
313         hKey = pCDevSettings_OpenDeviceKey(This,
314                                            FALSE);
315         if (hKey != NULL)
316         {
317             dwValue = (DWORD)This->bPruningOn;
318 
319             RegSetValueEx(hKey,
320                           TEXT("PruningMode"),
321                           0,
322                           REG_DWORD,
323                           (const BYTE *)&dwValue,
324                           sizeof(dwValue));
325 
326             RegCloseKey(hKey);
327         }
328     }
329 }
330 
331 static VOID
332 pCDevSettings_ReadHardwareInfo(HKEY hKey,
333                                LPCTSTR lpValueName,
334                                LPWSTR lpBuffer)
335 {
336     DWORD type = REG_BINARY;
337     DWORD size = 128 * sizeof(WCHAR);
338     RegQueryValueEx(hKey,
339                     lpValueName,
340                     NULL,
341                     &type,
342                     (PBYTE)lpBuffer,
343                     &size);
344 }
345 
346 static VOID
347 pCDevSettings_InitializeExtInterface(PCDevSettings This)
348 {
349     PDESK_EXT_INTERFACE Interface = &This->ExtInterface;
350     HKEY hKeyDev;
351 
352     ZeroMemory(Interface,
353                sizeof(*Interface));
354     Interface->cbSize = sizeof(*Interface);
355 
356     /* Initialize the callback table */
357     Interface->Context = impl_to_interface(This, IDataObject);
358     Interface->EnumAllModes = CDevSettings_EnumAllModes;
359     Interface->SetCurrentMode = CDevSettings_SetCurrentMode;
360     Interface->GetCurrentMode = CDevSettings_GetCurrentMode;
361     Interface->SetPruningMode = CDevSettings_SetPruningMode;
362     Interface->GetPruningMode = CDevSettings_GetPruningMode;
363 
364     /* Read the HardwareInformation.* values from the registry key */
365     hKeyDev = pCDevSettings_OpenDeviceKey(This,
366                                           TRUE);
367     if (hKeyDev != NULL)
368     {
369         DWORD dwType, dwMemSize = 0;
370         DWORD dwSize = sizeof(dwMemSize);
371 
372         if (RegQueryValueEx(hKeyDev,
373                             TEXT("HardwareInformation.MemorySize"),
374                             NULL,
375                             &dwType,
376                             (PBYTE)&dwMemSize,
377                             &dwSize) == ERROR_SUCCESS &&
378             (dwType == REG_BINARY || dwType == REG_DWORD) &&
379             dwSize == sizeof(dwMemSize))
380         {
381             dwMemSize /= 1024;
382 
383             if (dwMemSize > 1024)
384             {
385                 dwMemSize /= 1024;
386                 if (dwMemSize > 1024)
387                 {
388                     wsprintf(Interface->MemorySize,
389                              _T("%u GB"),
390                              dwMemSize / 1024);
391                 }
392                 else
393                 {
394                     wsprintf(Interface->MemorySize,
395                              _T("%u MB"),
396                              dwMemSize);
397                 }
398             }
399             else
400             {
401                 wsprintf(Interface->MemorySize,
402                          _T("%u KB"),
403                          dwMemSize);
404             }
405         }
406 
407         pCDevSettings_ReadHardwareInfo(hKeyDev,
408                                        TEXT("HardwareInformation.ChipType"),
409                                        Interface->ChipType);
410         pCDevSettings_ReadHardwareInfo(hKeyDev,
411                                        TEXT("HardwareInformation.DacType"),
412                                        Interface->DacType);
413         pCDevSettings_ReadHardwareInfo(hKeyDev,
414                                        TEXT("HardwareInformation.AdapterString"),
415                                        Interface->AdapterString);
416         pCDevSettings_ReadHardwareInfo(hKeyDev,
417                                        TEXT("HardwareInformation.BiosString"),
418                                        Interface->BiosString);
419         RegCloseKey(hKeyDev);
420     }
421 }
422 
423 static HRESULT
424 pCDevSettings_Initialize(PCDevSettings This,
425                          PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
426 {
427     HKEY hKey;
428 
429     This->Flags = 0;
430     This->StateFlags = DisplayDeviceInfo->DeviceStateFlags;
431     DPRINT1("This->StateFlags: %x\n", This->StateFlags);
432 
433     /* Register clipboard formats */
434     This->cfExtInterface = RegisterClipboardFormat(DESK_EXT_EXTINTERFACE);
435     This->cfDisplayDevice = RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE);
436     This->cfDisplayName = RegisterClipboardFormat(DESK_EXT_DISPLAYNAME);
437     This->cfDisplayId = RegisterClipboardFormat(DESK_EXT_DISPLAYID);
438     This->cfDisplayKey = RegisterClipboardFormat(DESK_EXT_DISPLAYKEY);
439     This->cfDisplayStateFlags = RegisterClipboardFormat(DESK_EXT_DISPLAYSTATEFLAGS);
440     This->cfMonitorName = RegisterClipboardFormat(DESK_EXT_MONITORNAME);
441     This->cfMonitorDevice = RegisterClipboardFormat(DESK_EXT_MONITORDEVICE);
442     This->cfPruningMode = RegisterClipboardFormat(DESK_EXT_PRUNINGMODE);
443 
444     /* Copy the device name */
445     This->pDisplayDevice = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceName);
446     DPRINT1("This->pDisplayDevice: %ws\n", This->pDisplayDevice);
447     This->pDisplayName = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceDescription);
448     DPRINT1("This->pDisplayName: %ws\n", This->pDisplayName);
449     This->pDisplayKey = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceKey);
450     DPRINT1("This->pDisplayKey: %ws\n", This->pDisplayKey);
451     This->pDisplayId = pCDevSettings_GetDeviceInstanceId(DisplayDeviceInfo->DeviceID);
452     DPRINT1("This->pDisplayId: %ws\n", This->pDisplayId);
453     This->pMonitorName = pCDevSettings_GetMonitorName(This->pDisplayDevice);
454     DPRINT1("This->pMonitorName: %ws\n", This->pMonitorName);
455     This->pMonitorDevice = pCDevSettings_GetMonitorDevice(This->pDisplayDevice);
456     DPRINT1("This->pMonitorDevice: %ws\n", This->pMonitorDevice);
457 
458     /* Check pruning mode */
459     This->bModesPruned = ((DisplayDeviceInfo->DeviceStateFlags & DISPLAY_DEVICE_MODESPRUNED) != 0);
460     hKey = pCDevSettings_OpenDeviceKey(This,
461                                        FALSE);
462     if (hKey == NULL)
463     {
464         hKey = pCDevSettings_OpenDeviceKey(This,
465                                            FALSE);
466         This->bKeyIsReadOnly = TRUE;
467     }
468     if (hKey != NULL)
469     {
470         DWORD dw = 0;
471         DWORD dwType, dwSize;
472 
473         dwSize = sizeof(dw);
474         if (RegQueryValueEx(hKey,
475                             TEXT("PruningMode"),
476                             NULL,
477                             &dwType,
478                             (PBYTE)&dw,
479                             &dwSize) == ERROR_SUCCESS)
480         {
481             if (dwType == REG_DWORD && dwSize == sizeof(dw))
482                 This->bPruningOn = (dw != 0);
483         }
484 
485         RegCloseKey(hKey);
486     }
487 
488     /* Initialize the shell extension interface */
489     pCDevSettings_InitializeExtInterface(This);
490 
491     return S_OK;
492 }
493 
494 static VOID
495 pCDevSettings_Free(PCDevSettings This)
496 {
497     pCDevSettings_FreeString(&This->pDisplayDevice);
498     pCDevSettings_FreeString(&This->pDisplayName);
499     pCDevSettings_FreeString(&This->pDisplayKey);
500     pCDevSettings_FreeString(&This->pDisplayId);
501     pCDevSettings_FreeString(&This->pMonitorName);
502     pCDevSettings_FreeString(&This->pMonitorDevice);
503 }
504 
505 static HRESULT STDMETHODCALLTYPE
506 CDevSettings_QueryInterface(IDataObject* iface,
507                             REFIID riid,
508                             void** ppvObject)
509 {
510     PCDevSettings This = impl_from_IDataObject(iface);
511 
512     *ppvObject = NULL;
513 
514     if (IsEqualGUID(riid,
515                     &IID_IUnknown) ||
516         IsEqualGUID(riid,
517                     &IID_IDataObject))
518     {
519         *ppvObject = (PVOID)impl_to_interface(This, IDataObject);
520         return S_OK;
521     }
522     else
523     {
524         DPRINT1("CDevSettings::QueryInterface: Queried unknown interface\n");
525     }
526 
527     return E_NOINTERFACE;
528 }
529 
530 static ULONG STDMETHODCALLTYPE
531 CDevSettings_AddRef(IDataObject* iface)
532 {
533     PCDevSettings This = impl_from_IDataObject(iface);
534     return (ULONG)InterlockedIncrement((PLONG)&This->ref);
535 }
536 
537 static ULONG STDMETHODCALLTYPE
538 CDevSettings_Release(IDataObject* iface)
539 {
540     ULONG refs;
541     PCDevSettings This = impl_from_IDataObject(iface);
542     refs = (ULONG)InterlockedDecrement((PLONG)&This->ref);
543     if (refs == 0)
544         pCDevSettings_Free(This);
545 
546     return refs;
547 }
548 
549 static HRESULT STDMETHODCALLTYPE
550 CDevSettings_GetData(IDataObject* iface,
551                      FORMATETC* pformatetcIn,
552                      STGMEDIUM* pmedium)
553 {
554     static const WCHAR szEmpty[] = {0};
555     HRESULT hr;
556     PCWSTR pszRet = NULL;
557     PWSTR pszBuf;
558     PCDevSettings This = impl_from_IDataObject(iface);
559 
560     ZeroMemory(pmedium,
561                sizeof(STGMEDIUM));
562 
563     hr = IDataObject_QueryGetData(iface,
564                                   pformatetcIn);
565     if (SUCCEEDED(hr))
566     {
567         /* Return the requested data back to the shell extension */
568 
569         if (pformatetcIn->cfFormat == This->cfDisplayDevice)
570         {
571             pszRet = This->pDisplayDevice;
572             DPRINT1("CDevSettings::GetData returns display device %ws\n", pszRet);
573         }
574         else if (pformatetcIn->cfFormat == This->cfDisplayName)
575         {
576             pszRet = This->pDisplayName;
577             DPRINT1("CDevSettings::GetData returns display name %ws\n", pszRet);
578         }
579         else if (pformatetcIn->cfFormat == This->cfDisplayKey)
580         {
581             pszRet = This->pDisplayKey;
582             DPRINT1("CDevSettings::GetData returns display key %ws\n", pszRet);
583         }
584         else if (pformatetcIn->cfFormat == This->cfDisplayId)
585         {
586             pszRet = This->pDisplayId;
587             DPRINT1("CDevSettings::GetData returns display id %ws\n", pszRet);
588         }
589         else if (pformatetcIn->cfFormat == This->cfMonitorName)
590         {
591             pszRet = This->pMonitorName;
592             DPRINT1("CDevSettings::GetData returns monitor name %ws\n", pszRet);
593         }
594         else if (pformatetcIn->cfFormat == This->cfMonitorDevice)
595         {
596             pszRet = This->pMonitorDevice;
597             DPRINT1("CDevSettings::GetData returns monitor device %ws\n", pszRet);
598         }
599         else if (pformatetcIn->cfFormat == This->cfExtInterface)
600         {
601             PDESK_EXT_INTERFACE pIface;
602 
603             pIface = GlobalAlloc(GPTR,
604                                  sizeof(*pIface));
605             if (pIface != NULL)
606             {
607                 CopyMemory(pIface,
608                            &This->ExtInterface,
609                            sizeof(This->ExtInterface));
610 
611                 DPRINT1("CDevSettings::GetData returns the desk.cpl extension interface\n");
612 
613                 pmedium->tymed = TYMED_HGLOBAL;
614                 pmedium->hGlobal = pIface;
615 
616                 return S_OK;
617             }
618             else
619                 return E_OUTOFMEMORY;
620         }
621         else if (pformatetcIn->cfFormat == This->cfDisplayStateFlags)
622         {
623             PDWORD pdw;
624 
625             pdw = GlobalAlloc(GPTR,
626                               sizeof(*pdw));
627             if (pdw != NULL)
628             {
629                 *pdw = This->StateFlags;
630 
631                 DPRINT1("CDevSettings::GetData returns the display state flags %x\n", This->StateFlags);
632 
633                 pmedium->tymed = TYMED_HGLOBAL;
634                 pmedium->hGlobal = pdw;
635 
636                 return S_OK;
637             }
638             else
639                 return E_OUTOFMEMORY;
640         }
641         else if (pformatetcIn->cfFormat == This->cfPruningMode)
642         {
643             PBYTE pb;
644 
645             pb = GlobalAlloc(GPTR,
646                              sizeof(*pb));
647             if (pb != NULL)
648             {
649                 *pb = (This->bModesPruned && This->bPruningOn);
650 
651                 pmedium->tymed = TYMED_HGLOBAL;
652                 pmedium->hGlobal = pb;
653 
654                 return S_OK;
655             }
656             else
657                 return E_OUTOFMEMORY;
658         }
659 
660         /* NOTE: This only returns null-terminated strings! */
661         if (pszRet == NULL)
662             pszRet = szEmpty;
663 
664         pszBuf = GlobalAlloc(GPTR,
665                              (wcslen(pszRet) + 1) * sizeof(WCHAR));
666         if (pszBuf != NULL)
667         {
668             hr = StringCbCopyW(pszBuf, (wcslen(pszRet) + 1) * sizeof(WCHAR), pszRet);
669             if (FAILED(hr))
670             {
671                 GlobalFree(pszBuf);
672                 return hr;
673             }
674 
675             pmedium->tymed = TYMED_HGLOBAL;
676             pmedium->hGlobal = pszBuf;
677 
678             hr = S_OK;
679         }
680         else
681             hr = E_OUTOFMEMORY;
682     }
683 
684     return hr;
685 }
686 
687 static HRESULT STDMETHODCALLTYPE
688 CDevSettings_GetDataHere(IDataObject* iface,
689                          FORMATETC* pformatetc,
690                          STGMEDIUM* pmedium)
691 {
692     ZeroMemory(pformatetc,
693                sizeof(*pformatetc));
694     return E_NOTIMPL;
695 }
696 
697 static HRESULT STDMETHODCALLTYPE
698 CDevSettings_QueryGetData(IDataObject* iface,
699                           FORMATETC* pformatetc)
700 {
701 #if DEBUG
702     TCHAR szFormatName[255];
703 #endif
704     PCDevSettings This = impl_from_IDataObject(iface);
705 
706     if (pformatetc->dwAspect != DVASPECT_CONTENT)
707         return DV_E_DVASPECT;
708 
709     if (pformatetc->lindex != -1)
710         return DV_E_LINDEX;
711 
712     if (!(pformatetc->tymed & TYMED_HGLOBAL))
713         return DV_E_TYMED;
714 
715     /* Check if the requested data can be provided */
716     if (pformatetc->cfFormat == This->cfExtInterface ||
717         pformatetc->cfFormat == This->cfDisplayDevice ||
718         pformatetc->cfFormat == This->cfDisplayName ||
719         pformatetc->cfFormat == This->cfDisplayId ||
720         pformatetc->cfFormat == This->cfDisplayKey ||
721         pformatetc->cfFormat == This->cfDisplayStateFlags ||
722         pformatetc->cfFormat == This->cfMonitorDevice ||
723         pformatetc->cfFormat == This->cfMonitorName ||
724         pformatetc->cfFormat == This->cfPruningMode)
725     {
726         return S_OK;
727     }
728 #if DEBUG
729     else
730     {
731         if (GetClipboardFormatName(pformatetc->cfFormat,
732                                    szFormatName,
733                                    sizeof(szFormatName) / sizeof(szFormatName[0])))
734         {
735             DPRINT1("CDevSettings::QueryGetData(\"%ws\")\n", szFormatName);
736         }
737         else
738         {
739             DPRINT1("CDevSettings::QueryGetData(Format %u)\n", (unsigned int)pformatetc->cfFormat);
740         }
741     }
742 #endif
743 
744     return DV_E_FORMATETC;
745 }
746 
747 static HRESULT STDMETHODCALLTYPE
748 CDevSettings_GetCanonicalFormatEtc(IDataObject* iface,
749                                    FORMATETC* pformatectIn,
750                                    FORMATETC* pformatetcOut)
751 {
752     HRESULT hr;
753 
754     DPRINT1("CDevSettings::GetCanonicalFormatEtc\n");
755 
756     hr = IDataObject_QueryGetData(iface,
757                                   pformatectIn);
758     if (SUCCEEDED(hr))
759     {
760         CopyMemory(pformatetcOut,
761                    pformatectIn,
762                    sizeof(FORMATETC));
763 
764         /* Make sure the data is target device independent */
765         if (pformatectIn->ptd == NULL)
766             hr = DATA_S_SAMEFORMATETC;
767         else
768         {
769             pformatetcOut->ptd = NULL;
770             hr = S_OK;
771         }
772     }
773     else
774     {
775         ZeroMemory(pformatetcOut,
776                    sizeof(FORMATETC));
777     }
778 
779     return hr;
780 }
781 
782 static HRESULT STDMETHODCALLTYPE
783 CDevSettings_SetData(IDataObject* iface,
784                      FORMATETC* pformatetc,
785                      STGMEDIUM* pmedium,
786                      BOOL fRelease)
787 {
788     DPRINT1("CDevSettings::SetData UNIMPLEMENTED\n");
789     return E_NOTIMPL;
790 }
791 
792 static __inline VOID
793 pCDevSettings_FillFormatEtc(FORMATETC *pFormatEtc,
794                             CLIPFORMAT cf)
795 {
796     pFormatEtc->cfFormat = cf;
797     pFormatEtc->ptd = NULL;
798     pFormatEtc->dwAspect = DVASPECT_CONTENT;
799     pFormatEtc->lindex = -1;
800     pFormatEtc->tymed = TYMED_HGLOBAL;
801 }
802 
803 static HRESULT STDMETHODCALLTYPE
804 CDevSettings_EnumFormatEtc(IDataObject* iface,
805                            DWORD dwDirection,
806                            IEnumFORMATETC** ppenumFormatEtc)
807 {
808     HRESULT hr;
809     FORMATETC fetc[9];
810     PCDevSettings This = impl_from_IDataObject(iface);
811 
812     *ppenumFormatEtc = NULL;
813 
814     if (dwDirection == DATADIR_GET)
815     {
816         pCDevSettings_FillFormatEtc(&fetc[0],
817                                     This->cfExtInterface);
818         pCDevSettings_FillFormatEtc(&fetc[1],
819                                     This->cfDisplayDevice);
820         pCDevSettings_FillFormatEtc(&fetc[2],
821                                     This->cfDisplayName);
822         pCDevSettings_FillFormatEtc(&fetc[3],
823                                     This->cfDisplayId);
824         pCDevSettings_FillFormatEtc(&fetc[4],
825                                     This->cfDisplayKey);
826         pCDevSettings_FillFormatEtc(&fetc[5],
827                                     This->cfDisplayStateFlags);
828         pCDevSettings_FillFormatEtc(&fetc[6],
829                                     This->cfMonitorName);
830         pCDevSettings_FillFormatEtc(&fetc[7],
831                                     This->cfMonitorDevice);
832         pCDevSettings_FillFormatEtc(&fetc[8],
833                                     This->cfPruningMode);
834 
835         hr = SHCreateStdEnumFmtEtc(sizeof(fetc) / sizeof(fetc[0]),
836                                    fetc,
837                                    ppenumFormatEtc);
838     }
839     else
840         hr = E_NOTIMPL;
841 
842     return hr;
843 }
844 
845 static HRESULT STDMETHODCALLTYPE
846 CDevSettings_DAdvise(IDataObject* iface,
847                      FORMATETC* pformatetc,
848                      DWORD advf,
849                      IAdviseSink* pAdvSink,
850                      DWORD* pdwConnection)
851 {
852     *pdwConnection = 0;
853     return OLE_E_ADVISENOTSUPPORTED;
854 }
855 
856 static HRESULT STDMETHODCALLTYPE
857 CDevSettings_DUnadvise(IDataObject* iface,
858                        DWORD dwConnection)
859 {
860     return OLE_E_ADVISENOTSUPPORTED;
861 }
862 
863 static HRESULT STDMETHODCALLTYPE
864 CDevSettings_EnumDAdvise(IDataObject* iface,
865                          IEnumSTATDATA** ppenumAdvise)
866 {
867     *ppenumAdvise = NULL;
868     return OLE_E_ADVISENOTSUPPORTED;
869 }
870 
871 static const struct IDataObjectVtbl vtblIDataObject = {
872     CDevSettings_QueryInterface,
873     CDevSettings_AddRef,
874     CDevSettings_Release,
875     CDevSettings_GetData,
876     CDevSettings_GetDataHere,
877     CDevSettings_QueryGetData,
878     CDevSettings_GetCanonicalFormatEtc,
879     CDevSettings_SetData,
880     CDevSettings_EnumFormatEtc,
881     CDevSettings_DAdvise,
882     CDevSettings_DUnadvise,
883     CDevSettings_EnumDAdvise,
884 };
885 
886 IDataObject *
887 CreateDevSettings(PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
888 {
889     PCDevSettings This;
890 
891     This = HeapAlloc(GetProcessHeap(),
892                      0,
893                      sizeof(*This));
894     if (This != NULL)
895     {
896         This->lpIDataObjectVtbl = &vtblIDataObject;
897         This->ref = 1;
898 
899         if (SUCCEEDED(pCDevSettings_Initialize(This,
900                                                DisplayDeviceInfo)))
901         {
902             return impl_to_interface(This, IDataObject);
903         }
904 
905         CDevSettings_Release(impl_to_interface(This, IDataObject));
906     }
907 
908     return NULL;
909 }
910 
911 LONG WINAPI
912 DisplaySaveSettings(PVOID pContext,
913                     HWND hwndPropSheet)
914 {
915     //PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
916     DPRINT("DisplaySaveSettings() UNIMPLEMENTED!\n");
917     return DISP_CHANGE_BADPARAM;
918 }
919