[CRT] Remove useless #undef abort from process.h
[reactos.git] / dll / win32 / shell32 / folders / CDrivesFolder.cpp
1 /*
2 * Virtual Workplace folder
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2009 Andrew Hill
7 * Copyright 2017-2019 Katayama Hirofumi MZ
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <precomp.h>
25 #include <process.h>
26
27 WINE_DEFAULT_DEBUG_CHANNEL (shell);
28
29 /*
30 CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in
31 the registry. The CRegFolder is aggregated by the CDrivesFolder.
32 The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder
33 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
34 CDrivesFolderEnum is only responsible for returning the physical items.
35
36 2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE
37 3. The parsing name returned for my computer is incorrect. It should be "My Computer"
38 */
39
40 static int iDriveIconIds[7] = { IDI_SHELL_DRIVE, /* DRIVE_UNKNOWN */
41 IDI_SHELL_CDROM, /* DRIVE_NO_ROOT_DIR*/
42 IDI_SHELL_3_14_FLOPPY, /* DRIVE_REMOVABLE*/
43 IDI_SHELL_DRIVE, /* DRIVE_FIXED*/
44 IDI_SHELL_NETDRIVE, /* DRIVE_REMOTE*/
45 IDI_SHELL_CDROM, /* DRIVE_CDROM*/
46 IDI_SHELL_RAMDISK /* DRIVE_RAMDISK*/
47 };
48
49 static int iDriveTypeIds[7] = { IDS_DRIVE_FIXED, /* DRIVE_UNKNOWN */
50 IDS_DRIVE_FIXED, /* DRIVE_NO_ROOT_DIR*/
51 IDS_DRIVE_FLOPPY, /* DRIVE_REMOVABLE*/
52 IDS_DRIVE_FIXED, /* DRIVE_FIXED*/
53 IDS_DRIVE_NETWORK, /* DRIVE_REMOTE*/
54 IDS_DRIVE_CDROM, /* DRIVE_CDROM*/
55 IDS_DRIVE_FIXED /* DRIVE_RAMDISK*/
56 };
57
58 /***********************************************************************
59 * IShellFolder implementation
60 */
61
62 #define RETRY_COUNT 3
63 #define RETRY_SLEEP 250
64 static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock)
65 {
66 DWORD dwError, dwBytesReturned;
67 DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME);
68 for (DWORD i = 0; i < RETRY_COUNT; ++i)
69 {
70 if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
71 return TRUE;
72
73 dwError = GetLastError();
74 if (dwError == ERROR_INVALID_FUNCTION)
75 break; /* don't sleep if function is not implemented */
76
77 Sleep(RETRY_SLEEP);
78 }
79 SetLastError(dwError);
80 return FALSE;
81 }
82
83 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
84 static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID)
85 {
86 /* GENERIC_WRITE isn't needed for umount */
87 DWORD dwAccessMode = GENERIC_READ;
88 DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
89
90 HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL);
91 if (hDrive == INVALID_HANDLE_VALUE)
92 return FALSE;
93
94 BOOL bResult, bNeedUnlock = FALSE;
95 DWORD dwBytesReturned, dwError = NO_ERROR;
96 PREVENT_MEDIA_REMOVAL removal;
97 do
98 {
99 bResult = TryToLockOrUnlockDrive(hDrive, TRUE);
100 if (!bResult)
101 {
102 dwError = GetLastError();
103 *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */
104 break;
105 }
106 bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
107 if (!bResult)
108 {
109 dwError = GetLastError();
110 *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */
111 bNeedUnlock = TRUE;
112 break;
113 }
114 removal.PreventMediaRemoval = FALSE;
115 bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL,
116 0, &dwBytesReturned, NULL);
117 if (!bResult)
118 {
119 *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
120 dwError = GetLastError();
121 bNeedUnlock = TRUE;
122 break;
123 }
124 bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
125 if (!bResult)
126 {
127 *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
128 dwError = GetLastError();
129 bNeedUnlock = TRUE;
130 break;
131 }
132 } while (0);
133
134 if (bNeedUnlock)
135 {
136 TryToLockOrUnlockDrive(hDrive, FALSE);
137 }
138
139 CloseHandle(hDrive);
140
141 SetLastError(dwError);
142 return bResult;
143 }
144
145 // A callback function for finding the stub windows.
146 static BOOL CALLBACK
147 EnumStubProc(HWND hwnd, LPARAM lParam)
148 {
149 CSimpleArray<HWND> *pStubs = reinterpret_cast<CSimpleArray<HWND> *>(lParam);
150
151 WCHAR szClass[64];
152 GetClassNameW(hwnd, szClass, _countof(szClass));
153
154 if (lstrcmpiW(szClass, L"StubWindow32") == 0)
155 {
156 pStubs->Add(hwnd);
157 }
158
159 return TRUE;
160 }
161
162 // Another callback function to find the owned window of the stub window.
163 static BOOL CALLBACK
164 EnumStubProc2(HWND hwnd, LPARAM lParam)
165 {
166 HWND *phwnd = reinterpret_cast<HWND *>(lParam);
167
168 if (phwnd[0] == GetWindow(hwnd, GW_OWNER))
169 {
170 phwnd[1] = hwnd;
171 return FALSE;
172 }
173
174 return TRUE;
175 }
176
177 // Parameters for format_drive_thread function below.
178 struct THREAD_PARAMS
179 {
180 UINT nDriveNumber;
181 };
182
183 static unsigned __stdcall format_drive_thread(void *args)
184 {
185 THREAD_PARAMS *params = (THREAD_PARAMS *)args;
186 UINT nDriveNumber = params->nDriveNumber;
187 LONG_PTR nProp = nDriveNumber | 0x7F00;
188
189 // Search the stub windows that already exist.
190 CSimpleArray<HWND> old_stubs;
191 EnumWindows(EnumStubProc, (LPARAM)&old_stubs);
192
193 for (INT n = 0; n < old_stubs.GetSize(); ++n)
194 {
195 HWND hwndStub = old_stubs[n];
196
197 // The target stub window has the prop.
198 if (GetPropW(hwndStub, L"DriveNumber") == (HANDLE)nProp)
199 {
200 // Found.
201 HWND ahwnd[2];
202 ahwnd[0] = hwndStub;
203 ahwnd[1] = NULL;
204 EnumWindows(EnumStubProc2, (LPARAM)ahwnd);
205
206 // Activate.
207 BringWindowToTop(ahwnd[1]);
208
209 delete params;
210 return 0;
211 }
212 }
213
214 // Create a stub window.
215 DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION;
216 DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
217 CStubWindow32 stub;
218 if (!stub.Create(NULL, NULL, NULL, style, exstyle))
219 {
220 ERR("StubWindow32 creation failed\n");
221 delete params;
222 return 0;
223 }
224
225 // Add prop to the target stub window.
226 SetPropW(stub, L"DriveNumber", (HANDLE)nProp);
227
228 // Do format.
229 SHFormatDrive(stub, nDriveNumber, SHFMT_ID_DEFAULT, 0);
230
231 // Clean up.
232 RemovePropW(stub, L"DriveNumber");
233 stub.DestroyWindow();
234 delete params;
235
236 return 0;
237 }
238
239 static HRESULT DoFormatDrive(HWND hwnd, UINT nDriveNumber)
240 {
241 THREAD_PARAMS *params = new THREAD_PARAMS;
242 params->nDriveNumber = nDriveNumber;
243
244 // Create thread to avoid locked.
245 unsigned tid;
246 HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, format_drive_thread, params, 0, &tid);
247 if (hThread == NULL)
248 {
249 delete params;
250 return E_FAIL;
251 }
252
253 CloseHandle(hThread);
254
255 return S_OK;
256 }
257
258 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
259 HWND hwnd,
260 IDataObject *pdtobj,
261 UINT uMsg,
262 WPARAM wParam,
263 LPARAM lParam)
264 {
265 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
266 return S_OK;
267
268 PIDLIST_ABSOLUTE pidlFolder;
269 PUITEMID_CHILD *apidl;
270 UINT cidl;
271 UINT nDriveType;
272 DWORD dwFlags;
273 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
274 if (FAILED_UNEXPECTEDLY(hr))
275 return hr;
276
277 char szDrive[8] = {0};
278 if (!_ILGetDrive(apidl[0], szDrive, sizeof(szDrive)))
279 {
280 ERR("pidl is not a drive\n");
281 SHFree(pidlFolder);
282 _ILFreeaPidl(apidl, cidl);
283 return E_FAIL;
284 }
285 nDriveType = GetDriveTypeA(szDrive);
286 GetVolumeInformationA(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
287
288 // custom command IDs
289 #define CMDID_FORMAT 1
290 #define CMDID_EJECT 2
291 #define CMDID_DISCONNECT 3
292
293 if (uMsg == DFM_MERGECONTEXTMENU)
294 {
295 QCMINFO *pqcminfo = (QCMINFO *)lParam;
296
297 UINT idCmdFirst = pqcminfo->idCmdFirst;
298 if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
299 {
300 /* add separator and Format */
301 UINT idCmd = idCmdFirst + CMDID_FORMAT;
302 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
303 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
304 }
305 if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
306 {
307 /* add separator and Eject */
308 UINT idCmd = idCmdFirst + CMDID_EJECT;
309 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
310 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
311 }
312 if (nDriveType == DRIVE_REMOTE)
313 {
314 /* add separator and Disconnect */
315 UINT idCmd = idCmdFirst + CMDID_DISCONNECT;
316 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
317 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
318 }
319
320 pqcminfo->idCmdFirst += 3;
321 }
322 else if (uMsg == DFM_INVOKECOMMAND)
323 {
324 WCHAR wszBuf[4] = L"A:\\";
325 wszBuf[0] = (WCHAR)szDrive[0];
326
327 INT nStringID = 0;
328 DWORD dwError = NO_ERROR;
329
330 if (wParam == DFM_CMD_PROPERTIES)
331 {
332 hr = SH_ShowDriveProperties(wszBuf, pidlFolder, apidl);
333 if (FAILED(hr))
334 {
335 dwError = ERROR_CAN_NOT_COMPLETE;
336 nStringID = IDS_CANTSHOWPROPERTIES;
337 }
338 }
339 else
340 {
341 if (wParam == CMDID_FORMAT)
342 {
343 hr = DoFormatDrive(hwnd, szDrive[0] - 'A');
344 }
345 else if (wParam == CMDID_EJECT)
346 {
347 /* do eject */
348 WCHAR physical[10];
349 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
350
351 if (DoEjectDrive(physical, nDriveType, &nStringID))
352 {
353 SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
354 }
355 else
356 {
357 dwError = GetLastError();
358 }
359 }
360 else if (wParam == CMDID_DISCONNECT)
361 {
362 /* do disconnect */
363 wszBuf[2] = UNICODE_NULL;
364 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
365 if (dwError == NO_ERROR)
366 {
367 SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
368 }
369 else
370 {
371 nStringID = IDS_CANTDISCONNECT;
372 }
373 }
374 }
375
376 if (nStringID != 0)
377 {
378 /* show error message */
379 WCHAR szFormat[128], szMessage[128];
380 LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
381 wsprintfW(szMessage, szFormat, dwError);
382 MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
383 }
384 }
385
386 SHFree(pidlFolder);
387 _ILFreeaPidl(apidl, cidl);
388
389 return hr;
390 }
391
392 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
393 HWND hwnd,
394 UINT cidl,
395 PCUITEMID_CHILD_ARRAY apidl,
396 IShellFolder *psf,
397 IContextMenu **ppcm)
398 {
399 HKEY hKeys[2];
400 UINT cKeys = 0;
401 AddClassKeyToArray(L"Drive", hKeys, &cKeys);
402 AddClassKeyToArray(L"Folder", hKeys, &cKeys);
403
404 return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
405 }
406
407 static HRESULT
408 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
409 LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
410 {
411 WCHAR wszPath[MAX_PATH];
412 WCHAR wszAutoRunInfPath[MAX_PATH];
413 WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
414 static const WCHAR wszAutoRunInf[] = { 'a','u','t','o','r','u','n','.','i','n','f',0 };
415 static const WCHAR wszAutoRun[] = { 'a','u','t','o','r','u','n',0 };
416
417 // get path
418 if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
419 return E_FAIL;
420 if (!PathIsDirectoryW(wszPath))
421 return E_FAIL;
422
423 // build the full path of autorun.inf
424 StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
425 PathAppendW(wszAutoRunInfPath, wszAutoRunInf);
426
427 // autorun.inf --> wszValue
428 if (GetPrivateProfileStringW(wszAutoRun, L"icon", NULL, wszValue, _countof(wszValue),
429 wszAutoRunInfPath) && wszValue[0] != 0)
430 {
431 // wszValue --> wszTemp
432 ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
433
434 // parse the icon location
435 *piIndex = PathParseIconLocationW(wszTemp);
436
437 // wszPath + wszTemp --> wszPath
438 if (PathIsRelativeW(wszTemp))
439 PathAppendW(wszPath, wszTemp);
440 else
441 StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
442
443 // wszPath --> szIconFile
444 GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
445
446 return S_OK;
447 }
448
449 return E_FAIL;
450 }
451
452 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot);
453
454 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
455 {
456 CComPtr<IDefaultExtractIconInit> initIcon;
457 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
458 if (FAILED_UNEXPECTEDLY(hr))
459 return hr;
460
461 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
462 UINT DriveType = GetDriveTypeA(pszDrive);
463 if (DriveType > DRIVE_RAMDISK)
464 DriveType = DRIVE_FIXED;
465
466 WCHAR wTemp[MAX_PATH];
467 int icon_idx;
468 UINT flags = 0;
469 if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
470 (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx)))
471 {
472 initIcon->SetNormalIcon(wTemp, icon_idx);
473 }
474 else if (SUCCEEDED(getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
475 &icon_idx, &flags)))
476 {
477 initIcon->SetNormalIcon(wTemp, icon_idx);
478 }
479 else
480 {
481 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
482 {
483 icon_idx = IDI_SHELL_REMOVEABLE;
484 }
485 else
486 {
487 icon_idx = iDriveIconIds[DriveType];
488 }
489 initIcon->SetNormalIcon(swShell32Name, -icon_idx);
490 }
491
492 return initIcon->QueryInterface(riid, ppvOut);
493 }
494
495 class CDrivesFolderEnum :
496 public CEnumIDListBase
497 {
498 public:
499 HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
500 {
501 /* enumerate the folders */
502 if (dwFlags & SHCONTF_FOLDERS)
503 {
504 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
505 DWORD dwDrivemap = GetLogicalDrives();
506
507 while (wszDriveName[0] <= 'Z')
508 {
509 if(dwDrivemap & 0x00000001L)
510 AddToEnumList(_ILCreateDrive(wszDriveName));
511 wszDriveName[0]++;
512 dwDrivemap = dwDrivemap >> 1;
513 }
514 }
515
516 /* Enumerate the items of the reg folder */
517 AppendItemsFromEnumerator(pRegEnumerator);
518
519 return S_OK;
520 }
521
522 BEGIN_COM_MAP(CDrivesFolderEnum)
523 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
524 END_COM_MAP()
525 };
526
527 /***********************************************************************
528 * IShellFolder [MyComputer] implementation
529 */
530
531 static const shvheader MyComputerSFHeader[] = {
532 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
533 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
534 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
535 {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
536 {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
537 };
538
539 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
540
541 static const DWORD dwComputerAttributes =
542 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
543 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
544 static const DWORD dwControlPanelAttributes =
545 SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
546 static const DWORD dwDriveAttributes =
547 SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
548 SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
549
550 CDrivesFolder::CDrivesFolder()
551 {
552 pidlRoot = NULL;
553 }
554
555 CDrivesFolder::~CDrivesFolder()
556 {
557 TRACE ("-- destroying IShellFolder(%p)\n", this);
558 SHFree(pidlRoot);
559 }
560
561 HRESULT WINAPI CDrivesFolder::FinalConstruct()
562 {
563 pidlRoot = _ILCreateMyComputer(); /* my qualified pidl */
564 if (pidlRoot == NULL)
565 return E_OUTOFMEMORY;
566
567 HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer,
568 pidlRoot,
569 L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
570 L"MyComputer",
571 IID_PPV_ARG(IShellFolder2, &m_regFolder));
572
573 return hr;
574 }
575
576 /**************************************************************************
577 * CDrivesFolder::ParseDisplayName
578 */
579 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
580 DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
581 {
582 HRESULT hr = E_INVALIDARG;
583 LPCWSTR szNext = NULL;
584 LPITEMIDLIST pidlTemp = NULL;
585 INT nDriveNumber;
586
587 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
588 hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
589 pchEaten, ppidl, pdwAttributes);
590
591 *ppidl = 0;
592 if (pchEaten)
593 *pchEaten = 0; /* strange but like the original */
594
595 /* handle CLSID paths */
596 if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
597 return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
598
599 nDriveNumber = PathGetDriveNumberW(lpszDisplayName);
600 if (nDriveNumber < 0)
601 return E_INVALIDARG;
602
603 /* check if this drive actually exists */
604 if ((::GetLogicalDrives() & (1 << nDriveNumber)) == 0)
605 {
606 return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
607 }
608
609 pidlTemp = _ILCreateDrive(lpszDisplayName);
610 if (!pidlTemp)
611 return E_OUTOFMEMORY;
612
613 if (lpszDisplayName[2] == L'\\')
614 {
615 szNext = &lpszDisplayName[3];
616 }
617
618 if (szNext && *szNext)
619 {
620 hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp,
621 (LPOLESTR) szNext, pchEaten, pdwAttributes);
622 }
623 else
624 {
625 hr = S_OK;
626 if (pdwAttributes && *pdwAttributes)
627 {
628 if (_ILIsDrive(pidlTemp))
629 *pdwAttributes &= dwDriveAttributes;
630 else if (_ILIsSpecialFolder(pidlTemp))
631 m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes);
632 else
633 ERR("Got an unkown pidl here!\n");
634 }
635 }
636
637 *ppidl = pidlTemp;
638
639 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
640
641 return hr;
642 }
643
644 /**************************************************************************
645 * CDrivesFolder::EnumObjects
646 */
647 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
648 {
649 CComPtr<IEnumIDList> pRegEnumerator;
650 m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
651
652 return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
653 }
654
655 /**************************************************************************
656 * CDrivesFolder::BindToObject
657 */
658 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
659 {
660 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
661 pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
662
663 if (!pidl)
664 return E_INVALIDARG;
665
666 if (_ILIsSpecialFolder(pidl))
667 return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
668
669 CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
670
671 PERSIST_FOLDER_TARGET_INFO pfti = {0};
672 pfti.dwAttributes = -1;
673 pfti.csidl = -1;
674 pfti.szTargetParsingName[0] = *pchDrive;
675 pfti.szTargetParsingName[1] = L':';
676 pfti.szTargetParsingName[2] = L'\\';
677
678 HRESULT hr = SHELL32_BindToSF(pidlRoot,
679 &pfti,
680 pidl,
681 &CLSID_ShellFSFolder,
682 riid,
683 ppvOut);
684 if (FAILED_UNEXPECTEDLY(hr))
685 return hr;
686
687 return S_OK;
688 }
689
690 /**************************************************************************
691 * CDrivesFolder::BindToStorage
692 */
693 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
694 {
695 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
696 pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
697
698 *ppvOut = NULL;
699 return E_NOTIMPL;
700 }
701
702 /**************************************************************************
703 * CDrivesFolder::CompareIDs
704 */
705
706 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
707 {
708 HRESULT hres;
709
710 if (!pidl1 || !pidl2)
711 {
712 ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
713 return E_INVALIDARG;
714 }
715
716 if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
717 return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
718
719 if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS)
720 return E_INVALIDARG;
721
722 CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
723 CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
724
725 int result;
726 switch(LOWORD(lParam))
727 {
728 case 0: /* name */
729 {
730 result = stricmp(pszDrive1, pszDrive2);
731 hres = MAKE_COMPARE_HRESULT(result);
732 break;
733 }
734 case 1: /* comments */
735 hres = MAKE_COMPARE_HRESULT(0);
736 break;
737 case 2: /* Type */
738 {
739 /* We want to return immediately because SHELL32_CompareDetails also compares children. */
740 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
741 }
742 case 3: /* Size */
743 case 4: /* Size Available */
744 {
745 ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
746
747 if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
748 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
749 else
750 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
751
752 if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
753 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
754 else
755 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
756
757 LARGE_INTEGER Diff;
758 if (lParam == 3) /* Size */
759 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
760 else /* Size available */
761 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
762
763 hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
764 break;
765 }
766 default:
767 return E_INVALIDARG;
768 }
769
770 if (HRESULT_CODE(hres) == 0)
771 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
772
773 return hres;
774 }
775
776 /**************************************************************************
777 * CDrivesFolder::CreateViewObject
778 */
779 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
780 {
781 CComPtr<IShellView> pShellView;
782 HRESULT hr = E_INVALIDARG;
783
784 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
785 hwndOwner, shdebugstr_guid (&riid), ppvOut);
786
787 if (!ppvOut)
788 return hr;
789
790 *ppvOut = NULL;
791
792 if (IsEqualIID(riid, IID_IDropTarget))
793 {
794 WARN("IDropTarget not implemented\n");
795 hr = E_NOTIMPL;
796 }
797 else if (IsEqualIID(riid, IID_IContextMenu))
798 {
799 HKEY hKeys[16];
800 UINT cKeys = 0;
801 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
802
803 DEFCONTEXTMENU dcm;
804 dcm.hwnd = hwndOwner;
805 dcm.pcmcb = this;
806 dcm.pidlFolder = pidlRoot;
807 dcm.psf = this;
808 dcm.cidl = 0;
809 dcm.apidl = NULL;
810 dcm.cKeys = cKeys;
811 dcm.aKeys = hKeys;
812 dcm.punkAssociationInfo = NULL;
813 hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
814 }
815 else if (IsEqualIID(riid, IID_IShellView))
816 {
817 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
818 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
819 }
820 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
821 return hr;
822 }
823
824 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
825 {
826 GUID *guid = _ILGetGUIDPointer(pidl);
827
828 TRACE("(%p)\n", pidl);
829
830 if (guid)
831 return IsEqualIID(*guid, CLSID_ControlPanel);
832 return FALSE;
833 }
834
835 /**************************************************************************
836 * CDrivesFolder::GetAttributesOf
837 */
838 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
839 {
840 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
841 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
842
843 if (cidl && !apidl)
844 return E_INVALIDARG;
845
846 if (*rgfInOut == 0)
847 *rgfInOut = ~0;
848
849 /* FIXME: always add SFGAO_CANLINK */
850 if(cidl == 0)
851 *rgfInOut &= dwComputerAttributes;
852 else
853 {
854 for (UINT i = 0; i < cidl; ++i)
855 {
856 if (_ILIsDrive(apidl[i]))
857 *rgfInOut &= dwDriveAttributes;
858 else if (_ILIsControlPanel(apidl[i]))
859 *rgfInOut &= dwControlPanelAttributes;
860 else if (_ILIsSpecialFolder(*apidl))
861 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
862 else
863 ERR("Got unknown pidl type!\n");
864 }
865 }
866
867 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
868 *rgfInOut &= ~SFGAO_VALIDATE;
869
870 TRACE ("-- result=0x%08x\n", *rgfInOut);
871 return S_OK;
872 }
873
874 /**************************************************************************
875 * CDrivesFolder::GetUIObjectOf
876 *
877 * PARAMETERS
878 * hwndOwner [in] Parent window for any output
879 * cidl [in] array size
880 * apidl [in] simple pidl array
881 * riid [in] Requested Interface
882 * prgfInOut [ ] reserved
883 * ppvObject [out] Resulting Interface
884 *
885 */
886 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
887 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
888 REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
889 {
890 LPVOID pObj = NULL;
891 HRESULT hr = E_INVALIDARG;
892
893 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
894 hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
895
896 if (!ppvOut)
897 return hr;
898
899 *ppvOut = NULL;
900
901 if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
902 {
903 if (_ILIsDrive(apidl[0]))
904 hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
905 else
906 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
907 }
908 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
909 {
910 hr = IDataObject_Constructor (hwndOwner,
911 pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
912 }
913 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
914 {
915 if (_ILIsDrive(apidl[0]))
916 hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
917 else
918 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
919 }
920 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
921 {
922 CComPtr<IShellFolder> psfChild;
923 hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
924 if (FAILED_UNEXPECTEDLY(hr))
925 return hr;
926
927 return psfChild->CreateViewObject(NULL, riid, ppvOut);
928 }
929 else
930 hr = E_NOINTERFACE;
931
932 if (SUCCEEDED(hr) && !pObj)
933 hr = E_OUTOFMEMORY;
934
935 *ppvOut = pObj;
936 TRACE ("(%p)->hr=0x%08x\n", this, hr);
937 return hr;
938 }
939
940 /**************************************************************************
941 * CDrivesFolder::GetDisplayNameOf
942 */
943 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
944 {
945 LPWSTR pszPath;
946 HRESULT hr = S_OK;
947
948 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
949 pdump (pidl);
950
951 if (!strRet)
952 return E_INVALIDARG;
953
954 if (!_ILIsPidlSimple (pidl))
955 {
956 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
957 }
958 else if (_ILIsSpecialFolder(pidl))
959 {
960 return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
961 }
962 else if (!_ILIsDrive(pidl))
963 {
964 ERR("Wrong pidl type\n");
965 return E_INVALIDARG;
966 }
967
968 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
969 if (!pszPath)
970 return E_OUTOFMEMORY;
971
972 pszPath[0] = 0;
973
974 _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */
975 /* long view "lw_name (C:)" */
976 if (!(dwFlags & SHGDN_FORPARSING))
977 {
978 WCHAR wszDrive[18] = {0};
979 DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
980 static const WCHAR wszOpenBracket[] = {' ', '(', 0};
981 static const WCHAR wszCloseBracket[] = {')', 0};
982
983 lstrcpynW(wszDrive, pszPath, 4);
984 pszPath[0] = L'\0';
985 GetVolumeInformationW(wszDrive, pszPath,
986 MAX_PATH - 7,
987 &dwVolumeSerialNumber,
988 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
989 pszPath[MAX_PATH-1] = L'\0';
990 if (!wcslen(pszPath))
991 {
992 UINT DriveType, ResourceId;
993 DriveType = GetDriveTypeW(wszDrive);
994 switch(DriveType)
995 {
996 case DRIVE_FIXED:
997 ResourceId = IDS_DRIVE_FIXED;
998 break;
999 case DRIVE_REMOTE:
1000 ResourceId = IDS_DRIVE_NETWORK;
1001 break;
1002 case DRIVE_CDROM:
1003 ResourceId = IDS_DRIVE_CDROM;
1004 break;
1005 default:
1006 ResourceId = 0;
1007 }
1008 if (ResourceId)
1009 {
1010 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1011 if (dwFileSystemFlags > MAX_PATH - 7)
1012 pszPath[MAX_PATH-7] = L'\0';
1013 }
1014 }
1015 wcscat (pszPath, wszOpenBracket);
1016 wszDrive[2] = L'\0';
1017 wcscat (pszPath, wszDrive);
1018 wcscat (pszPath, wszCloseBracket);
1019 }
1020
1021 if (SUCCEEDED(hr))
1022 {
1023 strRet->uType = STRRET_WSTR;
1024 strRet->pOleStr = pszPath;
1025 }
1026 else
1027 CoTaskMemFree(pszPath);
1028
1029 TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1030 return hr;
1031 }
1032
1033 /**************************************************************************
1034 * CDrivesFolder::SetNameOf
1035 * Changes the name of a file object or subfolder, possibly changing its item
1036 * identifier in the process.
1037 *
1038 * PARAMETERS
1039 * hwndOwner [in] Owner window for output
1040 * pidl [in] simple pidl of item to change
1041 * lpszName [in] the items new display name
1042 * dwFlags [in] SHGNO formatting flags
1043 * ppidlOut [out] simple pidl returned
1044 */
1045 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1046 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1047 {
1048 WCHAR szName[30];
1049
1050 if (_ILIsDrive(pidl))
1051 {
1052 if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1053 SetVolumeLabelW(szName, lpName);
1054 if (pPidlOut)
1055 *pPidlOut = _ILCreateDrive(szName);
1056 return S_OK;
1057 }
1058
1059 return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1060 }
1061
1062 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1063 {
1064 FIXME ("(%p)\n", this);
1065 return E_NOTIMPL;
1066 }
1067
1068 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1069 {
1070 FIXME ("(%p)\n", this);
1071 return E_NOTIMPL;
1072 }
1073
1074 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1075 {
1076 TRACE ("(%p)\n", this);
1077
1078 if (pSort)
1079 *pSort = 0;
1080 if (pDisplay)
1081 *pDisplay = 0;
1082 return S_OK;
1083 }
1084
1085 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
1086 {
1087 TRACE ("(%p)\n", this);
1088
1089 if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1090 return E_INVALIDARG;
1091 *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
1092 return S_OK;
1093 }
1094
1095 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1096 {
1097 FIXME ("(%p)\n", this);
1098 return E_NOTIMPL;
1099 }
1100
1101 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1102 {
1103 HRESULT hr;
1104
1105 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1106
1107 if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1108 return E_INVALIDARG;
1109
1110 if (!pidl)
1111 {
1112 psd->fmt = MyComputerSFHeader[iColumn].fmt;
1113 psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1114 return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1115 }
1116 else if (!_ILIsDrive(pidl))
1117 {
1118 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1119 }
1120 else
1121 {
1122 ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1123 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1124 UINT DriveType = GetDriveTypeA(pszDrive);
1125 if (DriveType > DRIVE_RAMDISK)
1126 DriveType = DRIVE_FIXED;
1127
1128 switch (iColumn)
1129 {
1130 case 0: /* name */
1131 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1132 break;
1133 case 1: /* FIXME: comments */
1134 hr = SHSetStrRet(&psd->str, "");
1135 break;
1136 case 2: /* type */
1137 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1138 hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1139 else
1140 hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1141 break;
1142 case 3: /* total size */
1143 case 4: /* free size */
1144 psd->str.cStr[0] = 0x00;
1145 psd->str.uType = STRRET_CSTR;
1146 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1147 {
1148 GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1149 if (iColumn == 3)
1150 StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1151 else
1152 StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1153 }
1154 hr = S_OK;
1155 break;
1156 }
1157 }
1158
1159 return hr;
1160 }
1161
1162 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1163 {
1164 FIXME("(%p)\n", this);
1165 return E_NOTIMPL;
1166 }
1167
1168 /************************************************************************
1169 * CDrivesFolder::GetClassID
1170 */
1171 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1172 {
1173 TRACE ("(%p)\n", this);
1174
1175 if (!lpClassId)
1176 return E_POINTER;
1177
1178 *lpClassId = CLSID_MyComputer;
1179 return S_OK;
1180 }
1181
1182 /************************************************************************
1183 * CDrivesFolder::Initialize
1184 *
1185 * NOTES: it makes no sense to change the pidl
1186 */
1187 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1188 {
1189 return S_OK;
1190 }
1191
1192 /**************************************************************************
1193 * CDrivesFolder::GetCurFolder
1194 */
1195 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1196 {
1197 TRACE("(%p)->(%p)\n", this, pidl);
1198
1199 if (!pidl)
1200 return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1201
1202 *pidl = ILClone(pidlRoot);
1203 return S_OK;
1204 }
1205
1206 /************************************************************************/
1207 /* IContextMenuCB interface */
1208
1209 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1210 {
1211 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1212 return S_OK;
1213
1214 /* no data object means no selection */
1215 if (!pdtobj)
1216 {
1217 if (uMsg == DFM_INVOKECOMMAND && wParam == 1) // #1
1218 {
1219 // "System" properties
1220 ShellExecuteW(hwndOwner,
1221 NULL,
1222 L"rundll32.exe",
1223 L"shell32.dll,Control_RunDLL sysdm.cpl",
1224 NULL,
1225 SW_SHOWNORMAL);
1226 }
1227 else if (uMsg == DFM_MERGECONTEXTMENU)
1228 {
1229 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1230 HMENU hpopup = CreatePopupMenu();
1231 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_SEPARATOR, NULL, MFS_ENABLED); // #0
1232 _InsertMenuItemW(hpopup, 1, TRUE, 1, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); // #1
1233 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1234 DestroyMenu(hpopup);
1235 }
1236
1237 return S_OK;
1238 }
1239
1240 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1241 return S_OK;
1242
1243 return Shell_DefaultContextMenuCallBack(this, pdtobj);
1244 }