1 /*
2  * Provides default drive shell extension
3  *
4  * Copyright 2005 Johannes Anderwald
5  * Copyright 2012 Rafal Harabien
6  * Copyright 2020 Katayama Hirofumi MZ
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include "precomp.h"
24 
25 #define _USE_MATH_DEFINES
26 #include <math.h>
27 #include <devguid.h>
28 
29 #define NTOS_MODE_USER
30 #include <ndk/iofuncs.h>
31 #include <ndk/obfuncs.h>
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(shell);
34 
35 typedef enum
36 {
37     HWPD_STANDARDLIST = 0,
38     HWPD_LARGELIST,
39     HWPD_MAX = HWPD_LARGELIST
40 } HWPAGE_DISPLAYMODE, *PHWPAGE_DISPLAYMODE;
41 
42 EXTERN_C HWND WINAPI
43 DeviceCreateHardwarePageEx(HWND hWndParent,
44                            LPGUID lpGuids,
45                            UINT uNumberOfGuids,
46                            HWPAGE_DISPLAYMODE DisplayMode);
47 UINT SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax);
48 
49 static VOID
50 GetDriveNameWithLetter(LPWSTR pwszText, UINT cchTextMax, LPCWSTR pwszDrive)
51 {
52     DWORD dwMaxComp, dwFileSys;
53     SIZE_T cchText = 0;
54 
55     if (GetVolumeInformationW(pwszDrive, pwszText, cchTextMax, NULL, &dwMaxComp, &dwFileSys, NULL, 0))
56     {
57         cchText = wcslen(pwszText);
58         if (cchText == 0)
59         {
60             /* load default volume label */
61             cchText = LoadStringW(shell32_hInstance, IDS_DRIVE_FIXED, pwszText, cchTextMax);
62         }
63     }
64 
65     StringCchPrintfW(pwszText + cchText, cchTextMax - cchText, L" (%c:)", pwszDrive[0]);
66 }
67 
68 static VOID
69 InitializeChkDskDialog(HWND hwndDlg, LPCWSTR pwszDrive)
70 {
71     WCHAR wszText[100];
72     UINT Length;
73     SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pwszDrive);
74 
75     Length = GetWindowTextW(hwndDlg, wszText, sizeof(wszText) / sizeof(WCHAR));
76     wszText[Length] = L' ';
77     GetDriveNameWithLetter(&wszText[Length + 1], (sizeof(wszText) / sizeof(WCHAR)) - Length - 1, pwszDrive);
78     SetWindowText(hwndDlg, wszText);
79 }
80 
81 static HWND hChkdskDrvDialog = NULL;
82 static BOOLEAN bChkdskSuccess = FALSE;
83 
84 static BOOLEAN NTAPI
85 ChkdskCallback(
86     IN CALLBACKCOMMAND Command,
87     IN ULONG SubAction,
88     IN PVOID ActionInfo)
89 {
90     PDWORD Progress;
91     PBOOLEAN pSuccess;
92     switch(Command)
93     {
94         case PROGRESS:
95             Progress = (PDWORD)ActionInfo;
96             SendDlgItemMessageW(hChkdskDrvDialog, 14002, PBM_SETPOS, (WPARAM)*Progress, 0);
97             break;
98         case DONE:
99             pSuccess = (PBOOLEAN)ActionInfo;
100             bChkdskSuccess = (*pSuccess);
101             break;
102 
103         case VOLUMEINUSE:
104         case INSUFFICIENTRIGHTS:
105         case FSNOTSUPPORTED:
106         case CLUSTERSIZETOOSMALL:
107             bChkdskSuccess = FALSE;
108             FIXME("\n");
109             break;
110 
111         default:
112             break;
113     }
114 
115     return TRUE;
116 }
117 
118 static VOID
119 ChkDskNow(HWND hwndDlg, LPCWSTR pwszDrive)
120 {
121     //DWORD ClusterSize = 0;
122     WCHAR wszFs[30];
123     ULARGE_INTEGER TotalNumberOfFreeBytes, FreeBytesAvailableUser;
124     BOOLEAN bCorrectErrors = FALSE, bScanDrive = FALSE;
125 
126     if(!GetVolumeInformationW(pwszDrive, NULL, 0, NULL, NULL, NULL, wszFs, _countof(wszFs)))
127     {
128         FIXME("failed to get drive fs type\n");
129         return;
130     }
131 
132     if (!GetDiskFreeSpaceExW(pwszDrive, &FreeBytesAvailableUser, &TotalNumberOfFreeBytes, NULL))
133     {
134         FIXME("failed to get drive space type\n");
135         return;
136     }
137 
138     /*if (!GetDefaultClusterSize(wszFs, &ClusterSize, &TotalNumberOfFreeBytes))
139     {
140         FIXME("invalid cluster size\n");
141         return;
142     }*/
143 
144     if (SendDlgItemMessageW(hwndDlg, 14000, BM_GETCHECK, 0, 0) == BST_CHECKED)
145         bCorrectErrors = TRUE;
146 
147     if (SendDlgItemMessageW(hwndDlg, 14001, BM_GETCHECK, 0, 0) == BST_CHECKED)
148         bScanDrive = TRUE;
149 
150     hChkdskDrvDialog = hwndDlg;
151     bChkdskSuccess = FALSE;
152     SendDlgItemMessageW(hwndDlg, 14002, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
153     Chkdsk((LPWSTR)pwszDrive, (LPWSTR)wszFs, bCorrectErrors, TRUE, FALSE, bScanDrive, NULL, NULL, ChkdskCallback); // FIXME: casts
154 
155     hChkdskDrvDialog = NULL;
156     bChkdskSuccess = FALSE;
157 }
158 
159 static INT_PTR CALLBACK
160 ChkDskDlg(
161     HWND hwndDlg,
162     UINT uMsg,
163     WPARAM wParam,
164     LPARAM lParam)
165 {
166     switch(uMsg)
167     {
168         case WM_INITDIALOG:
169             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
170             InitializeChkDskDialog(hwndDlg, (LPCWSTR)lParam);
171             return TRUE;
172         case WM_COMMAND:
173             switch(LOWORD(wParam))
174             {
175                 case IDCANCEL:
176                     EndDialog(hwndDlg, 0);
177                     break;
178                 case IDOK:
179                 {
180                     LPCWSTR pwszDrive = (LPCWSTR)GetWindowLongPtr(hwndDlg, DWLP_USER);
181                     ChkDskNow(hwndDlg, pwszDrive);
182                     break;
183                 }
184             }
185             break;
186     }
187 
188     return FALSE;
189 }
190 
191 VOID
192 CDrvDefExt::PaintStaticControls(HWND hwndDlg, LPDRAWITEMSTRUCT pDrawItem)
193 {
194     HBRUSH hBrush;
195 
196     if (pDrawItem->CtlID == 14013)
197     {
198         hBrush = CreateSolidBrush(RGB(0, 0, 255));
199         if (hBrush)
200         {
201             FillRect(pDrawItem->hDC, &pDrawItem->rcItem, hBrush);
202             DeleteObject((HGDIOBJ)hBrush);
203         }
204     }
205     else if (pDrawItem->CtlID == 14014)
206     {
207         hBrush = CreateSolidBrush(RGB(255, 0, 255));
208         if (hBrush)
209         {
210             FillRect(pDrawItem->hDC, &pDrawItem->rcItem, hBrush);
211             DeleteObject((HGDIOBJ)hBrush);
212         }
213     }
214     else if (pDrawItem->CtlID == 14015)
215     {
216         HBRUSH hBlueBrush = CreateSolidBrush(RGB(0, 0, 255));
217         HBRUSH hMagBrush = CreateSolidBrush(RGB(255, 0, 255));
218         HBRUSH hbrOld;
219         HPEN hDarkBluePen = CreatePen(PS_SOLID, 1, RGB(0, 0, 128));
220         HPEN hDarkMagPen = CreatePen(PS_SOLID, 1, RGB(128, 0, 128));
221         HPEN hOldPen = (HPEN)SelectObject(pDrawItem->hDC, hDarkMagPen);
222         INT xCenter = (pDrawItem->rcItem.left + pDrawItem->rcItem.right) / 2;
223         INT yCenter = (pDrawItem->rcItem.top + pDrawItem->rcItem.bottom - 10) / 2;
224         INT cx = pDrawItem->rcItem.right - pDrawItem->rcItem.left;
225         INT cy = pDrawItem->rcItem.bottom - pDrawItem->rcItem.top - 10;
226         INT xRadial = xCenter + (INT)(cos(M_PI + m_FreeSpacePerc / 100.0f * M_PI * 2.0f) * cx / 2);
227         INT yRadial = yCenter - (INT)(sin(M_PI + m_FreeSpacePerc / 100.0f * M_PI * 2.0f) * cy / 2);
228 
229         TRACE("FreeSpace %u a %f cx %d\n", m_FreeSpacePerc, M_PI+m_FreeSpacePerc / 100.0f * M_PI * 2.0f, cx);
230 
231         for (INT x = pDrawItem->rcItem.left; x < pDrawItem->rcItem.right; ++x)
232         {
233             double cos_val = (x - xCenter) * 2.0f / cx;
234             INT y = yCenter + (INT)(sin(acos(cos_val)) * cy / 2) - 1;
235 
236             if (m_FreeSpacePerc < 50 && x == xRadial)
237                 SelectObject(pDrawItem->hDC, hDarkBluePen);
238 
239             MoveToEx(pDrawItem->hDC, x, y, NULL);
240             LineTo(pDrawItem->hDC, x, y + 10);
241         }
242 
243         SelectObject(pDrawItem->hDC, hOldPen);
244 
245         if (m_FreeSpacePerc > 50)
246         {
247             hbrOld = (HBRUSH)SelectObject(pDrawItem->hDC, hMagBrush);
248 
249             Ellipse(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top,
250                     pDrawItem->rcItem.right, pDrawItem->rcItem.bottom - 10);
251 
252             SelectObject(pDrawItem->hDC, hBlueBrush);
253 
254             if (m_FreeSpacePerc < 100)
255             {
256                 Pie(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, pDrawItem->rcItem.right,
257                     pDrawItem->rcItem.bottom - 10, xRadial, yRadial, pDrawItem->rcItem.left, yCenter);
258             }
259         }
260         else
261         {
262             hbrOld = (HBRUSH)SelectObject(pDrawItem->hDC, hBlueBrush);
263 
264             Ellipse(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top,
265                     pDrawItem->rcItem.right, pDrawItem->rcItem.bottom - 10);
266 
267             SelectObject(pDrawItem->hDC, hMagBrush);
268 
269             if (m_FreeSpacePerc > 0)
270             {
271                 Pie(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, pDrawItem->rcItem.right,
272                     pDrawItem->rcItem.bottom - 10, pDrawItem->rcItem.left, yCenter, xRadial, yRadial);
273             }
274         }
275 
276         SelectObject(pDrawItem->hDC, hbrOld);
277 
278         DeleteObject(hBlueBrush);
279         DeleteObject(hMagBrush);
280         DeleteObject(hDarkBluePen);
281         DeleteObject(hDarkMagPen);
282     }
283 }
284 
285 // https://stackoverflow.com/questions/3098696/get-information-about-disk-drives-result-on-windows7-32-bit-system/3100268#3100268
286 static BOOL
287 GetDriveTypeAndCharacteristics(HANDLE hDevice, DEVICE_TYPE *pDeviceType, ULONG *pCharacteristics)
288 {
289     NTSTATUS Status;
290     IO_STATUS_BLOCK IoStatusBlock;
291     FILE_FS_DEVICE_INFORMATION DeviceInfo;
292 
293     Status = NtQueryVolumeInformationFile(hDevice, &IoStatusBlock,
294                                           &DeviceInfo, sizeof(DeviceInfo),
295                                           FileFsDeviceInformation);
296     if (Status == NO_ERROR)
297     {
298         *pDeviceType = DeviceInfo.DeviceType;
299         *pCharacteristics = DeviceInfo.Characteristics;
300         return TRUE;
301     }
302 
303     return FALSE;
304 }
305 
306 BOOL IsDriveFloppyW(LPCWSTR pszDriveRoot)
307 {
308     LPCWSTR RootPath = pszDriveRoot;
309     WCHAR szRoot[16], szDeviceName[16];
310     UINT uType;
311     HANDLE hDevice;
312     DEVICE_TYPE DeviceType;
313     ULONG ulCharacteristics;
314     BOOL ret;
315 
316     lstrcpynW(szRoot, RootPath, _countof(szRoot));
317 
318     if (L'a' <= szRoot[0] && szRoot[0] <= 'z')
319     {
320         szRoot[0] += ('A' - 'a');
321     }
322 
323     if ('A' <= szRoot[0] && szRoot[0] <= L'Z' &&
324         szRoot[1] == L':' && szRoot[2] == 0)
325     {
326         // 'C:' --> 'C:\'
327         szRoot[2] = L'\\';
328         szRoot[3] = 0;
329     }
330 
331     if (!PathIsRootW(szRoot))
332     {
333         return FALSE;
334     }
335 
336     uType = GetDriveTypeW(szRoot);
337     if (uType == DRIVE_REMOVABLE)
338     {
339         if (szRoot[0] == L'A' || szRoot[0] == L'B')
340             return TRUE;
341     }
342     else
343     {
344         return FALSE;
345     }
346 
347     lstrcpynW(szDeviceName, L"\\\\.\\", _countof(szDeviceName));
348     szDeviceName[4] = szRoot[0];
349     szDeviceName[5] = L':';
350     szDeviceName[6] = UNICODE_NULL;
351 
352     hDevice = CreateFileW(szDeviceName, FILE_READ_ATTRIBUTES,
353                           FILE_SHARE_READ | FILE_SHARE_WRITE,
354                           NULL, OPEN_EXISTING, 0, NULL);
355     if (hDevice == INVALID_HANDLE_VALUE)
356     {
357         return FALSE;
358     }
359 
360     ret = FALSE;
361     if (GetDriveTypeAndCharacteristics(hDevice, &DeviceType, &ulCharacteristics))
362     {
363         if ((ulCharacteristics & FILE_FLOPPY_DISKETTE) == FILE_FLOPPY_DISKETTE)
364             ret = TRUE;
365     }
366 
367     CloseHandle(hDevice);
368 
369     return ret;
370 }
371 
372 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot)
373 {
374     WCHAR szRoot[8];
375     MultiByteToWideChar(CP_ACP, 0, pszDriveRoot, -1, szRoot, _countof(szRoot));
376     return IsDriveFloppyW(szRoot);
377 }
378 
379 VOID
380 CDrvDefExt::InitGeneralPage(HWND hwndDlg)
381 {
382     WCHAR wszVolumeName[MAX_PATH+1] = {0};
383     WCHAR wszFileSystem[MAX_PATH+1] = {0};
384     WCHAR wszBuf[128];
385     BOOL bRet;
386 
387     bRet = GetVolumeInformationW(m_wszDrive, wszVolumeName, _countof(wszVolumeName), NULL, NULL, NULL, wszFileSystem, _countof(wszFileSystem));
388     if (bRet)
389     {
390         /* Set volume label and filesystem */
391         SetDlgItemTextW(hwndDlg, 14000, wszVolumeName);
392         SetDlgItemTextW(hwndDlg, 14002, wszFileSystem);
393     }
394     else
395     {
396         LoadStringW(shell32_hInstance, IDS_FS_UNKNOWN, wszFileSystem, _countof(wszFileSystem));
397         SetDlgItemTextW(hwndDlg, 14002, wszFileSystem);
398     }
399 
400     /* Set drive type and icon */
401     UINT DriveType = GetDriveTypeW(m_wszDrive);
402     UINT IconId, TypeStrId = 0;
403     switch (DriveType)
404     {
405         case DRIVE_REMOVABLE:
406             if (IsDriveFloppyW(m_wszDrive))
407                 IconId = IDI_SHELL_3_14_FLOPPY;
408             else
409                 IconId = IDI_SHELL_REMOVEABLE;
410             break;
411         case DRIVE_CDROM: IconId = IDI_SHELL_CDROM; TypeStrId = IDS_DRIVE_CDROM; break;
412         case DRIVE_REMOTE: IconId = IDI_SHELL_NETDRIVE; TypeStrId = IDS_DRIVE_NETWORK; break;
413         case DRIVE_RAMDISK: IconId = IDI_SHELL_RAMDISK; break;
414         default: IconId = IDI_SHELL_DRIVE; TypeStrId = IDS_DRIVE_FIXED;
415     }
416 
417     if (DriveType == DRIVE_CDROM || DriveType == DRIVE_REMOTE)
418     {
419         /* volume label textbox */
420         SendMessage(GetDlgItem(hwndDlg, 14000), EM_SETREADONLY, TRUE, 0);
421 
422         /* disk compression */
423         ShowWindow(GetDlgItem(hwndDlg, 14011), FALSE);
424 
425         /* index */
426         ShowWindow(GetDlgItem(hwndDlg, 14012), FALSE);
427     }
428 
429     HICON hIcon = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IconId), IMAGE_ICON, 32, 32, LR_SHARED);
430     if (hIcon)
431         SendDlgItemMessageW(hwndDlg, 14016, STM_SETICON, (WPARAM)hIcon, 0);
432     if (TypeStrId && LoadStringW(shell32_hInstance, TypeStrId, wszBuf, _countof(wszBuf)))
433         SetDlgItemTextW(hwndDlg, 14001, wszBuf);
434 
435     ULARGE_INTEGER FreeBytesAvailable, TotalNumberOfBytes;
436     if(GetDiskFreeSpaceExW(m_wszDrive, &FreeBytesAvailable, &TotalNumberOfBytes, NULL))
437     {
438         /* Init free space percentage used for drawing piechart */
439         m_FreeSpacePerc = (UINT)(FreeBytesAvailable.QuadPart * 100ull / TotalNumberOfBytes.QuadPart);
440 
441         /* Used space */
442         if (SH_FormatByteSize(TotalNumberOfBytes.QuadPart - FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
443             SetDlgItemTextW(hwndDlg, 14003, wszBuf);
444 
445         if (StrFormatByteSizeW(TotalNumberOfBytes.QuadPart - FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
446             SetDlgItemTextW(hwndDlg, 14004, wszBuf);
447 
448         /* Free space */
449         if (SH_FormatByteSize(FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
450             SetDlgItemTextW(hwndDlg, 14005, wszBuf);
451 
452         if (StrFormatByteSizeW(FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
453             SetDlgItemTextW(hwndDlg, 14006, wszBuf);
454 
455         /* Total space */
456         if (SH_FormatByteSize(TotalNumberOfBytes.QuadPart, wszBuf, _countof(wszBuf)))
457             SetDlgItemTextW(hwndDlg, 14007, wszBuf);
458 
459         if (StrFormatByteSizeW(TotalNumberOfBytes.QuadPart, wszBuf, _countof(wszBuf)))
460             SetDlgItemTextW(hwndDlg, 14008, wszBuf);
461     }
462     else
463     {
464         m_FreeSpacePerc = 0;
465 
466         if (SH_FormatByteSize(0, wszBuf, _countof(wszBuf)))
467         {
468             SetDlgItemTextW(hwndDlg, 14003, wszBuf);
469             SetDlgItemTextW(hwndDlg, 14005, wszBuf);
470             SetDlgItemTextW(hwndDlg, 14007, wszBuf);
471         }
472         if (StrFormatByteSizeW(0, wszBuf, _countof(wszBuf)))
473         {
474             SetDlgItemTextW(hwndDlg, 14004, wszBuf);
475             SetDlgItemTextW(hwndDlg, 14006, wszBuf);
476             SetDlgItemTextW(hwndDlg, 14008, wszBuf);
477         }
478     }
479 
480     /* Set drive description */
481     WCHAR wszFormat[50];
482     GetDlgItemTextW(hwndDlg, 14009, wszFormat, _countof(wszFormat));
483     swprintf(wszBuf, wszFormat, m_wszDrive[0]);
484     SetDlgItemTextW(hwndDlg, 14009, wszBuf);
485 
486     /* show disk cleanup button only for fixed drives */
487     ShowWindow(GetDlgItem(hwndDlg, 14010), DriveType == DRIVE_FIXED);
488 }
489 
490 INT_PTR CALLBACK
491 CDrvDefExt::GeneralPageProc(
492     HWND hwndDlg,
493     UINT uMsg,
494     WPARAM wParam,
495     LPARAM lParam)
496 {
497     switch(uMsg)
498     {
499         case WM_INITDIALOG:
500         {
501             LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
502             if (ppsp == NULL)
503                 break;
504 
505             CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(ppsp->lParam);
506             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pDrvDefExt);
507             pDrvDefExt->InitGeneralPage(hwndDlg);
508             return TRUE;
509         }
510         case WM_DRAWITEM:
511         {
512             LPDRAWITEMSTRUCT pDrawItem = (LPDRAWITEMSTRUCT)lParam;
513 
514             if (pDrawItem->CtlID >= 14013 && pDrawItem->CtlID <= 14015)
515             {
516                 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
517                 pDrvDefExt->PaintStaticControls(hwndDlg, pDrawItem);
518                 return TRUE;
519             }
520             break;
521         }
522         case WM_PAINT:
523             break;
524         case WM_COMMAND:
525             if (LOWORD(wParam) == 14010) /* Disk Cleanup */
526             {
527                 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
528                 WCHAR wszBuf[256];
529                 DWORD cbBuf = sizeof(wszBuf);
530 
531                 if (RegGetValueW(HKEY_LOCAL_MACHINE,
532                                  L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\CleanupPath",
533                                  NULL,
534                                  RRF_RT_REG_SZ,
535                                  NULL,
536                                  (PVOID)wszBuf,
537                                  &cbBuf) == ERROR_SUCCESS)
538                 {
539                     WCHAR wszCmd[MAX_PATH];
540 
541                     StringCbPrintfW(wszCmd, sizeof(wszCmd), wszBuf, pDrvDefExt->m_wszDrive[0]);
542 
543                     if (ShellExecuteW(hwndDlg, NULL, wszCmd, NULL, NULL, SW_SHOW) <= (HINSTANCE)32)
544                         ERR("Failed to create cleanup process %ls\n", wszCmd);
545                 }
546             }
547             else if (LOWORD(wParam) == 14000) /* Label */
548             {
549                 if (HIWORD(wParam) == EN_CHANGE)
550                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
551             }
552             break;
553         case WM_NOTIFY:
554             if (((LPNMHDR)lParam)->hwndFrom == GetParent(hwndDlg))
555             {
556                 /* Property Sheet */
557                 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
558 
559                 if (lppsn->hdr.code == PSN_APPLY)
560                 {
561                     CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
562                     WCHAR wszBuf[256];
563 
564                     if (GetDlgItemTextW(hwndDlg, 14000, wszBuf, _countof(wszBuf)))
565                         SetVolumeLabelW(pDrvDefExt->m_wszDrive, wszBuf);
566                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
567                     return TRUE;
568                 }
569             }
570             break;
571 
572         default:
573             break;
574     }
575 
576     return FALSE;
577 }
578 
579 INT_PTR CALLBACK
580 CDrvDefExt::ExtraPageProc(
581     HWND hwndDlg,
582     UINT uMsg,
583     WPARAM wParam,
584     LPARAM lParam)
585 {
586     switch (uMsg)
587     {
588         case WM_INITDIALOG:
589         {
590             LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
591             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)ppsp->lParam);
592             return TRUE;
593         }
594         case WM_COMMAND:
595         {
596             WCHAR wszBuf[MAX_PATH];
597             DWORD cbBuf = sizeof(wszBuf);
598             CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
599 
600             switch(LOWORD(wParam))
601             {
602                 case 14000:
603                     DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_CHECK_DISK), hwndDlg, ChkDskDlg, (LPARAM)pDrvDefExt->m_wszDrive);
604                     break;
605                 case 14001:
606                     if (RegGetValueW(HKEY_LOCAL_MACHINE,
607                                      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\DefragPath",
608                                      NULL,
609                                      RRF_RT_REG_SZ,
610                                      NULL,
611                                      (PVOID)wszBuf,
612                                      &cbBuf) == ERROR_SUCCESS)
613                     {
614                         WCHAR wszCmd[MAX_PATH];
615 
616                         StringCbPrintfW(wszCmd, sizeof(wszCmd), wszBuf, pDrvDefExt->m_wszDrive[0]);
617 
618                         if (ShellExecuteW(hwndDlg, NULL, wszCmd, NULL, NULL, SW_SHOW) <= (HINSTANCE)32)
619                             ERR("Failed to create defrag process %ls\n", wszCmd);
620                     }
621                     break;
622                 case 14002:
623                     if (RegGetValueW(HKEY_LOCAL_MACHINE,
624                                      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\BackupPath",
625                                      NULL,
626                                      RRF_RT_REG_SZ,
627                                      NULL,
628                                      (PVOID)wszBuf,
629                                      &cbBuf) == ERROR_SUCCESS)
630                     {
631                         if (ShellExecuteW(hwndDlg, NULL, wszBuf, NULL, NULL, SW_SHOW) <= (HINSTANCE)32)
632                             ERR("Failed to create backup process %ls\n", wszBuf);
633                     }
634             }
635             break;
636         }
637     }
638     return FALSE;
639 }
640 
641 INT_PTR CALLBACK
642 CDrvDefExt::HardwarePageProc(
643     HWND hwndDlg,
644     UINT uMsg,
645     WPARAM wParam,
646     LPARAM lParam)
647 {
648     UNREFERENCED_PARAMETER(lParam);
649     UNREFERENCED_PARAMETER(wParam);
650 
651     switch(uMsg)
652     {
653         case WM_INITDIALOG:
654         {
655             GUID Guids[2];
656             Guids[0] = GUID_DEVCLASS_DISKDRIVE;
657             Guids[1] = GUID_DEVCLASS_CDROM;
658 
659             /* create the hardware page */
660             DeviceCreateHardwarePageEx(hwndDlg, Guids, _countof(Guids), HWPD_STANDARDLIST);
661             break;
662         }
663     }
664 
665     return FALSE;
666 }
667 
668 CDrvDefExt::CDrvDefExt()
669 {
670     m_wszDrive[0] = L'\0';
671 }
672 
673 CDrvDefExt::~CDrvDefExt()
674 {
675 
676 }
677 
678 HRESULT WINAPI
679 CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
680 {
681     FORMATETC format;
682     STGMEDIUM stgm;
683     HRESULT hr;
684 
685     TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
686 
687     if (!pDataObj)
688         return E_FAIL;
689 
690     format.cfFormat = CF_HDROP;
691     format.ptd = NULL;
692     format.dwAspect = DVASPECT_CONTENT;
693     format.lindex = -1;
694     format.tymed = TYMED_HGLOBAL;
695 
696     hr = pDataObj->GetData(&format, &stgm);
697     if (FAILED(hr))
698         return hr;
699 
700     if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszDrive, _countof(m_wszDrive)))
701     {
702         ERR("DragQueryFileW failed\n");
703         ReleaseStgMedium(&stgm);
704         return E_FAIL;
705     }
706 
707     ReleaseStgMedium(&stgm);
708     TRACE("Drive properties %ls\n", m_wszDrive);
709 
710     return S_OK;
711 }
712 
713 HRESULT WINAPI
714 CDrvDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
715 {
716     UNIMPLEMENTED;
717     return E_NOTIMPL;
718 }
719 
720 HRESULT WINAPI
721 CDrvDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
722 {
723     UNIMPLEMENTED;
724     return E_NOTIMPL;
725 }
726 
727 HRESULT WINAPI
728 CDrvDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
729 {
730     UNIMPLEMENTED;
731     return E_NOTIMPL;
732 }
733 
734 HRESULT WINAPI
735 CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
736 {
737     HPROPSHEETPAGE hPage;
738 
739     hPage = SH_CreatePropertySheetPage(IDD_DRIVE_PROPERTIES,
740                                        GeneralPageProc,
741                                        (LPARAM)this,
742                                        NULL);
743     if (hPage)
744         pfnAddPage(hPage, lParam);
745 
746     if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
747     {
748         hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
749                                            ExtraPageProc,
750                                            (LPARAM)this,
751                                            NULL);
752         if (hPage)
753             pfnAddPage(hPage, lParam);
754     }
755 
756     if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
757     {
758         hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
759                                            HardwarePageProc,
760                                            (LPARAM)this,
761                                            NULL);
762         if (hPage)
763             pfnAddPage(hPage, lParam);
764     }
765 
766     return S_OK;
767 }
768 
769 HRESULT WINAPI
770 CDrvDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
771 {
772     UNIMPLEMENTED;
773     return E_NOTIMPL;
774 }
775 
776 HRESULT WINAPI
777 CDrvDefExt::SetSite(IUnknown *punk)
778 {
779     UNIMPLEMENTED;
780     return E_NOTIMPL;
781 }
782 
783 HRESULT WINAPI
784 CDrvDefExt::GetSite(REFIID iid, void **ppvSite)
785 {
786     UNIMPLEMENTED;
787     return E_NOTIMPL;
788 }
789