[SHELLEXTS] Improve the FILE header section. Brought to you by Adam Stachowicz. CORE...
[reactos.git] / reactos / dll / shellext / ntobjshex / ntobjfolder.cpp
1 /*
2 * PROJECT: ReactOS shell extensions
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/ntobjshex/ntobjfolder.cpp
5 * PURPOSE: NT Object Namespace shell extension
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 */
8
9 #include "precomp.h"
10 #include "ntobjenum.h"
11 #include <ntquery.h>
12 #include "util.h"
13
14 #define DFM_MERGECONTEXTMENU 1 // uFlags LPQCMINFO
15 #define DFM_INVOKECOMMAND 2 // idCmd pszArgs
16 #define DFM_INVOKECOMMANDEX 12 // idCmd PDFMICS
17 #define DFM_GETDEFSTATICID 14 // idCmd * 0
18
19 #define SHCIDS_ALLFIELDS 0x80000000L
20 #define SHCIDS_CANONICALONLY 0x10000000L
21
22 #define GET_SHGDN_FOR(dwFlags) ((DWORD)dwFlags & (DWORD)0x0000FF00)
23 #define GET_SHGDN_RELATION(dwFlags) ((DWORD)dwFlags & (DWORD)0x000000FF)
24
25 WINE_DEFAULT_DEBUG_CHANNEL(ntobjshex);
26
27 // {845B0FB2-66E0-416B-8F91-314E23F7C12D}
28 const GUID CLSID_NtObjectFolder = { 0x845b0fb2, 0x66e0, 0x416b, { 0x8f, 0x91, 0x31, 0x4e, 0x23, 0xf7, 0xc1, 0x2d } };
29
30 // {F4C430C3-3A8D-4B56-A018-E598DA60C2E0}
31 static const GUID GUID_NtObjectColumns = { 0xf4c430c3, 0x3a8d, 0x4b56, { 0xa0, 0x18, 0xe5, 0x98, 0xda, 0x60, 0xc2, 0xe0 } };
32
33 enum NtObjectColumns
34 {
35 NTOBJECT_COLUMN_NAME = 0,
36 NTOBJECT_COLUMN_TYPE,
37 NTOBJECT_COLUMN_LINKTARGET,
38 NTOBJECT_COLUMN_END
39 };
40
41 class CNtObjectFolderExtractIcon :
42 public CComObjectRootEx<CComMultiThreadModelNoCS>,
43 public IExtractIconW
44 {
45 PCITEMID_CHILD m_pcidlChild;
46 LPCWSTR m_NtPath;
47
48 public:
49 CNtObjectFolderExtractIcon() :
50 m_pcidlChild(NULL), m_NtPath(NULL)
51 {
52
53 }
54
55 virtual ~CNtObjectFolderExtractIcon()
56 {
57 if (m_pcidlChild)
58 ILFree((LPITEMIDLIST) m_pcidlChild);
59 }
60
61 HRESULT Initialize(LPCWSTR ntPath, UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
62 {
63 m_NtPath = ntPath;
64 if (cidl != 1)
65 return E_INVALIDARG;
66 m_pcidlChild = ILClone(apidl[0]);
67 return S_OK;
68 }
69
70 virtual HRESULT STDMETHODCALLTYPE GetIconLocation(
71 UINT uFlags,
72 LPWSTR szIconFile,
73 UINT cchMax,
74 INT *piIndex,
75 UINT *pwFlags)
76 {
77 const NtPidlEntry * entry = (NtPidlEntry *) m_pcidlChild;
78
79 if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC))
80 return E_INVALIDARG;
81
82 UINT flags = 0;
83
84 switch (entry->objectType)
85 {
86 case DIRECTORY_OBJECT:
87 case SYMBOLICLINK_OBJECT:
88 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
89 *piIndex = -((uFlags & GIL_OPENICON) ? IDI_NTOBJECTDIROPEN : IDI_NTOBJECTDIR);
90 *pwFlags = flags;
91 return S_OK;
92 case DEVICE_OBJECT:
93 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
94 *piIndex = -IDI_NTOBJECTDEVICE;
95 *pwFlags = flags;
96 return S_OK;
97 case PORT_OBJECT:
98 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
99 *piIndex = -IDI_NTOBJECTPORT;
100 *pwFlags = flags;
101 return S_OK;
102 case KEY_OBJECT:
103 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
104 *piIndex = -IDI_REGISTRYKEY;
105 *pwFlags = flags;
106 return S_OK;
107 default:
108 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
109 *piIndex = -IDI_NTOBJECTITEM;
110 *pwFlags = flags;
111 return S_OK;
112 }
113 }
114
115 virtual HRESULT STDMETHODCALLTYPE Extract(
116 LPCWSTR pszFile,
117 UINT nIconIndex,
118 HICON *phiconLarge,
119 HICON *phiconSmall,
120 UINT nIconSize)
121 {
122 return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize);
123 }
124
125 DECLARE_NOT_AGGREGATABLE(CNtObjectFolderExtractIcon)
126 DECLARE_PROTECT_FINAL_CONSTRUCT()
127
128 BEGIN_COM_MAP(CNtObjectFolderExtractIcon)
129 COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
130 END_COM_MAP()
131
132 };
133
134 class CNtObjectPidlHelper
135 {
136 public:
137 static HRESULT CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second)
138 {
139 if ((lParam & 0xFFFF0000) == SHCIDS_ALLFIELDS)
140 {
141 if (lParam != 0)
142 return E_INVALIDARG;
143
144 int minsize = min(first->cb, second->cb);
145 int ord = memcmp(second, first, minsize);
146
147 if (ord != 0)
148 return MAKE_HRESULT(0, 0, (USHORT) ord);
149
150 if (second->cb > first->cb)
151 return MAKE_HRESULT(0, 0, (USHORT) 1);
152 if (second->cb < first->cb)
153 return MAKE_HRESULT(0, 0, (USHORT) -1);
154 }
155 else
156 {
157 bool canonical = ((lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY);
158
159 switch (lParam & 0xFFFF)
160 {
161 case NTOBJECT_COLUMN_NAME:
162 {
163 bool f1 = (first->objectType == KEY_OBJECT) || (first->objectType == DIRECTORY_OBJECT);
164 bool f2 = (second->objectType == KEY_OBJECT) || (second->objectType == DIRECTORY_OBJECT);
165
166 if (f1 && !f2)
167 return MAKE_HRESULT(0, 0, (USHORT) -1);
168 if (f2 && !f1)
169 return MAKE_HRESULT(0, 0, (USHORT) 1);
170
171 if (canonical)
172 {
173 // Shortcut: avoid comparing contents if not necessary when the results are not for display.
174 if (second->entryNameLength > first->entryNameLength)
175 return MAKE_HRESULT(0, 0, (USHORT) 1);
176 if (second->entryNameLength < first->entryNameLength)
177 return MAKE_HRESULT(0, 0, (USHORT) -1);
178
179 int minlength = min(first->entryNameLength, second->entryNameLength);
180 if (minlength > 0)
181 {
182 int ord = memcmp(first->entryName, second->entryName, minlength);
183 if (ord != 0)
184 return MAKE_HRESULT(0, 0, (USHORT) ord);
185 }
186 return S_OK;
187 }
188 else
189 {
190 int minlength = min(first->entryNameLength, second->entryNameLength);
191 if (minlength > 0)
192 {
193 int ord = StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR));
194 if (ord != 0)
195 return MAKE_HRESULT(0, 0, (USHORT) ord);
196 }
197
198 if (second->entryNameLength > first->entryNameLength)
199 return MAKE_HRESULT(0, 0, (USHORT) 1);
200 if (second->entryNameLength < first->entryNameLength)
201 return MAKE_HRESULT(0, 0, (USHORT) -1);
202
203 return S_OK;
204 }
205 }
206 case NTOBJECT_COLUMN_TYPE:
207 {
208 int ord = second->objectType - first->objectType;
209 if (ord > 0)
210 return MAKE_HRESULT(0, 0, (USHORT) 1);
211 if (ord < 0)
212 return MAKE_HRESULT(0, 0, (USHORT) -1);
213
214 return S_OK;
215 }
216 case NTOBJECT_COLUMN_LINKTARGET:
217 {
218 // Can't sort by value
219 return E_INVALIDARG;
220 }
221 default:
222 {
223 DbgPrint("Unsupported sorting mode.\n");
224 return E_INVALIDARG;
225 }
226 }
227 }
228
229 return E_INVALIDARG;
230 }
231
232 static HRESULT CompareIDs(LPARAM lParam, const NtPidlEntry * first, LPCITEMIDLIST pcidl)
233 {
234 LPCITEMIDLIST p = pcidl;
235 NtPidlEntry * second = (NtPidlEntry*) &(p->mkid);
236 if ((second->cb < sizeof(NtPidlEntry)) || (second->magic != NT_OBJECT_PIDL_MAGIC))
237 return E_INVALIDARG;
238
239 return CompareIDs(lParam, first, second);
240 }
241
242 static HRESULT CompareIDs(LPARAM lParam, LPCITEMIDLIST pcidl1, LPCITEMIDLIST pcidl2)
243 {
244 LPCITEMIDLIST p = pcidl1;
245 NtPidlEntry * first = (NtPidlEntry*) &(p->mkid);
246 if ((first->cb < sizeof(NtPidlEntry)) || (first->magic != NT_OBJECT_PIDL_MAGIC))
247 return E_INVALIDARG;
248
249 return CompareIDs(lParam, first, pcidl2);
250 }
251
252 static ULONG ConvertAttributes(const NtPidlEntry * entry, PULONG inMask)
253 {
254 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
255 ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK;
256
257 if (entry->objectType == DIRECTORY_OBJECT)
258 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
259
260 if (entry->objectType == SYMBOLICLINK_OBJECT)
261 flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
262
263 if (entry->objectType == KEY_OBJECT)
264 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
265
266 return flags & mask;
267 }
268
269 static BOOL IsFolder(LPCITEMIDLIST pcidl)
270 {
271 NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid);
272 if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC))
273 return FALSE;
274
275 return (entry->objectType == DIRECTORY_OBJECT) ||
276 (entry->objectType == SYMBOLICLINK_OBJECT) ||
277 (entry->objectType == KEY_OBJECT);
278 }
279
280 static HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const NtPidlEntry ** pentry)
281 {
282 NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid);
283
284 if (entry->cb < sizeof(NtPidlEntry))
285 {
286 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(NtPidlEntry));
287 return E_INVALIDARG;
288 }
289
290 if (entry->magic != NT_OBJECT_PIDL_MAGIC)
291 {
292 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, NT_OBJECT_PIDL_MAGIC);
293 return E_INVALIDARG;
294 }
295
296 *pentry = entry;
297 return S_OK;
298 }
299 };
300
301 //-----------------------------------------------------------------------------
302 // CNtObjectFolder
303
304 CNtObjectFolder::CNtObjectFolder() :
305 m_shellPidl(NULL)
306 {
307 }
308
309 CNtObjectFolder::~CNtObjectFolder()
310 {
311 if (m_shellPidl)
312 ILFree(m_shellPidl);
313 }
314
315 // IShellFolder
316 HRESULT STDMETHODCALLTYPE CNtObjectFolder::ParseDisplayName(
317 HWND hwndOwner,
318 LPBC pbcReserved,
319 LPOLESTR lpszDisplayName,
320 ULONG *pchEaten,
321 LPITEMIDLIST *ppidl,
322 ULONG *pdwAttributes)
323 {
324 if (!ppidl)
325 return E_POINTER;
326
327 if (pchEaten)
328 *pchEaten = 0;
329
330 if (pdwAttributes)
331 *pdwAttributes = 0;
332
333 TRACE("CNtObjectFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
334
335 const NtPidlEntry * info;
336 IEnumIDList * it;
337 HRESULT hr = GetEnumNTDirectory(m_NtPath, &it);
338 if (FAILED(hr))
339 return hr;
340
341 while (TRUE)
342 {
343 hr = it->Next(1, ppidl, NULL);
344
345 if (FAILED(hr))
346 return hr;
347
348 if (hr != S_OK)
349 break;
350
351 hr = CNtObjectPidlHelper::GetInfoFromPidl(*ppidl, &info);
352 if (FAILED_UNEXPECTEDLY(hr))
353 return hr;
354
355 if (StrCmpW(info->entryName, lpszDisplayName) == 0)
356 break;
357 }
358
359 if (hr != S_OK)
360 {
361 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
362 }
363
364 if (pchEaten || pdwAttributes)
365 {
366 if (pchEaten)
367 *pchEaten = wcslen(info->entryName);
368
369 if (pdwAttributes)
370 *pdwAttributes = CNtObjectPidlHelper::ConvertAttributes(info, pdwAttributes);
371 }
372
373 return S_OK;
374 }
375
376 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects(
377 HWND hwndOwner,
378 SHCONTF grfFlags,
379 IEnumIDList **ppenumIDList)
380 {
381 return GetEnumNTDirectory(m_NtPath, ppenumIDList);
382 }
383
384 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToObject(
385 LPCITEMIDLIST pidl,
386 LPBC pbcReserved,
387 REFIID riid,
388 void **ppvOut)
389 {
390 const NtPidlEntry * info;
391
392 if (IsEqualIID(riid, IID_IShellFolder))
393 {
394 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
395 if (FAILED_UNEXPECTEDLY(hr))
396 return hr;
397
398 WCHAR path[MAX_PATH];
399
400 StringCbCopyW(path, _countof(path), m_NtPath);
401
402 PathAppendW(path, info->entryName);
403
404 LPITEMIDLIST first = ILCloneFirst(pidl);
405 LPCITEMIDLIST rest = ILGetNext(pidl);
406
407 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
408
409 if (info->objectType == SYMBOLICLINK_OBJECT)
410 {
411 WCHAR wbLink[MAX_PATH] = { 0 };
412 UNICODE_STRING link;
413 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
414
415 hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
416 if (FAILED_UNEXPECTEDLY(hr))
417 return hr;
418
419 if (link.Length > 0)
420 {
421 if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0]))
422 {
423 CComPtr<IShellFolder> psfDesktop;
424 hr = SHGetDesktopFolder(&psfDesktop);
425 if (FAILED_UNEXPECTEDLY(hr))
426 return hr;
427
428 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &first, NULL);
429 if (FAILED_UNEXPECTEDLY(hr))
430 return hr;
431
432 return psfDesktop->BindToObject(rest, pbcReserved, riid, ppvOut);
433 }
434
435 StringCbCopyW(path, _countof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
436 PathAppend(path, link.Buffer);
437
438 CComPtr<IShellFolder> psfDesktop;
439 hr = SHGetDesktopFolder(&psfDesktop);
440 if (FAILED_UNEXPECTEDLY(hr))
441 return hr;
442
443 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &first, NULL);
444 if (FAILED_UNEXPECTEDLY(hr))
445 return hr;
446 }
447 else
448 {
449 return E_UNEXPECTED;
450 }
451 }
452
453 CComPtr<IShellFolder> psfChild;
454
455 if (info->objectType == KEY_OBJECT)
456 {
457 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, &psfChild));
458 }
459 else
460 {
461 hr = ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, &psfChild));
462 }
463
464 ILFree(fullPidl);
465 ILFree(first);
466
467 if (rest->mkid.cb > 0)
468 {
469 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
470 }
471
472 return psfChild->QueryInterface(riid, ppvOut);
473 }
474
475 return E_NOTIMPL;
476 }
477
478 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToStorage(
479 LPCITEMIDLIST pidl,
480 LPBC pbcReserved,
481 REFIID riid,
482 void **ppvObj)
483 {
484 UNIMPLEMENTED;
485 return E_NOTIMPL;
486 }
487
488 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CompareIDs(
489 LPARAM lParam,
490 LPCITEMIDLIST pidl1,
491 LPCITEMIDLIST pidl2)
492 {
493 TRACE("CompareIDs\n");
494
495 HRESULT hr = CNtObjectPidlHelper::CompareIDs(lParam, pidl1, pidl2);
496 if (hr != S_OK)
497 return hr;
498
499 LPCITEMIDLIST rest1 = ILGetNext(pidl1);
500 LPCITEMIDLIST rest2 = ILGetNext(pidl2);
501
502 bool hasNext1 = (rest1->mkid.cb > 0);
503 bool hasNext2 = (rest2->mkid.cb > 0);
504
505 if (hasNext1 || hasNext2)
506 {
507 if (hasNext1 && !hasNext2)
508 return MAKE_HRESULT(0, 0, (USHORT) -1);
509
510 if (hasNext2 && !hasNext1)
511 return MAKE_HRESULT(0, 0, (USHORT) 1);
512
513 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
514
515 CComPtr<IShellFolder> psfNext;
516 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
517 if (FAILED_UNEXPECTEDLY(hr))
518 return hr;
519
520 return psfNext->CompareIDs(lParam, rest1, rest2);
521 }
522
523 return S_OK;
524 }
525
526 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CreateViewObject(
527 HWND hwndOwner,
528 REFIID riid,
529 void **ppvOut)
530 {
531 if (!IsEqualIID(riid, IID_IShellView))
532 return E_NOINTERFACE;
533
534 SFV_CREATE sfv;
535 sfv.cbSize = sizeof(sfv);
536 sfv.pshf = this;
537 sfv.psvOuter = NULL;
538 sfv.psfvcb = this;
539
540 return SHCreateShellFolderView(&sfv, (IShellView**) ppvOut);
541 }
542
543 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetAttributesOf(
544 UINT cidl,
545 PCUITEMID_CHILD_ARRAY apidl,
546 SFGAOF *rgfInOut)
547 {
548 const NtPidlEntry * info;
549
550 TRACE("GetAttributesOf %d\n", cidl);
551
552 if (cidl == 0)
553 {
554 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
555 return S_OK;
556 }
557
558 for (int i = 0; i < (int) cidl; i++)
559 {
560 PCUITEMID_CHILD pidl = apidl[i];
561
562 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
563 if (FAILED_UNEXPECTEDLY(hr))
564 return hr;
565
566 // Update attributes.
567 *rgfInOut = CNtObjectPidlHelper::ConvertAttributes(info, rgfInOut);
568 }
569
570 return S_OK;
571 }
572
573 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetUIObjectOf(
574 HWND hwndOwner,
575 UINT cidl,
576 PCUITEMID_CHILD_ARRAY apidl,
577 REFIID riid,
578 UINT *prgfInOut,
579 void **ppvOut)
580 {
581 DWORD res;
582 TRACE("GetUIObjectOf\n");
583
584 if (IsEqualIID(riid, IID_IContextMenu) ||
585 IsEqualIID(riid, IID_IContextMenu2) ||
586 IsEqualIID(riid, IID_IContextMenu3))
587 {
588 CComPtr<IContextMenu> pcm;
589
590 HKEY keys[1];
591
592 int nkeys = _countof(keys);
593 if (cidl == 1 && CNtObjectPidlHelper::IsFolder(apidl[0]))
594 {
595 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
596 if (!NT_SUCCESS(res))
597 return HRESULT_FROM_NT(res);
598 }
599 else
600 {
601 nkeys = 0;
602 }
603
604 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
605 if (FAILED_UNEXPECTEDLY(hr))
606 return hr;
607
608 return pcm->QueryInterface(riid, ppvOut);
609 }
610
611 if (IsEqualIID(riid, IID_IExtractIconW))
612 {
613 return ShellObjectCreatorInit<CNtObjectFolderExtractIcon>(m_NtPath, cidl, apidl, riid, ppvOut);
614 }
615
616 if (IsEqualIID(riid, IID_IDataObject))
617 {
618 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**) ppvOut);
619 }
620
621 if (IsEqualIID(riid, IID_IQueryAssociations))
622 {
623 if (cidl == 1 && CNtObjectPidlHelper::IsFolder(apidl[0]))
624 {
625 CComPtr<IQueryAssociations> pqa;
626 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
627 if (FAILED_UNEXPECTEDLY(hr))
628 return hr;
629
630 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
631 if (FAILED_UNEXPECTEDLY(hr))
632 return hr;
633
634 return pqa->QueryInterface(riid, ppvOut);
635 }
636 }
637
638 return E_NOTIMPL;
639 }
640
641 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDisplayNameOf(
642 LPCITEMIDLIST pidl,
643 SHGDNF uFlags,
644 STRRET *lpName)
645 {
646 const NtPidlEntry * info;
647
648 TRACE("GetDisplayNameOf %p\n", pidl);
649
650 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
651 if (FAILED_UNEXPECTEDLY(hr))
652 return hr;
653
654 if ((GET_SHGDN_RELATION(uFlags) == SHGDN_NORMAL) &&
655 (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING))
656 {
657 WCHAR path[MAX_PATH] = { 0 };
658
659 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
660 if (FAILED_UNEXPECTEDLY(hr))
661 return hr;
662
663 PathAppendW(path, info->entryName);
664
665 hr = MakeStrRetFromString(path, lpName);
666 if (FAILED_UNEXPECTEDLY(hr))
667 return hr;
668
669 LPCITEMIDLIST pidlFirst = ILCloneFirst(pidl);
670 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
671
672 if (pidlNext && pidlNext->mkid.cb > 0)
673 {
674 CComPtr<IShellFolder> psfChild;
675 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
676 if (FAILED_UNEXPECTEDLY(hr))
677 return hr;
678
679 WCHAR temp[MAX_PATH];
680 STRRET childName;
681
682 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
683 if (FAILED_UNEXPECTEDLY(hr))
684 return hr;
685
686 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
687 if (FAILED_UNEXPECTEDLY(hr))
688 return hr;
689
690 PathAppendW(path, temp);
691 }
692
693 ILFree((LPITEMIDLIST) pidlFirst);
694 }
695 else
696 {
697 MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
698 }
699
700 return S_OK;
701 }
702
703 HRESULT STDMETHODCALLTYPE CNtObjectFolder::SetNameOf(
704 HWND hwnd,
705 LPCITEMIDLIST pidl,
706 LPCOLESTR lpszName,
707 SHGDNF uFlags,
708 LPITEMIDLIST *ppidlOut)
709 {
710 UNIMPLEMENTED;
711 return E_NOTIMPL;
712 }
713
714 // IPersist
715 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetClassID(CLSID *lpClassId)
716 {
717 if (!lpClassId)
718 return E_POINTER;
719
720 *lpClassId = CLSID_NtObjectFolder;
721 return S_OK;
722 }
723
724 // IPersistFolder
725 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl)
726 {
727 m_shellPidl = ILClone(pidl);
728
729 StringCbCopy(m_NtPath, _countof(m_NtPath), L"\\");
730
731 return S_OK;
732 }
733
734 // Internal
735 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath)
736 {
737 m_shellPidl = ILClone(pidl);
738
739 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
740
741 return S_OK;
742 }
743
744 // IPersistFolder2
745 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetCurFolder(LPITEMIDLIST * pidl)
746 {
747 if (pidl)
748 *pidl = ILClone(m_shellPidl);
749 if (!m_shellPidl)
750 return S_FALSE;
751 return S_OK;
752 }
753
754 // IShellFolder2
755 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultSearchGUID(
756 GUID *lpguid)
757 {
758 UNIMPLEMENTED;
759 return E_NOTIMPL;
760 }
761
762 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumSearches(
763 IEnumExtraSearch **ppenum)
764 {
765 UNIMPLEMENTED;
766 return E_NOTIMPL;
767 }
768
769 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumn(
770 DWORD dwReserved,
771 ULONG *pSort,
772 ULONG *pDisplay)
773 {
774 if (pSort)
775 *pSort = 0;
776 if (pDisplay)
777 *pDisplay = 0;
778 return S_OK;
779 }
780
781 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
782 UINT iColumn,
783 SHCOLSTATEF *pcsFlags)
784 {
785 switch (iColumn)
786 {
787 case NTOBJECT_COLUMN_NAME:
788 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
789 return S_OK;
790 case NTOBJECT_COLUMN_TYPE:
791 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
792 return S_OK;
793 case NTOBJECT_COLUMN_LINKTARGET:
794 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW;
795 return S_OK;
796 }
797
798 return E_INVALIDARG;
799 }
800
801 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
802 LPCITEMIDLIST pidl,
803 const SHCOLUMNID *pscid,
804 VARIANT *pv)
805 {
806 const NtPidlEntry * info;
807
808 TRACE("GetDetailsEx\n");
809
810 if (pidl)
811 {
812 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
813 if (FAILED_UNEXPECTEDLY(hr))
814 return hr;
815
816 static const GUID storage = PSGUID_STORAGE;
817 if (IsEqualGUID(pscid->fmtid, storage))
818 {
819 if (pscid->pid == PID_STG_NAME)
820 {
821 return MakeVariantString(pv, info->entryName);
822 }
823 else if (pscid->pid == PID_STG_STORAGETYPE)
824 {
825 if (info->objectType < 0)
826 {
827 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
828
829 if (td->typeNameLength > 0)
830 {
831 return MakeVariantString(pv, td->typeName);
832 }
833 else
834 {
835 return MakeVariantString(pv, L"Unknown");
836 }
837 }
838 else
839 {
840 return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
841 }
842 }
843 }
844 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
845 {
846 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT)
847 {
848 WCHAR wbLink[MAX_PATH] = { 0 };
849 UNICODE_STRING link;
850 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
851
852 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
853
854 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
855 {
856 return MakeVariantString(pv, link.Buffer);
857 }
858 }
859
860 V_VT(pv) = VT_EMPTY;
861 return S_OK;
862 }
863 }
864
865 return E_INVALIDARG;
866 }
867
868 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
869 LPCITEMIDLIST pidl,
870 UINT iColumn,
871 SHELLDETAILS *psd)
872 {
873 const NtPidlEntry * info;
874
875 TRACE("GetDetailsOf\n");
876
877 if (pidl)
878 {
879 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
880 if (FAILED_UNEXPECTEDLY(hr))
881 return hr;
882
883 switch (iColumn)
884 {
885 case NTOBJECT_COLUMN_NAME:
886 psd->fmt = LVCFMT_LEFT;
887
888 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
889 return S_OK;
890 case NTOBJECT_COLUMN_TYPE:
891 psd->fmt = LVCFMT_LEFT;
892
893 if (info->objectType < 0)
894 {
895 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
896
897 if (td->typeNameLength > 0)
898 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
899 else
900 MakeStrRetFromString(L"Unknown", &(psd->str));
901 }
902 else
903 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
904 return S_OK;
905 case NTOBJECT_COLUMN_LINKTARGET:
906 {
907 psd->fmt = LVCFMT_LEFT;
908
909 if (info->objectType == SYMBOLICLINK_OBJECT)
910 {
911 WCHAR wbLink[MAX_PATH] = { 0 };
912 UNICODE_STRING link;
913 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
914
915 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
916
917 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
918 {
919 MakeStrRetFromString(link.Buffer, link.Length, &(psd->str));
920 return S_OK;
921 }
922 }
923
924 MakeStrRetFromString(L"", &(psd->str));
925 return S_OK;
926 }
927 }
928 }
929 else
930 {
931 switch (iColumn)
932 {
933 case NTOBJECT_COLUMN_NAME:
934 psd->fmt = LVCFMT_LEFT;
935 psd->cxChar = 30;
936
937 // TODO: Make localizable
938 MakeStrRetFromString(L"Object Name", &(psd->str));
939 return S_OK;
940 case NTOBJECT_COLUMN_TYPE:
941 psd->fmt = LVCFMT_LEFT;
942 psd->cxChar = 20;
943
944 // TODO: Make localizable
945 MakeStrRetFromString(L"Object Type", &(psd->str));
946 return S_OK;
947 case NTOBJECT_COLUMN_LINKTARGET:
948 psd->fmt = LVCFMT_LEFT;
949 psd->cxChar = 30;
950
951 // TODO: Make localizable
952 MakeStrRetFromString(L"Symlink Target", &(psd->str));
953 return S_OK;
954 }
955 }
956
957 return E_INVALIDARG;
958 }
959
960 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
961 UINT iColumn,
962 SHCOLUMNID *pscid)
963 {
964 static const GUID storage = PSGUID_STORAGE;
965 switch (iColumn)
966 {
967 case NTOBJECT_COLUMN_NAME:
968 pscid->fmtid = storage;
969 pscid->pid = PID_STG_NAME;
970 return S_OK;
971 case NTOBJECT_COLUMN_TYPE:
972 pscid->fmtid = storage;
973 pscid->pid = PID_STG_STORAGETYPE;
974 return S_OK;
975 case NTOBJECT_COLUMN_LINKTARGET:
976 pscid->fmtid = GUID_NtObjectColumns;
977 pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
978 return S_OK;
979 }
980 return E_INVALIDARG;
981 }
982
983 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
984 {
985 switch (uMsg)
986 {
987 case SFVM_DEFVIEWMODE:
988 {
989 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
990 *pViewMode = FVM_DETAILS;
991 return S_OK;
992 }
993 case SFVM_COLUMNCLICK:
994 return S_FALSE;
995 case SFVM_BACKGROUNDENUM:
996 return S_OK;
997 }
998 return E_NOTIMPL;
999 }
1000
1001 HRESULT CNtObjectFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1002 {
1003 switch (uMsg)
1004 {
1005 case DFM_MERGECONTEXTMENU:
1006 return S_OK;
1007 case DFM_INVOKECOMMAND:
1008 case DFM_INVOKECOMMANDEX:
1009 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1010 return S_FALSE;
1011 }
1012 return E_NOTIMPL;
1013 }