xref: /reactos/dll/shellext/deskmon/deskmon.c (revision fced1c11)
1 #include "precomp.h"
2 
3 #include <tchar.h>
4 #include <winreg.h>
5 #include <cfgmgr32.h>
6 
7 #define NDEBUG
8 #include <debug.h>
9 
10 #include "resource.h"
11 
12 static HINSTANCE hInstance;
13 static BOOL bFoundAdapter;
14 static DEVINST diAdapter;
15 
16 #ifdef UNICODE
17 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCWSTR,LPCWSTR,BOOL);
18 #define FUNC_DEVICEPROPERTIES "DevicePropertiesW"
19 #else
20 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCSTR,LPCSTR,BOOL);
21 #define FUNC_DEVICEPROPERTIES "DevicePropertiesA"
22 #endif
23 
24 /**
25  * @brief
26  * Converts a Hardware ID (DeviceID from EnumDisplayDevices)
27  * to an unique Device Instance ID.
28  *
29  * @param[in] lpDeviceID
30  * A pointer to a null-terminated string
31  * containing a Hardware ID concatenated with driver key.
32  * e.g. "Monitor\Default_Monitor\{4D36E96E-E325-11CE-BFC1-08002BE10318}\0000"
33  *
34  * @return
35  * A pointer to a null-terminated string
36  * containing an unique Device Instance ID
37  * or NULL in case of error.
38  * e.g. "DISPLAY\DEFAULT_MONITOR\4&2ABFAA30&0&00000001&00&02"
39  *
40  * @remarks
41  * The caller must free the returned string with LocalFree.
42  */
43 static LPTSTR
GetMonitorDevInstID(LPCTSTR lpDeviceID)44 GetMonitorDevInstID(LPCTSTR lpDeviceID)
45 {
46     CONFIGRET cr;
47     DEVINST diChild;
48     TCHAR szProperty[256];
49     DWORD dwSize;
50     LPTSTR lpDevInstId = NULL;
51 
52     if (!bFoundAdapter)
53         return NULL;
54 
55     /* Look for child monitor devices of selected video adapter */
56     cr = CM_Get_Child(&diChild, diAdapter, 0);
57     if (cr != CR_SUCCESS)
58     {
59         DPRINT1("CM_Get_Child failed: %d\n", cr);
60         return NULL;
61     }
62 
63     do
64     {
65         /* Get Hardware ID for each of them */
66         dwSize = sizeof(szProperty) - sizeof(TCHAR);
67 
68         cr = CM_Get_DevNode_Registry_Property(diChild,
69                                               CM_DRP_HARDWAREID,
70                                               NULL,
71                                               szProperty,
72                                               &dwSize,
73                                               0);
74         if (cr != CR_SUCCESS)
75         {
76             DPRINT1("CM_Get_DevNode_Registry_Property failed: %d\n", cr);
77             continue;
78         }
79 
80         /* Concatenate with driver key */
81         _tcscat(szProperty, TEXT("\\"));
82         dwSize = sizeof(szProperty) - sizeof(TCHAR);
83         dwSize -= _tcslen(szProperty) * sizeof(TCHAR);
84 
85         cr = CM_Get_DevNode_Registry_Property(diChild,
86                                               CM_DRP_DRIVER,
87                                               NULL,
88                                               szProperty + _tcslen(szProperty),
89                                               &dwSize,
90                                               0);
91         if (cr != CR_SUCCESS)
92         {
93             DPRINT1("CM_Get_DevNode_Registry_Property failed: %d\n", cr);
94             continue;
95         }
96 
97         /* If the strings match, this is our monitor device node */
98         if (_tcscmp(szProperty, lpDeviceID) == 0)
99         {
100             cr = CM_Get_Device_ID_Size(&dwSize,
101                                        diChild,
102                                        0);
103             if (cr != CR_SUCCESS)
104             {
105                 DPRINT1("CM_Get_Device_ID_Size failed: %d\n", cr);
106                 break;
107             }
108 
109             lpDevInstId = LocalAlloc(LMEM_FIXED,
110                                      (dwSize + 1) * sizeof(TCHAR));
111             if (lpDevInstId == NULL)
112             {
113                 DPRINT1("LocalAlloc failed\n");
114                 break;
115             }
116 
117             cr = CM_Get_Device_ID(diChild,
118                                   lpDevInstId,
119                                   dwSize + 1,
120                                   0);
121             if (cr != CR_SUCCESS)
122             {
123                 DPRINT1("CM_Get_Device_ID failed: %d\n", cr);
124                 LocalFree((HLOCAL)lpDevInstId);
125                 lpDevInstId = NULL;
126             }
127 
128             break;
129         }
130     } while (CM_Get_Sibling(&diChild, diChild, 0) == CR_SUCCESS);
131 
132     return lpDevInstId;
133 }
134 
135 static VOID
ShowMonitorProperties(PDESKMONITOR This)136 ShowMonitorProperties(PDESKMONITOR This)
137 {
138     HMODULE hDevMgr;
139     PDEVICEPROPERTIES pDeviceProperties;
140     LPTSTR lpDevInstID;
141 
142     if (This->SelMonitor != NULL)
143     {
144         lpDevInstID = GetMonitorDevInstID(This->SelMonitor->dd.DeviceID);
145         if (lpDevInstID != NULL)
146         {
147             hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
148             if (hDevMgr != NULL)
149             {
150                 pDeviceProperties = (PDEVICEPROPERTIES)GetProcAddress(hDevMgr,
151                                                                       FUNC_DEVICEPROPERTIES);
152                 if (pDeviceProperties != NULL)
153                 {
154                     pDeviceProperties(This->hwndDlg,
155                                       NULL,
156                                       lpDevInstID,
157                                       FALSE);
158                 }
159 
160                 FreeLibrary(hDevMgr);
161             }
162 
163             LocalFree((HLOCAL)lpDevInstID);
164         }
165     }
166 }
167 
168 static VOID
UpdateMonitorSelection(PDESKMONITOR This)169 UpdateMonitorSelection(PDESKMONITOR This)
170 {
171     INT i;
172     LPTSTR lpDevInstID = NULL;
173 
174     if (This->dwMonitorCount > 1)
175     {
176         This->SelMonitor = NULL;
177 
178         i = (INT)SendDlgItemMessage(This->hwndDlg,
179                                     IDC_MONITORLIST,
180                                     LB_GETCURSEL,
181                                     0,
182                                     0);
183         if (i >= 0)
184         {
185             This->SelMonitor = (PDESKMONINFO)SendDlgItemMessage(This->hwndDlg,
186                                                                 IDC_MONITORLIST,
187                                                                 LB_GETITEMDATA,
188                                                                 (WPARAM)i,
189                                                                 0);
190         }
191     }
192     else
193         This->SelMonitor = This->Monitors;
194 
195     if (This->SelMonitor != NULL)
196         lpDevInstID = GetMonitorDevInstID(This->SelMonitor->dd.DeviceID);
197 
198     EnableWindow(GetDlgItem(This->hwndDlg,
199                             IDC_MONITORPROPERTIES),
200                  lpDevInstID != NULL && lpDevInstID[0] != TEXT('\0'));
201 
202     if (lpDevInstID != NULL)
203         LocalFree((HLOCAL)lpDevInstID);
204 }
205 
206 static VOID
UpdatePruningControls(PDESKMONITOR This)207 UpdatePruningControls(PDESKMONITOR This)
208 {
209     EnableWindow(GetDlgItem(This->hwndDlg,
210                             IDC_PRUNINGCHECK),
211                  This->bModesPruned && !This->bKeyIsReadOnly);
212     CheckDlgButton(This->hwndDlg,
213                    IDC_PRUNINGCHECK,
214                    (This->bModesPruned && This->bPruningOn) ? BST_CHECKED : BST_UNCHECKED);
215 }
216 
217 static VOID
GetPruningSettings(PDESKMONITOR This)218 GetPruningSettings(PDESKMONITOR This)
219 {
220     BOOL bModesPruned = FALSE, bKeyIsReadOnly = FALSE, bPruningOn = FALSE;
221 
222     if (This->DeskExtInterface != NULL)
223     {
224         This->DeskExtInterface->GetPruningMode(This->DeskExtInterface->Context,
225                                                &bModesPruned,
226                                                &bKeyIsReadOnly,
227                                                &bPruningOn);
228     }
229 
230     /* Check the boolean values against zero before assigning it to the bitfields! */
231     This->bModesPruned = (bModesPruned != FALSE);
232     This->bKeyIsReadOnly = (bKeyIsReadOnly != FALSE);
233     This->bPruningOn = (bPruningOn != FALSE);
234 
235     UpdatePruningControls(This);
236 }
237 
238 static VOID
UpdateRefreshFrequencyList(PDESKMONITOR This)239 UpdateRefreshFrequencyList(PDESKMONITOR This)
240 {
241     PDEVMODEW lpCurrentMode, lpMode;
242     TCHAR szBuffer[64];
243     DWORD dwIndex;
244     INT i;
245     BOOL bHasDef = FALSE;
246     BOOL bAdded = FALSE;
247 
248     /* Fill the refresh rate combo box */
249     SendDlgItemMessage(This->hwndDlg,
250                        IDC_REFRESHRATE,
251                        CB_RESETCONTENT,
252                        0,
253                        0);
254 
255     lpCurrentMode = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
256     dwIndex = 0;
257 
258     do
259     {
260         lpMode = This->DeskExtInterface->EnumAllModes(This->DeskExtInterface->Context,
261                                                       dwIndex++);
262         if (lpMode != NULL &&
263             lpMode->dmBitsPerPel == lpCurrentMode->dmBitsPerPel &&
264             lpMode->dmPelsWidth == lpCurrentMode->dmPelsWidth &&
265             lpMode->dmPelsHeight == lpCurrentMode->dmPelsHeight)
266         {
267             /* We're only interested in refresh rates for the current resolution and color depth */
268 
269             if (lpMode->dmDisplayFrequency <= 1)
270             {
271                 /* Default hardware frequency */
272                 if (bHasDef)
273                     continue;
274 
275                 bHasDef = TRUE;
276 
277                 if (!LoadString(hInstance,
278                                 IDS_USEDEFFRQUENCY,
279                                 szBuffer,
280                                 sizeof(szBuffer) / sizeof(szBuffer[0])))
281                 {
282                     szBuffer[0] = TEXT('\0');
283                 }
284             }
285             else
286             {
287                 TCHAR szFmt[64];
288 
289                 if (!LoadString(hInstance,
290                                 IDS_FREQFMT,
291                                 szFmt,
292                                 sizeof(szFmt) / sizeof(szFmt[0])))
293                 {
294                     szFmt[0] = TEXT('\0');
295                 }
296 
297                 _sntprintf(szBuffer,
298                            sizeof(szBuffer) / sizeof(szBuffer[0]),
299                            szFmt,
300                            lpMode->dmDisplayFrequency);
301             }
302 
303             i = (INT)SendDlgItemMessage(This->hwndDlg,
304                                         IDC_REFRESHRATE,
305                                         CB_ADDSTRING,
306                                         0,
307                                         (LPARAM)szBuffer);
308             if (i >= 0)
309             {
310                 bAdded = TRUE;
311 
312                 SendDlgItemMessage(This->hwndDlg,
313                                    IDC_REFRESHRATE,
314                                    CB_SETITEMDATA,
315                                    (WPARAM)i,
316                                    (LPARAM)lpMode);
317 
318                 if (lpMode->dmDisplayFrequency == lpCurrentMode->dmDisplayFrequency)
319                 {
320                     SendDlgItemMessage(This->hwndDlg,
321                                        IDC_REFRESHRATE,
322                                        CB_SETCURSEL,
323                                        (WPARAM)i,
324                                        0);
325                 }
326             }
327         }
328 
329     } while (lpMode != NULL);
330 
331     EnableWindow(GetDlgItem(This->hwndDlg,
332                             IDS_MONITORSETTINGSGROUP),
333                  bAdded);
334     EnableWindow(GetDlgItem(This->hwndDlg,
335                             IDS_REFRESHRATELABEL),
336                  bAdded);
337     EnableWindow(GetDlgItem(This->hwndDlg,
338                             IDC_REFRESHRATE),
339                  bAdded);
340 
341     GetPruningSettings(This);
342 }
343 
344 static VOID
InitMonitorDialog(PDESKMONITOR This)345 InitMonitorDialog(PDESKMONITOR This)
346 {
347     PDESKMONINFO pmi, pminext, *pmilink;
348     LPTSTR lpDeviceId;
349     CONFIGRET cr;
350     DISPLAY_DEVICE dd;
351     BOOL bRet;
352     INT i;
353     DWORD dwIndex;
354 
355     /* Free all allocated monitors */
356     pmi = This->Monitors;
357     This->Monitors = NULL;
358     while (pmi != NULL)
359     {
360         pminext = pmi->Next;
361         LocalFree((HLOCAL)pmi);
362         pmi = pminext;
363     }
364 
365     This->SelMonitor = NULL;
366     This->dwMonitorCount = 0;
367 
368     bFoundAdapter = FALSE;
369     lpDeviceId = QueryDeskCplString(This->pdtobj,
370                                     RegisterClipboardFormat(DESK_EXT_DISPLAYID));
371 
372     if (lpDeviceId != NULL && lpDeviceId[0] != TEXT('\0'))
373     {
374         cr = CM_Locate_DevNode(&diAdapter,
375                                lpDeviceId,
376                                CM_LOCATE_DEVNODE_NORMAL);
377         bFoundAdapter = (cr == CR_SUCCESS);
378 
379         if (!bFoundAdapter)
380             DPRINT1("CM_Locate_DevNode failed: %d\n", cr);
381     }
382 
383     if (This->lpDisplayDevice != NULL)
384         LocalFree((HLOCAL)This->lpDisplayDevice);
385 
386     This->lpDisplayDevice = QueryDeskCplString(This->pdtobj,
387                                                RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE));
388 
389     if (This->DeskExtInterface != NULL)
390     {
391         if (This->lpDisplayDevice != NULL)
392         {
393             /* Enumerate all monitors */
394             dwIndex = 0;
395             pmilink = &This->Monitors;
396 
397             do
398             {
399                 dd.cb = sizeof(dd);
400                 bRet = EnumDisplayDevices(This->lpDisplayDevice,
401                                           dwIndex++,
402                                           &dd,
403                                           0);
404                 if (bRet)
405                 {
406                     pmi = LocalAlloc(LMEM_FIXED,
407                                      sizeof(*pmi));
408                     if (pmi != NULL)
409                     {
410                         CopyMemory(&pmi->dd,
411                                    &dd,
412                                    sizeof(dd));
413                         pmi->Next = NULL;
414                         *pmilink = pmi;
415                         pmilink = &pmi->Next;
416 
417                         This->dwMonitorCount++;
418                     }
419                 }
420             } while (bRet);
421         }
422 
423         This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
424     }
425     else
426         This->lpDevModeOnInit = NULL;
427 
428     This->lpSelDevMode = This->lpDevModeOnInit;
429 
430     /* Setup the UI depending on how many monitors are attached */
431     if (This->dwMonitorCount == 0)
432     {
433         LPTSTR lpMonitorName;
434 
435         /* This is a fallback, let's hope that desk.cpl can provide us with a monitor name */
436         lpMonitorName = QueryDeskCplString(This->pdtobj,
437                                            RegisterClipboardFormat(DESK_EXT_MONITORNAME));
438 
439         SetDlgItemText(This->hwndDlg,
440                        IDC_MONITORNAME,
441                        lpMonitorName);
442 
443         if (lpMonitorName != NULL)
444             LocalFree((HLOCAL)lpMonitorName);
445     }
446     else if (This->dwMonitorCount == 1)
447     {
448         This->SelMonitor = This->Monitors;
449         SetDlgItemText(This->hwndDlg,
450                        IDC_MONITORNAME,
451                        This->Monitors->dd.DeviceString);
452     }
453     else
454     {
455         SendDlgItemMessage(This->hwndDlg,
456                            IDC_MONITORLIST,
457                            LB_RESETCONTENT,
458                            0,
459                            0);
460 
461         pmi = This->Monitors;
462         while (pmi != NULL)
463         {
464             i = (INT)SendDlgItemMessage(This->hwndDlg,
465                                         IDC_MONITORLIST,
466                                         LB_ADDSTRING,
467                                         0,
468                                         (LPARAM)pmi->dd.DeviceString);
469             if (i >= 0)
470             {
471                 SendDlgItemMessage(This->hwndDlg,
472                                    IDC_MONITORLIST,
473                                    LB_SETITEMDATA,
474                                    (WPARAM)i,
475                                    (LPARAM)pmi);
476 
477                 if (This->SelMonitor == NULL)
478                 {
479                     SendDlgItemMessage(This->hwndDlg,
480                                        IDC_MONITORLIST,
481                                        LB_SETCURSEL,
482                                        (WPARAM)i,
483                                        0);
484 
485                     This->SelMonitor = pmi;
486                 }
487             }
488 
489             pmi = pmi->Next;
490         }
491     }
492 
493     /* Show/Hide controls */
494     ShowWindow(GetDlgItem(This->hwndDlg,
495                           IDC_MONITORNAME),
496                (This->dwMonitorCount <= 1 ? SW_SHOW : SW_HIDE));
497     ShowWindow(GetDlgItem(This->hwndDlg,
498                           IDC_MONITORLIST),
499                (This->dwMonitorCount > 1 ? SW_SHOW : SW_HIDE));
500 
501     UpdateRefreshFrequencyList(This);
502     UpdateMonitorSelection(This);
503 }
504 
505 static VOID
UpdatePruningSelection(PDESKMONITOR This)506 UpdatePruningSelection(PDESKMONITOR This)
507 {
508     BOOL bPruningOn;
509 
510     if (This->DeskExtInterface != NULL && This->bModesPruned && !This->bKeyIsReadOnly)
511     {
512         bPruningOn = IsDlgButtonChecked(This->hwndDlg,
513                                         IDC_PRUNINGCHECK) != BST_UNCHECKED;
514 
515         if (bPruningOn != This->bPruningOn)
516         {
517             /* Tell desk.cpl to turn on/off pruning mode */
518             This->bPruningOn = bPruningOn;
519             This->DeskExtInterface->SetPruningMode(This->DeskExtInterface->Context,
520                                                    bPruningOn);
521 
522             /* Fill the refresh rate combobox again, we now receive a filtered
523                or unfiltered device mode list from desk.cpl (depending on whether
524                pruning is active or not) */
525             UpdateRefreshFrequencyList(This);
526 
527             (void)PropSheet_Changed(GetParent(This->hwndDlg),
528                                     This->hwndDlg);
529         }
530     }
531 }
532 
533 static VOID
UpdateRefreshRateSelection(PDESKMONITOR This)534 UpdateRefreshRateSelection(PDESKMONITOR This)
535 {
536     PDEVMODEW lpCurrentDevMode;
537     INT i;
538 
539     if (This->DeskExtInterface != NULL)
540     {
541         i = (INT)SendDlgItemMessage(This->hwndDlg,
542                                     IDC_REFRESHRATE,
543                                     CB_GETCURSEL,
544                                     0,
545                                     0);
546         if (i >= 0)
547         {
548             lpCurrentDevMode = This->lpSelDevMode;
549             This->lpSelDevMode = (PDEVMODEW)SendDlgItemMessage(This->hwndDlg,
550                                                                IDC_REFRESHRATE,
551                                                                CB_GETITEMDATA,
552                                                                (WPARAM)i,
553                                                                0);
554 
555             if (This->lpSelDevMode != NULL && This->lpSelDevMode != lpCurrentDevMode)
556             {
557                 This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
558                                                        This->lpSelDevMode);
559 
560                 UpdateRefreshFrequencyList(This);
561 
562                 (void)PropSheet_Changed(GetParent(This->hwndDlg),
563                                         This->hwndDlg);
564             }
565         }
566     }
567 }
568 
569 static LONG
ApplyMonitorChanges(PDESKMONITOR This)570 ApplyMonitorChanges(PDESKMONITOR This)
571 {
572     LONG lChangeRet;
573 
574     if (This->DeskExtInterface != NULL)
575     {
576         /* Change the display settings through desk.cpl */
577         lChangeRet = DeskCplExtDisplaySaveSettings(This->DeskExtInterface,
578                                                    This->hwndDlg);
579         if (lChangeRet == DISP_CHANGE_SUCCESSFUL)
580         {
581             /* Save the new mode */
582             This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
583             This->lpSelDevMode = This->lpDevModeOnInit;
584             return PSNRET_NOERROR;
585         }
586         else if (lChangeRet == DISP_CHANGE_RESTART)
587         {
588             /* Notify desk.cpl that the user needs to reboot */
589             PropSheet_RestartWindows(GetParent(This->hwndDlg));
590             return PSNRET_NOERROR;
591         }
592     }
593 
594     InitMonitorDialog(This);
595 
596     return PSNRET_INVALID_NOCHANGEPAGE;
597 }
598 
599 static VOID
ResetMonitorChanges(PDESKMONITOR This)600 ResetMonitorChanges(PDESKMONITOR This)
601 {
602     if (This->DeskExtInterface != NULL && This->lpDevModeOnInit != NULL)
603     {
604         This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
605                                                This->lpDevModeOnInit);
606     }
607 }
608 
609 static INT_PTR CALLBACK
MonitorDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)610 MonitorDlgProc(HWND hwndDlg,
611                UINT uMsg,
612                WPARAM wParam,
613                LPARAM lParam)
614 {
615     PDESKMONITOR This;
616     INT_PTR Ret = 0;
617 
618     if (uMsg != WM_INITDIALOG)
619     {
620         This = (PDESKMONITOR)GetWindowLongPtr(hwndDlg, DWLP_USER);
621     }
622 
623     switch (uMsg)
624     {
625         case WM_INITDIALOG:
626             This = (PDESKMONITOR)((LPCPROPSHEETPAGE)lParam)->lParam;
627             This->hwndDlg = hwndDlg;
628             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)This);
629 
630             InitMonitorDialog(This);
631             Ret = TRUE;
632             break;
633 
634         case WM_COMMAND:
635             switch (LOWORD(wParam))
636             {
637                 case IDC_MONITORPROPERTIES:
638                     ShowMonitorProperties(This);
639                     break;
640 
641                 case IDC_MONITORLIST:
642                     if (HIWORD(wParam) == LBN_SELCHANGE)
643                         UpdateMonitorSelection(This);
644                     break;
645 
646                 case IDC_PRUNINGCHECK:
647                     if (HIWORD(wParam) == BN_CLICKED)
648                         UpdatePruningSelection(This);
649                     break;
650 
651                 case IDC_REFRESHRATE:
652                     if (HIWORD(wParam) == CBN_SELCHANGE)
653                         UpdateRefreshRateSelection(This);
654                     break;
655             }
656             break;
657 
658         case WM_NOTIFY:
659         {
660             NMHDR *nmh = (NMHDR *)lParam;
661 
662             switch (nmh->code)
663             {
664                 case PSN_APPLY:
665                 {
666                     SetWindowLongPtr(hwndDlg,
667                                      DWLP_MSGRESULT,
668                                      ApplyMonitorChanges(This));
669                     Ret = TRUE;
670                     break;
671                 }
672 
673                 case PSN_RESET:
674                     ResetMonitorChanges(This);
675                     break;
676 
677                 case PSN_SETACTIVE:
678                     UpdateRefreshFrequencyList(This);
679                     break;
680             }
681             break;
682         }
683     }
684 
685     return Ret;
686 }
687 
688 static VOID
IDeskMonitor_Destroy(PDESKMONITOR This)689 IDeskMonitor_Destroy(PDESKMONITOR This)
690 {
691     PDESKMONINFO pmi, pminext;
692 
693     if (This->pdtobj != NULL)
694     {
695         IDataObject_Release(This->pdtobj);
696         This->pdtobj = NULL;
697     }
698 
699     if (This->DeskExtInterface != NULL)
700     {
701         LocalFree((HLOCAL)This->DeskExtInterface);
702         This->DeskExtInterface = NULL;
703     }
704 
705     if (This->lpDisplayDevice != NULL)
706     {
707         LocalFree((HLOCAL)This->lpDisplayDevice);
708         This->lpDisplayDevice = NULL;
709     }
710 
711     /* Free all monitors */
712     pmi = This->Monitors;
713     This->Monitors = NULL;
714     while (pmi != NULL)
715     {
716         pminext = pmi->Next;
717         LocalFree((HLOCAL)pmi);
718         pmi = pminext;
719     }
720 }
721 
722 ULONG
IDeskMonitor_AddRef(PDESKMONITOR This)723 IDeskMonitor_AddRef(PDESKMONITOR This)
724 {
725     ULONG ret;
726 
727     ret = InterlockedIncrement((PLONG)&This->ref);
728     if (ret == 1)
729         InterlockedIncrement(&dll_refs);
730 
731     return ret;
732 }
733 
734 ULONG
IDeskMonitor_Release(PDESKMONITOR This)735 IDeskMonitor_Release(PDESKMONITOR This)
736 {
737     ULONG ret;
738 
739     ret = InterlockedDecrement((PLONG)&This->ref);
740     if (ret == 0)
741     {
742         IDeskMonitor_Destroy(This);
743         InterlockedDecrement(&dll_refs);
744 
745         HeapFree(GetProcessHeap(),
746                  0,
747                  This);
748     }
749 
750     return ret;
751 }
752 
753 HRESULT STDMETHODCALLTYPE
IDeskMonitor_QueryInterface(PDESKMONITOR This,REFIID iid,PVOID * pvObject)754 IDeskMonitor_QueryInterface(PDESKMONITOR This,
755                             REFIID iid,
756                             PVOID *pvObject)
757 {
758     *pvObject = NULL;
759 
760     if (IsEqualIID(iid,
761                    &IID_IShellPropSheetExt) ||
762         IsEqualIID(iid,
763                    &IID_IUnknown))
764     {
765         *pvObject = impl_to_interface(This, IShellPropSheetExt);
766     }
767     else if (IsEqualIID(iid,
768                         &IID_IShellExtInit))
769     {
770         *pvObject = impl_to_interface(This, IShellExtInit);
771     }
772     else if (IsEqualIID(iid,
773                         &IID_IClassFactory))
774     {
775         *pvObject = impl_to_interface(This, IClassFactory);
776     }
777     else
778     {
779         DPRINT1("IDeskMonitor::QueryInterface(%p,%p): E_NOINTERFACE\n", iid, pvObject);
780         return E_NOINTERFACE;
781     }
782 
783     IDeskMonitor_AddRef(This);
784     return S_OK;
785 }
786 
787 HRESULT
IDeskMonitor_Initialize(PDESKMONITOR This,LPCITEMIDLIST pidlFolder,IDataObject * pdtobj,HKEY hkeyProgID)788 IDeskMonitor_Initialize(PDESKMONITOR This,
789                         LPCITEMIDLIST pidlFolder,
790                         IDataObject *pdtobj,
791                         HKEY hkeyProgID)
792 {
793     DPRINT1("IDeskMonitor::Initialize(%p,%p,%p)\n", pidlFolder, pdtobj, hkeyProgID);
794 
795     if (pdtobj != NULL)
796     {
797         IDataObject_AddRef(pdtobj);
798         This->pdtobj = pdtobj;
799 
800         /* Get a copy of the desk.cpl extension interface */
801         This->DeskExtInterface = QueryDeskCplExtInterface(This->pdtobj);
802         if (This->DeskExtInterface != NULL)
803             return S_OK;
804     }
805 
806     return S_FALSE;
807 }
808 
809 HRESULT
IDeskMonitor_AddPages(PDESKMONITOR This,LPFNADDPROPSHEETPAGE pfnAddPage,LPARAM lParam)810 IDeskMonitor_AddPages(PDESKMONITOR This,
811                       LPFNADDPROPSHEETPAGE pfnAddPage,
812                       LPARAM lParam)
813 {
814     HPROPSHEETPAGE hpsp;
815     PROPSHEETPAGE psp;
816 
817     DPRINT1("IDeskMonitor::AddPages(%p,%p)\n", pfnAddPage, lParam);
818 
819     psp.dwSize = sizeof(psp);
820     psp.dwFlags = PSP_DEFAULT;
821     psp.hInstance = hInstance;
822     psp.pszTemplate = MAKEINTRESOURCE(IDD_MONITOR);
823     psp.pfnDlgProc = MonitorDlgProc;
824     psp.lParam = (LPARAM)This;
825 
826     hpsp = CreatePropertySheetPage(&psp);
827     if (hpsp != NULL && pfnAddPage(hpsp, lParam))
828         return S_OK;
829 
830     return S_FALSE;
831 }
832 
833 HRESULT
IDeskMonitor_ReplacePage(PDESKMONITOR This,EXPPS uPageID,LPFNADDPROPSHEETPAGE pfnReplacePage,LPARAM lParam)834 IDeskMonitor_ReplacePage(PDESKMONITOR This,
835                          EXPPS uPageID,
836                          LPFNADDPROPSHEETPAGE pfnReplacePage,
837                          LPARAM lParam)
838 {
839     DPRINT1("IDeskMonitor::ReplacePage(%u,%p,%p)\n", uPageID, pfnReplacePage, lParam);
840     return E_NOTIMPL;
841 }
842 
843 HRESULT
IDeskMonitor_Constructor(REFIID riid,LPVOID * ppv)844 IDeskMonitor_Constructor(REFIID riid,
845                          LPVOID *ppv)
846 {
847     PDESKMONITOR This;
848     HRESULT hRet = E_OUTOFMEMORY;
849 
850     DPRINT1("IDeskMonitor::Constructor(%p,%p)\n", riid, ppv);
851 
852     This = HeapAlloc(GetProcessHeap(),
853                      0,
854                      sizeof(*This));
855     if (This != NULL)
856     {
857         ZeroMemory(This,
858                    sizeof(*This));
859 
860         IDeskMonitor_InitIface(This);
861 
862         hRet = IDeskMonitor_QueryInterface(This,
863                                            riid,
864                                            ppv);
865         if (!SUCCEEDED(hRet))
866             IDeskMonitor_Release(This);
867     }
868 
869     return hRet;
870 }
871 
872 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD dwReason,LPVOID lpvReserved)873 DllMain(HINSTANCE hinstDLL,
874         DWORD dwReason,
875         LPVOID lpvReserved)
876 {
877     switch (dwReason)
878     {
879         case DLL_PROCESS_ATTACH:
880             hInstance = hinstDLL;
881             DisableThreadLibraryCalls(hInstance);
882             break;
883     }
884 
885     return TRUE;
886 }
887