Reactos
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
33WINE_DEFAULT_DEBUG_CHANNEL(shell);
34
35typedef enum
36{
37 HWPD_STANDARDLIST = 0,
38 HWPD_LARGELIST,
39 HWPD_MAX = HWPD_LARGELIST
40} HWPAGE_DISPLAYMODE, *PHWPAGE_DISPLAYMODE;
41
42EXTERN_C HWND WINAPI
43DeviceCreateHardwarePageEx(HWND hWndParent,
44 LPGUID lpGuids,
45 UINT uNumberOfGuids,
46 HWPAGE_DISPLAYMODE DisplayMode);
47UINT SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax);
48
49static VOID
50GetDriveNameWithLetter(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
68static VOID
69InitializeChkDskDialog(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
81static HWND hChkdskDrvDialog = NULL;
82static BOOLEAN bChkdskSuccess = FALSE;
83
84static BOOLEAN NTAPI
85ChkdskCallback(
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
118static VOID
119ChkDskNow(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
159static INT_PTR CALLBACK
160ChkDskDlg(
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
191VOID
192CDrvDefExt::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 hBlackPen = (HPEN)GetStockObject(BLACK_PEN);
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 HPEN hCenterPen;
237
238 if (m_FreeSpacePerc < 50 && x == xRadial)
239 SelectObject(pDrawItem->hDC, hDarkBluePen);
240
241 /* Temporarily change pens to draw edges */
242 if (x == pDrawItem->rcItem.left)
243 hCenterPen = (HPEN)SelectObject(pDrawItem->hDC, hBlackPen);
244 else if (x == pDrawItem->rcItem.right - 1)
245 SelectObject(pDrawItem->hDC, hBlackPen);
246
247 MoveToEx(pDrawItem->hDC, x, y, NULL);
248 LineTo(pDrawItem->hDC, x, y + 10);
249 SetPixel(pDrawItem->hDC, x, y + 10, RGB(0, 0, 0));
250
251 /* Restore fill section pens */
252 if (x == pDrawItem->rcItem.left)
253 SelectObject(pDrawItem->hDC, hCenterPen);
254 }
255
256 SelectObject(pDrawItem->hDC, hBlackPen);
257
258 if (m_FreeSpacePerc > 50)
259 {
260 hbrOld = (HBRUSH)SelectObject(pDrawItem->hDC, hMagBrush);
261
262 Ellipse(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top,
263 pDrawItem->rcItem.right, pDrawItem->rcItem.bottom - 10);
264
265 SelectObject(pDrawItem->hDC, hBlueBrush);
266
267 if (m_FreeSpacePerc < 100)
268 {
269 Pie(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, pDrawItem->rcItem.right,
270 pDrawItem->rcItem.bottom - 10, xRadial, yRadial, pDrawItem->rcItem.left, yCenter);
271 }
272 }
273 else
274 {
275 hbrOld = (HBRUSH)SelectObject(pDrawItem->hDC, hBlueBrush);
276
277 Ellipse(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top,
278 pDrawItem->rcItem.right, pDrawItem->rcItem.bottom - 10);
279
280 SelectObject(pDrawItem->hDC, hMagBrush);
281
282 if (m_FreeSpacePerc > 0)
283 {
284 Pie(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, pDrawItem->rcItem.right,
285 pDrawItem->rcItem.bottom - 10, pDrawItem->rcItem.left, yCenter, xRadial, yRadial);
286 }
287 }
288
289 SelectObject(pDrawItem->hDC, hbrOld);
290 SelectObject(pDrawItem->hDC, hOldPen);
291
292 DeleteObject(hBlueBrush);
293 DeleteObject(hMagBrush);
294 DeleteObject(hDarkBluePen);
295 DeleteObject(hDarkMagPen);
296 }
297}
298
299// https://stackoverflow.com/questions/3098696/get-information-about-disk-drives-result-on-windows7-32-bit-system/3100268#3100268
300static BOOL
301GetDriveTypeAndCharacteristics(HANDLE hDevice, DEVICE_TYPE *pDeviceType, ULONG *pCharacteristics)
302{
303 NTSTATUS Status;
304 IO_STATUS_BLOCK IoStatusBlock;
305 FILE_FS_DEVICE_INFORMATION DeviceInfo;
306
307 Status = NtQueryVolumeInformationFile(hDevice, &IoStatusBlock,
308 &DeviceInfo, sizeof(DeviceInfo),
309 FileFsDeviceInformation);
310 if (Status == NO_ERROR)
311 {
312 *pDeviceType = DeviceInfo.DeviceType;
313 *pCharacteristics = DeviceInfo.Characteristics;
314 return TRUE;
315 }
316
317 return FALSE;
318}
319
320BOOL IsDriveFloppyW(LPCWSTR pszDriveRoot)
321{
322 LPCWSTR RootPath = pszDriveRoot;
323 WCHAR szRoot[16], szDeviceName[16];
324 UINT uType;
325 HANDLE hDevice;
326 DEVICE_TYPE DeviceType;
327 ULONG ulCharacteristics;
328 BOOL ret;
329
330 lstrcpynW(szRoot, RootPath, _countof(szRoot));
331
332 if (L'a' <= szRoot[0] && szRoot[0] <= 'z')
333 {
334 szRoot[0] += ('A' - 'a');
335 }
336
337 if ('A' <= szRoot[0] && szRoot[0] <= L'Z' &&
338 szRoot[1] == L':' && szRoot[2] == 0)
339 {
340 // 'C:' --> 'C:\'
341 szRoot[2] = L'\\';
342 szRoot[3] = 0;
343 }
344
345 if (!PathIsRootW(szRoot))
346 {
347 return FALSE;
348 }
349
350 uType = GetDriveTypeW(szRoot);
351 if (uType == DRIVE_REMOVABLE)
352 {
353 if (szRoot[0] == L'A' || szRoot[0] == L'B')
354 return TRUE;
355 }
356 else
357 {
358 return FALSE;
359 }
360
361 lstrcpynW(szDeviceName, L"\\\\.\\", _countof(szDeviceName));
362 szDeviceName[4] = szRoot[0];
363 szDeviceName[5] = L':';
364 szDeviceName[6] = UNICODE_NULL;
365
366 hDevice = CreateFileW(szDeviceName, FILE_READ_ATTRIBUTES,
367 FILE_SHARE_READ | FILE_SHARE_WRITE,
368 NULL, OPEN_EXISTING, 0, NULL);
369 if (hDevice == INVALID_HANDLE_VALUE)
370 {
371 return FALSE;
372 }
373
374 ret = FALSE;
375 if (GetDriveTypeAndCharacteristics(hDevice, &DeviceType, &ulCharacteristics))
376 {
377 if ((ulCharacteristics & FILE_FLOPPY_DISKETTE) == FILE_FLOPPY_DISKETTE)
378 ret = TRUE;
379 }
380
381 CloseHandle(hDevice);
382
383 return ret;
384}
385
386BOOL IsDriveFloppyA(LPCSTR pszDriveRoot)
387{
388 WCHAR szRoot[8];
389 MultiByteToWideChar(CP_ACP, 0, pszDriveRoot, -1, szRoot, _countof(szRoot));
390 return IsDriveFloppyW(szRoot);
391}
392
393VOID
394CDrvDefExt::InitGeneralPage(HWND hwndDlg)
395{
396 WCHAR wszVolumeName[MAX_PATH+1] = {0};
397 WCHAR wszFileSystem[MAX_PATH+1] = {0};
398 WCHAR wszBuf[128];
399 BOOL bRet, bFloppy = FALSE, bHasFS = FALSE;
400
401 bRet = GetVolumeInformationW(m_wszDrive, wszVolumeName, _countof(wszVolumeName), NULL, NULL, NULL, wszFileSystem, _countof(wszFileSystem));
402 if (bRet)
403 {
404 /* Set volume label and filesystem */
405 SetDlgItemTextW(hwndDlg, 14000, wszVolumeName);
406 SetDlgItemTextW(hwndDlg, 14002, wszFileSystem);
407 bHasFS = *wszFileSystem != UNICODE_NULL;
408 }
409 else
410 {
411 LoadStringW(shell32_hInstance, IDS_FS_UNKNOWN, wszFileSystem, _countof(wszFileSystem));
412 SetDlgItemTextW(hwndDlg, 14002, wszFileSystem);
413 }
414
415 /* Set drive type and icon */
416 // TODO: Call SHGetFileInfo to get this info
417 UINT DriveType = GetDriveTypeW(m_wszDrive);
418 UINT IconId, TypeStrId;
419 switch (DriveType)
420 {
421 case DRIVE_REMOVABLE:
422 bFloppy = IsDriveFloppyW(m_wszDrive);
423 IconId = bFloppy ? IDI_SHELL_3_14_FLOPPY : IDI_SHELL_REMOVEABLE;
424 TypeStrId = bFloppy ? IDS_DRIVE_FLOPPY : IDS_DRIVE_REMOVABLE;
425 break;
426 case DRIVE_CDROM: IconId = IDI_SHELL_CDROM; TypeStrId = IDS_DRIVE_CDROM; break;
427 case DRIVE_REMOTE: IconId = IDI_SHELL_NETDRIVE; TypeStrId = IDS_DRIVE_NETWORK; break;
428 case DRIVE_RAMDISK: IconId = IDI_SHELL_RAMDISK; break;
429 default: IconId = IDI_SHELL_DRIVE; TypeStrId = IDS_DRIVE_FIXED;
430 }
431
432 BOOL bCanSetLabel = bHasFS;
433 if (DriveType == DRIVE_CDROM || DriveType == DRIVE_REMOTE || bFloppy)
434 {
435 bCanSetLabel = bFloppy && bHasFS;
436
437 /* disk compression */
438 ShowWindow(GetDlgItem(hwndDlg, 14011), FALSE);
439
440 /* index */
441 ShowWindow(GetDlgItem(hwndDlg, 14012), FALSE);
442 }
443 /* volume label textbox */
444 SendMessage(GetDlgItem(hwndDlg, 14000), EM_SETREADONLY, !bCanSetLabel, 0);
445
446 HICON hIcon = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IconId), IMAGE_ICON, 32, 32, LR_SHARED);
447 if (hIcon)
448 SendDlgItemMessageW(hwndDlg, 14016, STM_SETICON, (WPARAM)hIcon, 0);
449 if (TypeStrId && LoadStringW(shell32_hInstance, TypeStrId, wszBuf, _countof(wszBuf)))
450 SetDlgItemTextW(hwndDlg, 14001, wszBuf);
451
452 ULARGE_INTEGER FreeBytesAvailable, TotalNumberOfBytes;
453 if(GetDiskFreeSpaceExW(m_wszDrive, &FreeBytesAvailable, &TotalNumberOfBytes, NULL))
454 {
455 /* Init free space percentage used for drawing piechart */
456 m_FreeSpacePerc = (UINT)(FreeBytesAvailable.QuadPart * 100ull / TotalNumberOfBytes.QuadPart);
457
458 /* Used space */
459 if (SH_FormatByteSize(TotalNumberOfBytes.QuadPart - FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
460 SetDlgItemTextW(hwndDlg, 14003, wszBuf);
461
462 if (StrFormatByteSizeW(TotalNumberOfBytes.QuadPart - FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
463 SetDlgItemTextW(hwndDlg, 14004, wszBuf);
464
465 /* Free space */
466 if (SH_FormatByteSize(FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
467 SetDlgItemTextW(hwndDlg, 14005, wszBuf);
468
469 if (StrFormatByteSizeW(FreeBytesAvailable.QuadPart, wszBuf, _countof(wszBuf)))
470 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
471
472 /* Total space */
473 if (SH_FormatByteSize(TotalNumberOfBytes.QuadPart, wszBuf, _countof(wszBuf)))
474 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
475
476 if (StrFormatByteSizeW(TotalNumberOfBytes.QuadPart, wszBuf, _countof(wszBuf)))
477 SetDlgItemTextW(hwndDlg, 14008, wszBuf);
478 }
479 else
480 {
481 m_FreeSpacePerc = 0;
482
483 if (SH_FormatByteSize(0, wszBuf, _countof(wszBuf)))
484 {
485 SetDlgItemTextW(hwndDlg, 14003, wszBuf);
486 SetDlgItemTextW(hwndDlg, 14005, wszBuf);
487 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
488 }
489 if (StrFormatByteSizeW(0, wszBuf, _countof(wszBuf)))
490 {
491 SetDlgItemTextW(hwndDlg, 14004, wszBuf);
492 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
493 SetDlgItemTextW(hwndDlg, 14008, wszBuf);
494 }
495 }
496
497 /* Set drive description */
498 WCHAR wszFormat[50];
499 GetDlgItemTextW(hwndDlg, 14009, wszFormat, _countof(wszFormat));
500 swprintf(wszBuf, wszFormat, m_wszDrive[0]);
501 SetDlgItemTextW(hwndDlg, 14009, wszBuf);
502
503 /* show disk cleanup button only for fixed drives */
504 ShowWindow(GetDlgItem(hwndDlg, 14010), DriveType == DRIVE_FIXED);
505}
506
507INT_PTR CALLBACK
508CDrvDefExt::GeneralPageProc(
509 HWND hwndDlg,
510 UINT uMsg,
511 WPARAM wParam,
512 LPARAM lParam)
513{
514 switch(uMsg)
515 {
516 case WM_INITDIALOG:
517 {
518 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
519 if (ppsp == NULL)
520 break;
521
522 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(ppsp->lParam);
523 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pDrvDefExt);
524 pDrvDefExt->InitGeneralPage(hwndDlg);
525 return TRUE;
526 }
527 case WM_DRAWITEM:
528 {
529 LPDRAWITEMSTRUCT pDrawItem = (LPDRAWITEMSTRUCT)lParam;
530
531 if (pDrawItem->CtlID >= 14013 && pDrawItem->CtlID <= 14015)
532 {
533 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
534 pDrvDefExt->PaintStaticControls(hwndDlg, pDrawItem);
535 return TRUE;
536 }
537 break;
538 }
539 case WM_PAINT:
540 break;
541 case WM_COMMAND:
542 if (LOWORD(wParam) == 14010) /* Disk Cleanup */
543 {
544 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
545 WCHAR wszBuf[256];
546 DWORD cbBuf = sizeof(wszBuf);
547
548 if (RegGetValueW(HKEY_LOCAL_MACHINE,
549 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\CleanupPath",
550 NULL,
551 RRF_RT_REG_SZ,
552 NULL,
553 (PVOID)wszBuf,
554 &cbBuf) == ERROR_SUCCESS)
555 {
556 WCHAR wszCmd[MAX_PATH];
557
558 StringCbPrintfW(wszCmd, sizeof(wszCmd), wszBuf, pDrvDefExt->m_wszDrive[0]);
559 WCHAR* wszArgs = PathGetArgsW(wszCmd);
560 if (wszArgs && *wszArgs && wszArgs != wszCmd)
561 wszArgs[-1] = UNICODE_NULL;
562 else
563 wszArgs = NULL;
564
565 if (ShellExecuteW(hwndDlg, NULL, wszCmd, wszArgs, NULL, SW_SHOW) <= (HINSTANCE)32)
566 ERR("Failed to create cleanup process %ls\n", wszCmd);
567 }
568 }
569 else if (LOWORD(wParam) == 14000) /* Label */
570 {
571 if (HIWORD(wParam) == EN_CHANGE)
572 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
573 }
574 break;
575 case WM_NOTIFY:
576 if (((LPNMHDR)lParam)->hwndFrom == GetParent(hwndDlg))
577 {
578 /* Property Sheet */
579 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
580
581 if (lppsn->hdr.code == PSN_APPLY)
582 {
583 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
584
585 HRESULT hr = E_FAIL;
586 HWND hLabel = GetDlgItem(hwndDlg, 14000);
587 WCHAR wszBuf[256];
588 *wszBuf = UNICODE_NULL;
589 if (GetWindowTextW(hLabel, wszBuf, _countof(wszBuf)) || GetWindowTextLengthW(hLabel) == 0)
590 hr = CDrivesFolder::SetDriveLabel(hwndDlg, pDrvDefExt->m_wszDrive, wszBuf);
591
592 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FAILED(hr) ? PSNRET_INVALID : PSNRET_NOERROR);
593 return TRUE;
594 }
595 }
596 break;
597
598 default:
599 break;
600 }
601
602 return FALSE;
603}
604
605INT_PTR CALLBACK
606CDrvDefExt::ExtraPageProc(
607 HWND hwndDlg,
608 UINT uMsg,
609 WPARAM wParam,
610 LPARAM lParam)
611{
612 switch (uMsg)
613 {
614 case WM_INITDIALOG:
615 {
616 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
617 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)ppsp->lParam);
618 return TRUE;
619 }
620 case WM_COMMAND:
621 {
622 WCHAR wszBuf[MAX_PATH];
623 DWORD cbBuf = sizeof(wszBuf);
624 CDrvDefExt *pDrvDefExt = reinterpret_cast<CDrvDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
625
626 switch(LOWORD(wParam))
627 {
628 case 14000:
629 DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_CHECK_DISK), hwndDlg, ChkDskDlg, (LPARAM)pDrvDefExt->m_wszDrive);
630 break;
631 case 14001:
632 if (RegGetValueW(HKEY_LOCAL_MACHINE,
633 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\DefragPath",
634 NULL,
635 RRF_RT_REG_SZ,
636 NULL,
637 (PVOID)wszBuf,
638 &cbBuf) == ERROR_SUCCESS)
639 {
640 WCHAR wszCmd[MAX_PATH];
641
642 StringCbPrintfW(wszCmd, sizeof(wszCmd), wszBuf, pDrvDefExt->m_wszDrive[0]);
643
644 if (ShellExecuteW(hwndDlg, NULL, wszCmd, NULL, NULL, SW_SHOW) <= (HINSTANCE)32)
645 ERR("Failed to create defrag process %ls\n", wszCmd);
646 }
647 break;
648 case 14002:
649 if (RegGetValueW(HKEY_LOCAL_MACHINE,
650 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\BackupPath",
651 NULL,
652 RRF_RT_REG_SZ,
653 NULL,
654 (PVOID)wszBuf,
655 &cbBuf) == ERROR_SUCCESS)
656 {
657 if (ShellExecuteW(hwndDlg, NULL, wszBuf, NULL, NULL, SW_SHOW) <= (HINSTANCE)32)
658 ERR("Failed to create backup process %ls\n", wszBuf);
659 }
660 }
661 break;
662 }
663 }
664 return FALSE;
665}
666
667INT_PTR CALLBACK
668CDrvDefExt::HardwarePageProc(
669 HWND hwndDlg,
670 UINT uMsg,
671 WPARAM wParam,
672 LPARAM lParam)
673{
674 UNREFERENCED_PARAMETER(lParam);
675 UNREFERENCED_PARAMETER(wParam);
676
677 switch(uMsg)
678 {
679 case WM_INITDIALOG:
680 {
681 GUID Guids[2];
682 Guids[0] = GUID_DEVCLASS_DISKDRIVE;
683 Guids[1] = GUID_DEVCLASS_CDROM;
684
685 /* create the hardware page */
686 DeviceCreateHardwarePageEx(hwndDlg, Guids, _countof(Guids), HWPD_STANDARDLIST);
687 break;
688 }
689 }
690
691 return FALSE;
692}
693
694CDrvDefExt::CDrvDefExt()
695{
696 m_wszDrive[0] = L'\0';
697}
698
699CDrvDefExt::~CDrvDefExt()
700{
701
702}
703
704struct CDrop
705{
706 HRESULT hr;
707 STGMEDIUM stgm;
708 HDROP hDrop;
709
710 explicit CDrop(IDataObject *pDO)
711 {
712 FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
713 hDrop = SUCCEEDED(hr = pDO->GetData(&format, &stgm)) ? (HDROP)stgm.hGlobal : NULL;
714 }
715
716 ~CDrop()
717 {
718 if (hDrop)
719 ReleaseStgMedium(&stgm);
720 }
721
722 UINT GetCount()
723 {
724 return DragQueryFileW(hDrop, -1, NULL, 0);
725 }
726};
727
728static inline bool
729IsValidDrivePath(PCWSTR Path)
730{
731 return GetDriveTypeW(Path) > DRIVE_NO_ROOT_DIR;
732}
733
734HRESULT WINAPI
735CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
736{
737 HRESULT hr;
738
739 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
740
741 if (!pDataObj)
742 return E_FAIL;
743
744
745 CDrop drop(pDataObj);
746 if (FAILED_UNEXPECTEDLY(hr = drop.hr))
747 return hr;
748
749 if (!DragQueryFileW(drop.hDrop, 0, m_wszDrive, _countof(m_wszDrive)))
750 {
751 ERR("DragQueryFileW failed\n");
752 return E_FAIL;
753 }
754
755 if (drop.GetCount() > 1)
756 m_Multiple = pDataObj;
757
758 TRACE("Drive properties %ls\n", m_wszDrive);
759
760 return S_OK;
761}
762
763HRESULT WINAPI
764CDrvDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
765{
766 UNIMPLEMENTED;
767 return E_NOTIMPL;
768}
769
770HRESULT WINAPI
771CDrvDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
772{
773 UNIMPLEMENTED;
774 return E_NOTIMPL;
775}
776
777HRESULT WINAPI
778CDrvDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
779{
780 UNIMPLEMENTED;
781 return E_NOTIMPL;
782}
783
784HRESULT
785CDrvDefExt::AddMainPage(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
786{
787 WCHAR szTitle[MAX_PATH], *pszTitle = NULL;
788 if (m_Multiple)
789 {
790 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl(SHSimpleIDListFromPathW(m_wszDrive));
791 if (SUCCEEDED(SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, szTitle, _countof(szTitle), NULL)))
792 pszTitle = szTitle;
793 }
794
795 HPROPSHEETPAGE hPage;
796 hPage = SH_CreatePropertySheetPageEx(IDD_DRIVE_PROPERTIES, GeneralPageProc, (LPARAM)this,
797 pszTitle, &PropSheetPageLifetimeCallback<CDrvDefExt>);
798 HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam);
799 if (FAILED_UNEXPECTEDLY(hr))
800 return hr;
801 else
802 AddRef(); // For PropSheetPageLifetimeCallback
803 return hr;
804}
805
806HRESULT WINAPI
807CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
808{
809 HRESULT hr = AddMainPage(pfnAddPage, lParam);
810 if (FAILED_UNEXPECTEDLY(hr))
811 return hr;
812
813 if (m_Multiple)
814 {
815 CDrop drop(m_Multiple);
816 UINT count = SUCCEEDED(drop.hr) ? drop.GetCount() : 0;
817 for (UINT i = 0; ++i < count;) // Skipping the first drive since it already has a page
818 {
819 CComPtr<CDrvDefExt> SheetExt;
820 if (FAILED_UNEXPECTEDLY(hr = ShellObjectCreator(SheetExt)))
821 continue;
822 if (!DragQueryFileW(drop.hDrop, i, SheetExt->m_wszDrive, _countof(SheetExt->m_wszDrive)))
823 continue;
824 if (!IsValidDrivePath(SheetExt->m_wszDrive))
825 continue;
826
827 SheetExt->m_Multiple = m_Multiple;
828 SheetExt->AddMainPage(pfnAddPage, lParam);
829 }
830 }
831 else
832 {
833 HPROPSHEETPAGE hPage;
834 if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
835 {
836 hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
837 ExtraPageProc,
838 (LPARAM)this,
839 NULL);
840 if (hPage)
841 pfnAddPage(hPage, lParam);
842 }
843
844 if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
845 {
846 hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
847 HardwarePageProc,
848 (LPARAM)this,
849 NULL);
850 if (hPage)
851 pfnAddPage(hPage, lParam);
852 }
853 }
854
855 return S_OK;
856}
857
858HRESULT WINAPI
859CDrvDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
860{
861 UNIMPLEMENTED;
862 return E_NOTIMPL;
863}
864
865HRESULT WINAPI
866CDrvDefExt::SetSite(IUnknown *punk)
867{
868 UNIMPLEMENTED;
869 return E_NOTIMPL;
870}
871
872HRESULT WINAPI
873CDrvDefExt::GetSite(REFIID iid, void **ppvSite)
874{
875 UNIMPLEMENTED;
876 return E_NOTIMPL;
877}