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