[NTOBJSHEX]
[reactos.git] / reactos / dll / shellext / ntobjshex / foldercommon.h
1 /*
2 * PROJECT: ReactOS shell extensions
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/ntobjshex/ntobjfolder.h
5 * PURPOSE: NT Object Namespace shell extension
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 */
8 #pragma once
9
10 extern const GUID CLSID_NtObjectFolder;
11
12 class CFolderViewCB :
13 public CComObjectRootEx<CComMultiThreadModelNoCS>,
14 public IShellFolderViewCB
15 {
16 IShellView* m_View;
17
18 public:
19
20 CFolderViewCB() : m_View(NULL) {}
21 virtual ~CFolderViewCB() {}
22
23 virtual HRESULT STDMETHODCALLTYPE MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
24 {
25 switch (uMsg)
26 {
27 case SFVM_DEFVIEWMODE:
28 {
29 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*)lParam;
30 *pViewMode = FVM_DETAILS;
31 return S_OK;
32 }
33 case SFVM_COLUMNCLICK:
34 return S_FALSE;
35 case SFVM_BACKGROUNDENUM:
36 return S_OK;
37 }
38
39 DbgPrint("MessageSFVCB unimplemented %d %08x %08x\n", uMsg, wParam, lParam);
40 return E_NOTIMPL;
41 }
42
43 virtual HRESULT STDMETHODCALLTYPE Initialize(IShellView* psv)
44 {
45 m_View = psv;
46 return S_OK;
47 }
48
49 DECLARE_NOT_AGGREGATABLE(CFolderViewCB)
50 DECLARE_PROTECT_FINAL_CONSTRUCT()
51
52 BEGIN_COM_MAP(CFolderViewCB)
53 COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB)
54 END_COM_MAP()
55 };
56
57 template<class TSelf, typename TItemId, class TExtractIcon>
58 class CCommonFolder :
59 public CComObjectRootEx<CComMultiThreadModelNoCS>,
60 public IShellFolder2,
61 public IPersistFolder2
62 {
63 protected:
64 WCHAR m_NtPath[MAX_PATH];
65
66 LPITEMIDLIST m_shellPidl;
67
68 public:
69
70 CCommonFolder() :
71 m_shellPidl(NULL)
72 {
73 }
74
75 virtual ~CCommonFolder()
76 {
77 if (m_shellPidl)
78 ILFree(m_shellPidl);
79 }
80
81 // IShellFolder
82 virtual HRESULT STDMETHODCALLTYPE ParseDisplayName(
83 HWND hwndOwner,
84 LPBC pbcReserved,
85 LPOLESTR lpszDisplayName,
86 ULONG *pchEaten,
87 LPITEMIDLIST *ppidl,
88 ULONG *pdwAttributes)
89 {
90 if (!ppidl)
91 return E_POINTER;
92
93 if (pchEaten)
94 *pchEaten = 0;
95
96 if (pdwAttributes)
97 *pdwAttributes = 0;
98
99 TRACE("CCommonFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
100
101 const TItemId * info;
102 IEnumIDList * it;
103 HRESULT hr = EnumObjects(hwndOwner, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &it);
104 if (FAILED(hr))
105 return hr;
106
107 PWSTR end = StrChrW(lpszDisplayName, '\\');
108 int length = end ? end - lpszDisplayName : wcslen(lpszDisplayName);
109
110 while (TRUE)
111 {
112 hr = it->Next(1, ppidl, NULL);
113
114 if (FAILED(hr))
115 return hr;
116
117 if (hr != S_OK)
118 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
119
120 hr = GetInfoFromPidl(*ppidl, &info);
121 if (FAILED_UNEXPECTEDLY(hr))
122 return hr;
123
124 if (StrCmpNW(info->entryName, lpszDisplayName, length) == 0)
125 break;
126 }
127
128 // if has remaining path to parse (and the path didn't just terminate in a backslash)
129 if (end && wcslen(end) > 1)
130 {
131 CComPtr<IShellFolder> psfChild;
132 hr = BindToObject(*ppidl, pbcReserved, IID_PPV_ARG(IShellFolder, &psfChild));
133 if (FAILED_UNEXPECTEDLY(hr))
134 return hr;
135
136 LPITEMIDLIST child;
137 hr = psfChild->ParseDisplayName(hwndOwner, pbcReserved, end + 1, pchEaten, &child, pdwAttributes);
138 if (FAILED(hr))
139 return hr;
140
141 LPITEMIDLIST old = *ppidl;
142 *ppidl = ILCombine(old, child);
143 ILFree(old);
144
145 // Count the path separator
146 if (pchEaten)
147 (*pchEaten) += 1;
148 }
149 else
150 {
151 if (pdwAttributes)
152 *pdwAttributes = ConvertAttributes(info, pdwAttributes);
153 }
154
155 if (pchEaten)
156 *pchEaten += wcslen(info->entryName);
157
158 return S_OK;
159 }
160
161 virtual HRESULT STDMETHODCALLTYPE EnumObjects(
162 HWND hwndOwner,
163 SHCONTF grfFlags,
164 IEnumIDList **ppenumIDList) PURE;
165
166 virtual HRESULT STDMETHODCALLTYPE BindToObject(
167 LPCITEMIDLIST pidl,
168 LPBC pbcReserved,
169 REFIID riid,
170 void **ppvOut)
171 {
172 const TItemId * info;
173
174 if (IsEqualIID(riid, IID_IShellFolder))
175 {
176 HRESULT hr = GetInfoFromPidl(pidl, &info);
177 if (FAILED_UNEXPECTEDLY(hr))
178 return hr;
179
180 WCHAR path[MAX_PATH];
181
182 StringCbCopyW(path, _countof(path), m_NtPath);
183 PathAppendW(path, info->entryName);
184
185 LPITEMIDLIST first = ILCloneFirst(pidl);
186 LPCITEMIDLIST rest = ILGetNext(pidl);
187
188 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
189
190 if (IsSymLink(info))
191 {
192 return RedirectToSymLink(info, first, rest, pbcReserved, (IShellFolder**)ppvOut);
193 }
194
195 CComPtr<IShellFolder> psfChild;
196 hr = InternalBindToObject(path, info, first, rest, fullPidl, pbcReserved, &psfChild);
197
198 ILFree(fullPidl);
199 ILFree(first);
200
201 if (FAILED(hr))
202 return hr;
203
204 if (hr == S_FALSE)
205 return S_OK;
206
207 if (rest->mkid.cb > 0)
208 {
209 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
210 }
211
212 return psfChild->QueryInterface(riid, ppvOut);
213 }
214
215 return E_NOTIMPL;
216 }
217
218 protected:
219 virtual HRESULT STDMETHODCALLTYPE InternalBindToObject(
220 PWSTR path,
221 const TItemId * info,
222 LPITEMIDLIST first,
223 LPCITEMIDLIST rest,
224 LPITEMIDLIST fullPidl,
225 LPBC pbcReserved,
226 IShellFolder** ppsfChild) PURE;
227
228 public:
229 virtual HRESULT STDMETHODCALLTYPE RedirectToSymLink(
230 const TItemId * info,
231 LPITEMIDLIST first,
232 LPCITEMIDLIST rest,
233 LPBC pbcReserved,
234 IShellFolder ** ppsfChild)
235 {
236 return E_NOTIMPL;
237 }
238
239 virtual HRESULT STDMETHODCALLTYPE BindToStorage(
240 LPCITEMIDLIST pidl,
241 LPBC pbcReserved,
242 REFIID riid,
243 void **ppvObj)
244 {
245 UNIMPLEMENTED;
246 return E_NOTIMPL;
247 }
248
249 virtual HRESULT STDMETHODCALLTYPE CompareIDs(
250 LPARAM lParam,
251 LPCITEMIDLIST pidl1,
252 LPCITEMIDLIST pidl2)
253 {
254 HRESULT hr;
255
256 TRACE("CompareIDs %d\n", lParam);
257
258 const TItemId * id1;
259 hr = GetInfoFromPidl(pidl1, &id1);
260 if (FAILED(hr))
261 return E_INVALIDARG;
262
263 const TItemId * id2;
264 hr = GetInfoFromPidl(pidl2, &id2);
265 if (FAILED(hr))
266 return E_INVALIDARG;
267
268 hr = CompareIDs(lParam, id1, id2);
269 if (hr != S_EQUAL)
270 return hr;
271
272 // The wollowing snipped is basically SHELL32_CompareChildren
273
274 PUIDLIST_RELATIVE rest1 = ILGetNext(pidl1);
275 PUIDLIST_RELATIVE rest2 = ILGetNext(pidl2);
276
277 bool isEmpty1 = (rest1->mkid.cb == 0);
278 bool isEmpty2 = (rest2->mkid.cb == 0);
279
280 if (isEmpty1 || isEmpty1)
281 return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
282
283 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
284 if (!first1)
285 return E_OUTOFMEMORY;
286
287 CComPtr<IShellFolder> psfNext;
288 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
289 if (FAILED_UNEXPECTEDLY(hr))
290 return hr;
291
292 return psfNext->CompareIDs(lParam, rest1, rest2);
293 }
294
295 protected:
296 virtual HRESULT STDMETHODCALLTYPE CompareName(
297 LPARAM lParam,
298 const TItemId * first,
299 const TItemId * second)
300 {
301 bool f1 = IsFolder(first);
302 bool f2 = IsFolder(second);
303
304 HRESULT hr = MAKE_COMPARE_HRESULT(f2 - f1);
305 if (hr != S_EQUAL)
306 return hr;
307
308 bool canonical = (lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY;
309 if (canonical)
310 {
311 // Shortcut: avoid comparing contents if not necessary when the results are not for display.
312 hr = MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
313 if (hr != S_EQUAL)
314 return hr;
315
316 int minlength = min(first->entryNameLength, second->entryNameLength);
317 if (minlength > 0)
318 {
319 hr = MAKE_COMPARE_HRESULT(memcmp(first->entryName, second->entryName, minlength));
320 if (hr != S_EQUAL)
321 return hr;
322 }
323
324 return S_EQUAL;
325 }
326
327 int minlength = min(first->entryNameLength, second->entryNameLength);
328 if (minlength > 0)
329 {
330 hr = MAKE_COMPARE_HRESULT(StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR)));
331 if (hr != S_EQUAL)
332 return hr;
333 }
334
335 return MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
336 }
337
338 public:
339 virtual HRESULT STDMETHODCALLTYPE CreateViewObject(
340 HWND hwndOwner,
341 REFIID riid,
342 void **ppvOut)
343 {
344 if (!IsEqualIID(riid, IID_IShellView))
345 return E_NOINTERFACE;
346
347 _CComObject<CFolderViewCB> *pcb;
348
349 HRESULT hr = _CComObject<CFolderViewCB>::CreateInstance(&pcb);
350 if (FAILED(hr))
351 return hr;
352
353 pcb->AddRef();
354
355 SFV_CREATE sfv;
356 sfv.cbSize = sizeof(sfv);
357 sfv.pshf = this;
358 sfv.psvOuter = NULL;
359 sfv.psfvcb = pcb;
360
361 IShellView* view;
362
363 hr = SHCreateShellFolderView(&sfv, &view);
364 if (FAILED(hr))
365 return hr;
366
367 pcb->Initialize(view);
368
369 pcb->Release();
370
371 *ppvOut = view;
372
373 return S_OK;
374 }
375
376 virtual HRESULT STDMETHODCALLTYPE GetAttributesOf(
377 UINT cidl,
378 PCUITEMID_CHILD_ARRAY apidl,
379 SFGAOF *rgfInOut)
380 {
381 const TItemId * info;
382
383 TRACE("GetAttributesOf %d\n", cidl);
384
385 if (cidl == 0)
386 {
387 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
388 return S_OK;
389 }
390
391 for (int i = 0; i < (int)cidl; i++)
392 {
393 PCUITEMID_CHILD pidl = apidl[i];
394
395 HRESULT hr = GetInfoFromPidl(pidl, &info);
396 if (FAILED_UNEXPECTEDLY(hr))
397 return hr;
398
399 // Update attributes.
400 *rgfInOut = ConvertAttributes(info, rgfInOut);
401 }
402
403 return S_OK;
404 }
405
406 virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf(
407 HWND hwndOwner,
408 UINT cidl,
409 PCUITEMID_CHILD_ARRAY apidl,
410 REFIID riid,
411 UINT *prgfInOut,
412 void **ppvOut)
413 {
414 DWORD res;
415 TRACE("GetUIObjectOf\n");
416
417 if (IsEqualIID(riid, IID_IContextMenu) ||
418 IsEqualIID(riid, IID_IContextMenu2) ||
419 IsEqualIID(riid, IID_IContextMenu3))
420 {
421 CComPtr<IContextMenu> pcm;
422
423 HKEY keys[1];
424
425 int nkeys = _countof(keys);
426 if (cidl == 1 && IsFolder(apidl[0]))
427 {
428 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
429 if (!NT_SUCCESS(res))
430 return HRESULT_FROM_NT(res);
431 }
432 else
433 {
434 nkeys = 0;
435 }
436
437 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
438 if (FAILED_UNEXPECTEDLY(hr))
439 return hr;
440
441 return pcm->QueryInterface(riid, ppvOut);
442 }
443
444 if (IsEqualIID(riid, IID_IExtractIconW))
445 {
446 return ShellObjectCreatorInit<TExtractIcon>(m_NtPath, m_shellPidl, cidl, apidl, riid, ppvOut);
447 }
448
449 if (IsEqualIID(riid, IID_IDataObject))
450 {
451 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**)ppvOut);
452 }
453
454 if (IsEqualIID(riid, IID_IQueryAssociations))
455 {
456 if (cidl == 1 && IsFolder(apidl[0]))
457 {
458 CComPtr<IQueryAssociations> pqa;
459 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
460 if (FAILED_UNEXPECTEDLY(hr))
461 return hr;
462
463 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
464 if (FAILED_UNEXPECTEDLY(hr))
465 return hr;
466
467 return pqa->QueryInterface(riid, ppvOut);
468 }
469 }
470
471 return E_NOTIMPL;
472 }
473
474 virtual HRESULT STDMETHODCALLTYPE GetDisplayNameOf(
475 LPCITEMIDLIST pidl,
476 SHGDNF uFlags,
477 STRRET *lpName)
478 {
479 const TItemId * info;
480
481 TRACE("GetDisplayNameOf %p\n", pidl);
482
483 HRESULT hr = GetInfoFromPidl(pidl, &info);
484 if (FAILED_UNEXPECTEDLY(hr))
485 return hr;
486
487 if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
488 {
489 hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
490 if (FAILED_UNEXPECTEDLY(hr))
491 return hr;
492 }
493
494 WCHAR path[MAX_PATH] = { 0 };
495
496 if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
497 {
498 if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
499 {
500 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
501 if (FAILED_UNEXPECTEDLY(hr))
502 return hr;
503 }
504 }
505
506 PathAppendW(path, info->entryName);
507
508 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
509 if (pidlNext && pidlNext->mkid.cb > 0)
510 {
511 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
512
513 CComPtr<IShellFolder> psfChild;
514 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
515 if (FAILED_UNEXPECTEDLY(hr))
516 return hr;
517
518 WCHAR temp[MAX_PATH];
519 STRRET childName;
520
521 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
522 if (FAILED_UNEXPECTEDLY(hr))
523 return hr;
524
525 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
526 if (FAILED_UNEXPECTEDLY(hr))
527 return hr;
528
529 PathAppendW(path, temp);
530
531 ILFree(pidlFirst);
532 }
533
534 hr = MakeStrRetFromString(path, lpName);
535 if (FAILED_UNEXPECTEDLY(hr))
536 return hr;
537
538 return S_OK;
539 }
540
541 virtual HRESULT STDMETHODCALLTYPE SetNameOf(
542 HWND hwnd,
543 LPCITEMIDLIST pidl,
544 LPCOLESTR lpszName,
545 SHGDNF uFlags,
546 LPITEMIDLIST *ppidlOut)
547 {
548 UNIMPLEMENTED;
549 return E_NOTIMPL;
550 }
551
552 // IShellFolder2
553 virtual HRESULT STDMETHODCALLTYPE GetDefaultSearchGUID(
554 GUID *lpguid)
555 {
556 UNIMPLEMENTED;
557 return E_NOTIMPL;
558 }
559
560 virtual HRESULT STDMETHODCALLTYPE EnumSearches(
561 IEnumExtraSearch **ppenum)
562 {
563 UNIMPLEMENTED;
564 return E_NOTIMPL;
565 }
566
567 virtual HRESULT STDMETHODCALLTYPE GetDefaultColumn(
568 DWORD dwReserved,
569 ULONG *pSort,
570 ULONG *pDisplay)
571 {
572 if (pSort)
573 *pSort = 0;
574 if (pDisplay)
575 *pDisplay = 0;
576 return S_OK;
577 }
578
579 virtual HRESULT STDMETHODCALLTYPE GetDefaultColumnState(
580 UINT iColumn,
581 SHCOLSTATEF *pcsFlags) PURE;
582
583 virtual HRESULT STDMETHODCALLTYPE GetDetailsEx(
584 LPCITEMIDLIST pidl,
585 const SHCOLUMNID *pscid,
586 VARIANT *pv) PURE;
587
588 virtual HRESULT STDMETHODCALLTYPE GetDetailsOf(
589 LPCITEMIDLIST pidl,
590 UINT iColumn,
591 SHELLDETAILS *psd) PURE;
592
593 virtual HRESULT STDMETHODCALLTYPE MapColumnToSCID(
594 UINT iColumn,
595 SHCOLUMNID *pscid) PURE;
596
597 // IPersist
598 virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *lpClassId)
599 {
600 if (!lpClassId)
601 return E_POINTER;
602
603 *lpClassId = CLSID_NtObjectFolder;
604 return S_OK;
605 }
606
607 // IPersistFolder
608 virtual HRESULT STDMETHODCALLTYPE Initialize(LPCITEMIDLIST pidl)
609 {
610 m_shellPidl = ILClone(pidl);
611
612 StringCbCopy(m_NtPath, _countof(m_NtPath), L"\\");
613
614 return S_OK;
615 }
616
617 // IPersistFolder2
618 virtual HRESULT STDMETHODCALLTYPE GetCurFolder(LPITEMIDLIST * pidl)
619 {
620 if (pidl)
621 *pidl = ILClone(m_shellPidl);
622 if (!m_shellPidl)
623 return S_FALSE;
624 return S_OK;
625 }
626
627 // Internal
628 protected:
629 virtual HRESULT STDMETHODCALLTYPE CompareIDs(
630 LPARAM lParam,
631 const TItemId * first,
632 const TItemId * second) PURE;
633
634 virtual ULONG STDMETHODCALLTYPE ConvertAttributes(
635 const TItemId * entry,
636 PULONG inMask) PURE;
637
638 virtual BOOL STDMETHODCALLTYPE IsFolder(LPCITEMIDLIST pcidl) PURE;
639
640 virtual BOOL STDMETHODCALLTYPE IsFolder(const TItemId * info) PURE;
641
642 virtual BOOL STDMETHODCALLTYPE IsSymLink(const TItemId * info)
643 {
644 return FALSE;
645 }
646
647 virtual HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const TItemId ** pentry) PURE;
648
649 public:
650 static HRESULT CALLBACK DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
651 {
652 switch (uMsg)
653 {
654 case DFM_MERGECONTEXTMENU:
655 return S_OK;
656 case DFM_INVOKECOMMAND:
657 case DFM_INVOKECOMMANDEX:
658 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
659 return S_FALSE;
660 }
661 return E_NOTIMPL;
662 }
663
664 DECLARE_REGISTRY_RESOURCEID(IDR_REGISTRYFOLDER)
665 DECLARE_NOT_AGGREGATABLE(TSelf)
666 DECLARE_PROTECT_FINAL_CONSTRUCT()
667
668 BEGIN_COM_MAP(TSelf)
669 COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
670 COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
671 COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
672 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
673 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
674 END_COM_MAP()
675
676 };