[MPR]
[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 PWSTR end = StrChrW(lpszDisplayName, '\\');
342 int length = end ? end - lpszDisplayName : wcslen(lpszDisplayName);
343
344 while (TRUE)
345 {
346 hr = it->Next(1, ppidl, NULL);
347
348 if (FAILED(hr))
349 return hr;
350
351 if (hr != S_OK)
352 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
353
354 hr = CNtObjectPidlHelper::GetInfoFromPidl(*ppidl, &info);
355 if (FAILED_UNEXPECTEDLY(hr))
356 return hr;
357
358 if (StrCmpNW(info->entryName, lpszDisplayName, length) == 0)
359 break;
360 }
361
362 // if has remaining path to parse (and the path didn't just terminate in a backslash)
363 if (end && wcslen(end) > 1)
364 {
365 CComPtr<IShellFolder> psfChild;
366 hr = BindToObject(*ppidl, pbcReserved, IID_PPV_ARG(IShellFolder, &psfChild));
367 if (FAILED_UNEXPECTEDLY(hr))
368 return hr;
369
370 LPITEMIDLIST child;
371 hr = psfChild->ParseDisplayName(hwndOwner, pbcReserved, end + 1, pchEaten, &child, pdwAttributes);
372 if (FAILED(hr))
373 return hr;
374
375 LPITEMIDLIST old = *ppidl;
376 *ppidl = ILCombine(old, child);
377 ILFree(old);
378
379 // Count the path separator
380 if (pchEaten)
381 (*pchEaten) += 1;
382 }
383 else
384 {
385 if (pdwAttributes)
386 *pdwAttributes = CNtObjectPidlHelper::ConvertAttributes(info, pdwAttributes);
387 }
388
389 if (pchEaten)
390 *pchEaten += wcslen(info->entryName);
391
392 return S_OK;
393 }
394
395 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects(
396 HWND hwndOwner,
397 SHCONTF grfFlags,
398 IEnumIDList **ppenumIDList)
399 {
400 return GetEnumNTDirectory(m_NtPath, ppenumIDList);
401 }
402
403 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToObject(
404 LPCITEMIDLIST pidl,
405 LPBC pbcReserved,
406 REFIID riid,
407 void **ppvOut)
408 {
409 const NtPidlEntry * info;
410
411 if (IsEqualIID(riid, IID_IShellFolder))
412 {
413 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
414 if (FAILED_UNEXPECTEDLY(hr))
415 return hr;
416
417 WCHAR path[MAX_PATH];
418
419 StringCbCopyW(path, _countof(path), m_NtPath);
420 PathAppendW(path, info->entryName);
421
422 LPITEMIDLIST first = ILCloneFirst(pidl);
423 LPCITEMIDLIST rest = ILGetNext(pidl);
424
425 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
426
427 if (info->objectType == SYMBOLICLINK_OBJECT)
428 {
429 WCHAR wbLink[MAX_PATH] = { 0 };
430 UNICODE_STRING link;
431 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
432
433 hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
434 if (FAILED_UNEXPECTEDLY(hr))
435 return hr;
436
437 if (link.Length > 0)
438 {
439 if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0]))
440 {
441 CComPtr<IShellFolder> psfDesktop;
442 hr = SHGetDesktopFolder(&psfDesktop);
443 if (FAILED_UNEXPECTEDLY(hr))
444 return hr;
445
446 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &first, NULL);
447 if (FAILED_UNEXPECTEDLY(hr))
448 return hr;
449
450 return psfDesktop->BindToObject(rest, pbcReserved, riid, ppvOut);
451 }
452
453 StringCbCopyW(path, _countof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
454 PathAppend(path, link.Buffer);
455
456 CComPtr<IShellFolder> psfDesktop;
457 hr = SHGetDesktopFolder(&psfDesktop);
458 if (FAILED_UNEXPECTEDLY(hr))
459 return hr;
460
461 LPITEMIDLIST pidl;
462
463 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &pidl, NULL);
464 if (FAILED_UNEXPECTEDLY(hr))
465 return hr;
466
467 CComPtr<IShellFolder> psfChild;
468 hr = psfDesktop->BindToObject(pidl, NULL, riid, ppvOut);
469 ILFree(pidl);
470
471 return hr;
472 }
473 else
474 {
475 return E_UNEXPECTED;
476 }
477 }
478
479 CComPtr<IShellFolder> psfChild;
480
481 if (info->objectType == KEY_OBJECT)
482 {
483 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, &psfChild));
484 }
485 else
486 {
487 hr = ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, &psfChild));
488 }
489
490 ILFree(fullPidl);
491 ILFree(first);
492
493 if (rest->mkid.cb > 0)
494 {
495 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
496 }
497
498 return psfChild->QueryInterface(riid, ppvOut);
499 }
500
501 return E_NOTIMPL;
502 }
503
504 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToStorage(
505 LPCITEMIDLIST pidl,
506 LPBC pbcReserved,
507 REFIID riid,
508 void **ppvObj)
509 {
510 UNIMPLEMENTED;
511 return E_NOTIMPL;
512 }
513
514 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CompareIDs(
515 LPARAM lParam,
516 LPCITEMIDLIST pidl1,
517 LPCITEMIDLIST pidl2)
518 {
519 TRACE("CompareIDs\n");
520
521 HRESULT hr = CNtObjectPidlHelper::CompareIDs(lParam, pidl1, pidl2);
522 if (hr != S_OK)
523 return hr;
524
525 LPCITEMIDLIST rest1 = ILGetNext(pidl1);
526 LPCITEMIDLIST rest2 = ILGetNext(pidl2);
527
528 bool hasNext1 = (rest1->mkid.cb > 0);
529 bool hasNext2 = (rest2->mkid.cb > 0);
530
531 if (hasNext1 || hasNext2)
532 {
533 if (hasNext1 && !hasNext2)
534 return MAKE_HRESULT(0, 0, (USHORT) -1);
535
536 if (hasNext2 && !hasNext1)
537 return MAKE_HRESULT(0, 0, (USHORT) 1);
538
539 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
540
541 CComPtr<IShellFolder> psfNext;
542 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
543 if (FAILED_UNEXPECTEDLY(hr))
544 return hr;
545
546 return psfNext->CompareIDs(lParam, rest1, rest2);
547 }
548
549 return S_OK;
550 }
551
552 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CreateViewObject(
553 HWND hwndOwner,
554 REFIID riid,
555 void **ppvOut)
556 {
557 if (!IsEqualIID(riid, IID_IShellView))
558 return E_NOINTERFACE;
559
560 SFV_CREATE sfv;
561 sfv.cbSize = sizeof(sfv);
562 sfv.pshf = static_cast<IShellFolder*>(this);
563 sfv.psvOuter = NULL;
564 sfv.psfvcb = static_cast<IShellFolderViewCB*>(this);
565
566 return SHCreateShellFolderView(&sfv, (IShellView**) ppvOut);
567 }
568
569 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetAttributesOf(
570 UINT cidl,
571 PCUITEMID_CHILD_ARRAY apidl,
572 SFGAOF *rgfInOut)
573 {
574 const NtPidlEntry * info;
575
576 TRACE("GetAttributesOf %d\n", cidl);
577
578 if (cidl == 0)
579 {
580 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
581 return S_OK;
582 }
583
584 for (int i = 0; i < (int) cidl; i++)
585 {
586 PCUITEMID_CHILD pidl = apidl[i];
587
588 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
589 if (FAILED_UNEXPECTEDLY(hr))
590 return hr;
591
592 // Update attributes.
593 *rgfInOut = CNtObjectPidlHelper::ConvertAttributes(info, rgfInOut);
594 }
595
596 return S_OK;
597 }
598
599 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetUIObjectOf(
600 HWND hwndOwner,
601 UINT cidl,
602 PCUITEMID_CHILD_ARRAY apidl,
603 REFIID riid,
604 UINT *prgfInOut,
605 void **ppvOut)
606 {
607 DWORD res;
608 TRACE("GetUIObjectOf\n");
609
610 if (IsEqualIID(riid, IID_IContextMenu) ||
611 IsEqualIID(riid, IID_IContextMenu2) ||
612 IsEqualIID(riid, IID_IContextMenu3))
613 {
614 CComPtr<IContextMenu> pcm;
615
616 HKEY keys[1];
617
618 int nkeys = _countof(keys);
619 if (cidl == 1 && CNtObjectPidlHelper::IsFolder(apidl[0]))
620 {
621 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
622 if (!NT_SUCCESS(res))
623 return HRESULT_FROM_NT(res);
624 }
625 else
626 {
627 nkeys = 0;
628 }
629
630 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
631 if (FAILED_UNEXPECTEDLY(hr))
632 return hr;
633
634 return pcm->QueryInterface(riid, ppvOut);
635 }
636
637 if (IsEqualIID(riid, IID_IExtractIconW))
638 {
639 return ShellObjectCreatorInit<CNtObjectFolderExtractIcon>(m_NtPath, cidl, apidl, riid, ppvOut);
640 }
641
642 if (IsEqualIID(riid, IID_IDataObject))
643 {
644 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**) ppvOut);
645 }
646
647 if (IsEqualIID(riid, IID_IQueryAssociations))
648 {
649 if (cidl == 1 && CNtObjectPidlHelper::IsFolder(apidl[0]))
650 {
651 CComPtr<IQueryAssociations> pqa;
652 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
653 if (FAILED_UNEXPECTEDLY(hr))
654 return hr;
655
656 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
657 if (FAILED_UNEXPECTEDLY(hr))
658 return hr;
659
660 return pqa->QueryInterface(riid, ppvOut);
661 }
662 }
663
664 return E_NOTIMPL;
665 }
666
667 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDisplayNameOf(
668 LPCITEMIDLIST pidl,
669 SHGDNF uFlags,
670 STRRET *lpName)
671 {
672 const NtPidlEntry * info;
673
674 TRACE("GetDisplayNameOf %p\n", pidl);
675
676 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
677 if (FAILED_UNEXPECTEDLY(hr))
678 return hr;
679
680 if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
681 {
682 hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
683 if (FAILED_UNEXPECTEDLY(hr))
684 return hr;
685 }
686
687 WCHAR path[MAX_PATH] = { 0 };
688
689 if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
690 {
691 if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
692 {
693 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
694 if (FAILED_UNEXPECTEDLY(hr))
695 return hr;
696 }
697 }
698
699 PathAppendW(path, info->entryName);
700
701 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
702 if (pidlNext && pidlNext->mkid.cb > 0)
703 {
704 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
705
706 CComPtr<IShellFolder> psfChild;
707 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
708 if (FAILED_UNEXPECTEDLY(hr))
709 return hr;
710
711 WCHAR temp[MAX_PATH];
712 STRRET childName;
713
714 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
715 if (FAILED_UNEXPECTEDLY(hr))
716 return hr;
717
718 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
719 if (FAILED_UNEXPECTEDLY(hr))
720 return hr;
721
722 PathAppendW(path, temp);
723
724 ILFree(pidlFirst);
725 }
726
727 hr = MakeStrRetFromString(path, lpName);
728 if (FAILED_UNEXPECTEDLY(hr))
729 return hr;
730
731 return S_OK;
732 }
733
734 HRESULT STDMETHODCALLTYPE CNtObjectFolder::SetNameOf(
735 HWND hwnd,
736 LPCITEMIDLIST pidl,
737 LPCOLESTR lpszName,
738 SHGDNF uFlags,
739 LPITEMIDLIST *ppidlOut)
740 {
741 UNIMPLEMENTED;
742 return E_NOTIMPL;
743 }
744
745 // IPersist
746 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetClassID(CLSID *lpClassId)
747 {
748 if (!lpClassId)
749 return E_POINTER;
750
751 *lpClassId = CLSID_NtObjectFolder;
752 return S_OK;
753 }
754
755 // IPersistFolder
756 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl)
757 {
758 m_shellPidl = ILClone(pidl);
759
760 StringCbCopy(m_NtPath, _countof(m_NtPath), L"\\");
761
762 return S_OK;
763 }
764
765 // Internal
766 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath)
767 {
768 m_shellPidl = ILClone(pidl);
769
770 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
771
772 return S_OK;
773 }
774
775 // IPersistFolder2
776 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetCurFolder(LPITEMIDLIST * pidl)
777 {
778 if (pidl)
779 *pidl = ILClone(m_shellPidl);
780 if (!m_shellPidl)
781 return S_FALSE;
782 return S_OK;
783 }
784
785 // IShellFolder2
786 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultSearchGUID(
787 GUID *lpguid)
788 {
789 UNIMPLEMENTED;
790 return E_NOTIMPL;
791 }
792
793 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumSearches(
794 IEnumExtraSearch **ppenum)
795 {
796 UNIMPLEMENTED;
797 return E_NOTIMPL;
798 }
799
800 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumn(
801 DWORD dwReserved,
802 ULONG *pSort,
803 ULONG *pDisplay)
804 {
805 if (pSort)
806 *pSort = 0;
807 if (pDisplay)
808 *pDisplay = 0;
809 return S_OK;
810 }
811
812 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
813 UINT iColumn,
814 SHCOLSTATEF *pcsFlags)
815 {
816 switch (iColumn)
817 {
818 case NTOBJECT_COLUMN_NAME:
819 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
820 return S_OK;
821 case NTOBJECT_COLUMN_TYPE:
822 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
823 return S_OK;
824 case NTOBJECT_COLUMN_LINKTARGET:
825 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW;
826 return S_OK;
827 }
828
829 return E_INVALIDARG;
830 }
831
832 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
833 LPCITEMIDLIST pidl,
834 const SHCOLUMNID *pscid,
835 VARIANT *pv)
836 {
837 const NtPidlEntry * info;
838
839 TRACE("GetDetailsEx\n");
840
841 if (pidl)
842 {
843 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
844 if (FAILED_UNEXPECTEDLY(hr))
845 return hr;
846
847 static const GUID storage = PSGUID_STORAGE;
848 if (IsEqualGUID(pscid->fmtid, storage))
849 {
850 if (pscid->pid == PID_STG_NAME)
851 {
852 return MakeVariantString(pv, info->entryName);
853 }
854 else if (pscid->pid == PID_STG_STORAGETYPE)
855 {
856 if (info->objectType < 0)
857 {
858 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
859
860 if (td->typeNameLength > 0)
861 {
862 return MakeVariantString(pv, td->typeName);
863 }
864 else
865 {
866 return MakeVariantString(pv, L"Unknown");
867 }
868 }
869 else
870 {
871 return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
872 }
873 }
874 }
875 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
876 {
877 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT)
878 {
879 WCHAR wbLink[MAX_PATH] = { 0 };
880 UNICODE_STRING link;
881 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
882
883 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
884
885 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
886 {
887 return MakeVariantString(pv, link.Buffer);
888 }
889 }
890
891 V_VT(pv) = VT_EMPTY;
892 return S_OK;
893 }
894 }
895
896 return E_INVALIDARG;
897 }
898
899 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
900 LPCITEMIDLIST pidl,
901 UINT iColumn,
902 SHELLDETAILS *psd)
903 {
904 const NtPidlEntry * info;
905
906 TRACE("GetDetailsOf\n");
907
908 if (pidl)
909 {
910 HRESULT hr = CNtObjectPidlHelper::GetInfoFromPidl(pidl, &info);
911 if (FAILED_UNEXPECTEDLY(hr))
912 return hr;
913
914 switch (iColumn)
915 {
916 case NTOBJECT_COLUMN_NAME:
917 psd->fmt = LVCFMT_LEFT;
918
919 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
920 return S_OK;
921 case NTOBJECT_COLUMN_TYPE:
922 psd->fmt = LVCFMT_LEFT;
923
924 if (info->objectType < 0)
925 {
926 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
927
928 if (td->typeNameLength > 0)
929 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
930 else
931 MakeStrRetFromString(L"Unknown", &(psd->str));
932 }
933 else
934 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
935 return S_OK;
936 case NTOBJECT_COLUMN_LINKTARGET:
937 {
938 psd->fmt = LVCFMT_LEFT;
939
940 if (info->objectType == SYMBOLICLINK_OBJECT)
941 {
942 WCHAR wbLink[MAX_PATH] = { 0 };
943 UNICODE_STRING link;
944 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
945
946 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
947
948 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
949 {
950 MakeStrRetFromString(link.Buffer, link.Length, &(psd->str));
951 return S_OK;
952 }
953 }
954
955 MakeStrRetFromString(L"", &(psd->str));
956 return S_OK;
957 }
958 }
959 }
960 else
961 {
962 switch (iColumn)
963 {
964 case NTOBJECT_COLUMN_NAME:
965 psd->fmt = LVCFMT_LEFT;
966 psd->cxChar = 30;
967
968 // TODO: Make localizable
969 MakeStrRetFromString(L"Object Name", &(psd->str));
970 return S_OK;
971 case NTOBJECT_COLUMN_TYPE:
972 psd->fmt = LVCFMT_LEFT;
973 psd->cxChar = 20;
974
975 // TODO: Make localizable
976 MakeStrRetFromString(L"Object Type", &(psd->str));
977 return S_OK;
978 case NTOBJECT_COLUMN_LINKTARGET:
979 psd->fmt = LVCFMT_LEFT;
980 psd->cxChar = 30;
981
982 // TODO: Make localizable
983 MakeStrRetFromString(L"Symlink Target", &(psd->str));
984 return S_OK;
985 }
986 }
987
988 return E_INVALIDARG;
989 }
990
991 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
992 UINT iColumn,
993 SHCOLUMNID *pscid)
994 {
995 static const GUID storage = PSGUID_STORAGE;
996 switch (iColumn)
997 {
998 case NTOBJECT_COLUMN_NAME:
999 pscid->fmtid = storage;
1000 pscid->pid = PID_STG_NAME;
1001 return S_OK;
1002 case NTOBJECT_COLUMN_TYPE:
1003 pscid->fmtid = storage;
1004 pscid->pid = PID_STG_STORAGETYPE;
1005 return S_OK;
1006 case NTOBJECT_COLUMN_LINKTARGET:
1007 pscid->fmtid = GUID_NtObjectColumns;
1008 pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
1009 return S_OK;
1010 }
1011 return E_INVALIDARG;
1012 }
1013
1014 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1015 {
1016 switch (uMsg)
1017 {
1018 case SFVM_DEFVIEWMODE:
1019 {
1020 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
1021 *pViewMode = FVM_DETAILS;
1022 return S_OK;
1023 }
1024 case SFVM_COLUMNCLICK:
1025 return S_FALSE;
1026 case SFVM_BACKGROUNDENUM:
1027 return S_OK;
1028 }
1029 return E_NOTIMPL;
1030 }
1031
1032 HRESULT CNtObjectFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1033 {
1034 switch (uMsg)
1035 {
1036 case DFM_MERGECONTEXTMENU:
1037 return S_OK;
1038 case DFM_INVOKECOMMAND:
1039 case DFM_INVOKECOMMANDEX:
1040 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1041 return S_FALSE;
1042 }
1043 return E_NOTIMPL;
1044 }