[NTOBJSHEX] Add Italian translation by Carlo Bramini. CORE-9725
[reactos.git] / reactos / dll / shellext / ntobjshex / ntobjns.cpp
1 /*
2 * PROJECT: ReactOS shell extensions
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll\shellext\ntobjshex\ntobjns.cpp
5 * PURPOSE: NT Object Namespace shell extension
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 */
8
9 #include "precomp.h"
10 #include "ntobjutil.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_CREATEDATE,
38 NTOBJECT_COLUMN_LINKTARGET,
39 NTOBJECT_COLUMN_END
40 };
41
42 class CNtObjectFolderExtractIcon :
43 public CComObjectRootEx<CComMultiThreadModelNoCS>,
44 public IExtractIconW
45 {
46 PCIDLIST_ABSOLUTE m_pcidlFolder;
47 PCITEMID_CHILD m_pcidlChild;
48
49 public:
50 CNtObjectFolderExtractIcon() :
51 m_pcidlFolder(NULL),
52 m_pcidlChild(NULL)
53 {
54
55 }
56
57 virtual ~CNtObjectFolderExtractIcon()
58 {
59 if (m_pcidlFolder)
60 ILFree((LPITEMIDLIST) m_pcidlFolder);
61 if (m_pcidlChild)
62 ILFree((LPITEMIDLIST) m_pcidlChild);
63 }
64
65 HRESULT Initialize(PCIDLIST_ABSOLUTE parent, UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
66 {
67 m_pcidlFolder = ILClone(parent);
68 if (cidl != 1)
69 return E_INVALIDARG;
70 m_pcidlChild = ILClone(apidl[0]);
71 return S_OK;
72 }
73
74 virtual HRESULT STDMETHODCALLTYPE GetIconLocation(
75 UINT uFlags,
76 LPWSTR szIconFile,
77 UINT cchMax,
78 INT *piIndex,
79 UINT *pwFlags)
80 {
81 const NtPidlEntry * entry = (NtPidlEntry *) m_pcidlChild;
82
83 if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC))
84 return E_INVALIDARG;
85
86 UINT flags = 0;
87
88 #define GIL_CHECKSHIELD 0x0200
89 #define GIL_SHIELD 0x0200
90 if (uFlags & GIL_CHECKSHIELD && !(entry->objectInformation.GrantedAccess & STANDARD_RIGHTS_READ))
91 flags |= GIL_SHIELD;
92
93 switch (entry->objectType)
94 {
95 case DIRECTORY_OBJECT:
96 case SYMBOLICLINK_OBJECT:
97 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
98 *piIndex = -((uFlags & GIL_OPENICON) ? IDI_NTOBJECTDIROPEN : IDI_NTOBJECTDIR);
99 *pwFlags = flags;
100 return S_OK;
101 case DEVICE_OBJECT:
102 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
103 *piIndex = -IDI_NTOBJECTDEVICE;
104 *pwFlags = flags;
105 return S_OK;
106 case PORT_OBJECT:
107 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
108 *piIndex = -IDI_NTOBJECTPORT;
109 *pwFlags = flags;
110 return S_OK;
111 case KEY_OBJECT:
112 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
113 *piIndex = -IDI_REGISTRYKEY;
114 *pwFlags = flags;
115 return S_OK;
116 default:
117 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
118 *piIndex = -IDI_NTOBJECTITEM;
119 *pwFlags = flags;
120 return S_OK;
121 }
122 }
123
124 virtual HRESULT STDMETHODCALLTYPE Extract(
125 LPCWSTR pszFile,
126 UINT nIconIndex,
127 HICON *phiconLarge,
128 HICON *phiconSmall,
129 UINT nIconSize)
130 {
131 return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize);
132 }
133
134 DECLARE_NOT_AGGREGATABLE(CNtObjectFolderExtractIcon)
135 DECLARE_PROTECT_FINAL_CONSTRUCT()
136
137 BEGIN_COM_MAP(CNtObjectFolderExtractIcon)
138 COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
139 END_COM_MAP()
140
141 };
142
143 class CNtObjectPidlManager
144 {
145 private:
146 PWSTR m_ntPath;
147
148 HDPA m_hDpa;
149 UINT m_hDpaCount;
150
151 int DpaDeleteCallback(NtPidlEntry * info)
152 {
153 CoTaskMemFree(info);
154 return 0;
155 }
156
157 static int CALLBACK s_DpaDeleteCallback(void *pItem, void *pData)
158 {
159 CNtObjectPidlManager * mf = (CNtObjectPidlManager*) pData;
160 NtPidlEntry * item = (NtPidlEntry*) pItem;
161 return mf->DpaDeleteCallback(item);
162 }
163
164 public:
165 CNtObjectPidlManager() :
166 m_ntPath(NULL),
167 m_hDpa(NULL),
168 m_hDpaCount(0)
169 {
170 }
171
172 ~CNtObjectPidlManager()
173 {
174 DPA_DestroyCallback(m_hDpa, s_DpaDeleteCallback, this);
175 }
176
177 HRESULT Initialize(PWSTR ntPath)
178 {
179 m_ntPath = ntPath;
180 m_hDpa = NULL;
181
182 return S_OK;
183 }
184
185 HRESULT Enumerate()
186 {
187 if (m_hDpa)
188 return S_OK;
189
190 m_hDpa = DPA_Create(10);
191
192 if (!m_hDpa)
193 return E_OUTOFMEMORY;
194
195 HRESULT hr = EnumerateNtDirectory(m_hDpa, m_ntPath, &m_hDpaCount);
196 if (FAILED_UNEXPECTEDLY(hr))
197 return hr;
198
199 return S_OK;
200 }
201
202 HRESULT FindPidlInList(PCUITEMID_CHILD pcidl, const NtPidlEntry ** pinfo)
203 {
204 HRESULT hr;
205
206 if (!m_hDpa)
207 {
208 hr = Enumerate();
209 if (FAILED_UNEXPECTEDLY(hr))
210 return hr;
211
212 if (!m_hDpa)
213 return E_FAIL;
214 }
215
216 const NtPidlEntry * info = (const NtPidlEntry *) pcidl;
217 if ((info->cb < sizeof(NtPidlEntry)) || (info->magic != NT_OBJECT_PIDL_MAGIC))
218 {
219 ERR("FindPidlInList: Requested pidl is not of the correct type.\n");
220 return E_INVALIDARG;
221 }
222
223 TRACE("Searching for pidl { name='%S' } in a list of %d items\n", info->entryName, m_hDpaCount);
224
225 for (UINT i = 0; i < m_hDpaCount; i++)
226 {
227 const NtPidlEntry * pInfo = (const NtPidlEntry *) DPA_GetPtr(m_hDpa, i);
228 ASSERT(pInfo);
229
230 hr = CompareIDs(SHCIDS_CANONICALONLY, pInfo, info);
231 if (FAILED_UNEXPECTEDLY(hr))
232 return hr;
233
234 if (hr == S_OK)
235 {
236 *pinfo = pInfo;
237 return S_OK;
238 }
239 else
240 {
241 TRACE("Comparison returned %d for '%S'\n", (int) (short) (hr & 0xFFFF), pInfo->entryName);
242 }
243 }
244
245 ERR("PIDL NOT FOUND: Requested filename: %S\n", info->entryName);
246 *pinfo = NULL;
247
248 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
249 }
250
251 HRESULT FindByName(LPCWSTR strParsingName, NtPidlEntry ** pinfo)
252 {
253 HRESULT hr;
254
255 if (!m_hDpa)
256 {
257 hr = Enumerate();
258 if (FAILED_UNEXPECTEDLY(hr))
259 return hr;
260
261 if (!m_hDpa)
262 return E_FAIL;
263 }
264
265 TRACE("Searching for '%S' in a list of %d items\n", strParsingName, m_hDpaCount);
266
267 for (int i = 0; i < (int) m_hDpaCount; i++)
268 {
269 NtPidlEntry * pInfo = (NtPidlEntry *) DPA_GetPtr(m_hDpa, i);
270 ASSERT(pInfo);
271
272 int order = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
273 pInfo->entryName, wcslen(pInfo->entryName),
274 strParsingName, wcslen(strParsingName));
275
276 if (order == CSTR_EQUAL)
277 {
278 *pinfo = pInfo;
279 return S_OK;
280 }
281 }
282
283 TRACE("Pidl not found\n");
284 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
285 }
286
287 HRESULT GetPidl(UINT index, NtPidlEntry ** pEntry)
288 {
289 if (!m_hDpa)
290 {
291 HRESULT hr = Enumerate();
292 if (FAILED_UNEXPECTEDLY(hr))
293 return hr;
294
295 if (!m_hDpa)
296 return E_FAIL;
297 }
298
299 *pEntry = NULL;
300
301 NtPidlEntry * entry = (NtPidlEntry *) DPA_GetPtr(m_hDpa, index);
302 if (!entry)
303 {
304 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
305 }
306
307 *pEntry = entry;
308 return S_OK;
309 }
310
311 HRESULT GetCount(UINT * count)
312 {
313 if (!m_hDpa)
314 {
315 HRESULT hr = Enumerate();
316 if (FAILED_UNEXPECTEDLY(hr))
317 return hr;
318
319 if (!m_hDpa)
320 return E_FAIL;
321 }
322
323 *count = m_hDpaCount;
324 return S_OK;
325 }
326
327 static LPITEMIDLIST CreatePidlFromItem(const NtPidlEntry * entry)
328 {
329 LPITEMIDLIST idl = (LPITEMIDLIST) CoTaskMemAlloc(entry->cb + 2);
330 if (!idl)
331 return NULL;
332 memset(idl, 0, entry->cb + 2);
333 memcpy(idl, entry, entry->cb);
334 return idl;
335 }
336
337 static HRESULT CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second)
338 {
339 if ((lParam & 0xFFFF0000) == SHCIDS_ALLFIELDS)
340 {
341 if (lParam != 0)
342 return E_INVALIDARG;
343
344 int minsize = min(first->cb, second->cb);
345 int ord = memcmp(second, first, minsize);
346
347 if (ord != 0)
348 return MAKE_HRESULT(0, 0, (USHORT) ord);
349
350 if (second->cb > first->cb)
351 return MAKE_HRESULT(0, 0, (USHORT) 1);
352 if (second->cb < first->cb)
353 return MAKE_HRESULT(0, 0, (USHORT) -1);
354 }
355 else
356 {
357 bool canonical = ((lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY);
358
359 switch (lParam & 0xFFFF)
360 {
361 case NTOBJECT_COLUMN_NAME:
362 {
363 bool f1 = (first->objectType == KEY_OBJECT) || (first->objectType == DIRECTORY_OBJECT);
364 bool f2 = (second->objectType == KEY_OBJECT) || (second->objectType == DIRECTORY_OBJECT);
365
366 if (f1 && !f2)
367 return MAKE_HRESULT(0, 0, (USHORT) -1);
368 if (f2 && !f1)
369 return MAKE_HRESULT(0, 0, (USHORT) 1);
370
371 if (canonical)
372 {
373 // Shortcut: avoid comparing contents if not necessary when the results are not for display.
374 if (second->entryNameLength > first->entryNameLength)
375 return MAKE_HRESULT(0, 0, (USHORT) 1);
376 if (second->entryNameLength < first->entryNameLength)
377 return MAKE_HRESULT(0, 0, (USHORT) -1);
378
379 int minlength = min(first->entryNameLength, second->entryNameLength);
380 if (minlength > 0)
381 {
382 int ord = memcmp(first->entryName, second->entryName, minlength);
383 if (ord != 0)
384 return MAKE_HRESULT(0, 0, (USHORT) ord);
385 }
386 return S_OK;
387 }
388 else
389 {
390 int minlength = min(first->entryNameLength, second->entryNameLength);
391 if (minlength > 0)
392 {
393 int ord = StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR));
394 if (ord != 0)
395 return MAKE_HRESULT(0, 0, (USHORT) ord);
396 }
397
398 if (second->entryNameLength > first->entryNameLength)
399 return MAKE_HRESULT(0, 0, (USHORT) 1);
400 if (second->entryNameLength < first->entryNameLength)
401 return MAKE_HRESULT(0, 0, (USHORT) -1);
402
403 return S_OK;
404 }
405 }
406 case NTOBJECT_COLUMN_TYPE:
407 {
408 int ord = second->objectType - first->objectType;
409 if (ord > 0)
410 return MAKE_HRESULT(0, 0, (USHORT) 1);
411 if (ord < 0)
412 return MAKE_HRESULT(0, 0, (USHORT) -1);
413
414 return S_OK;
415 }
416 case NTOBJECT_COLUMN_CREATEDATE:
417 {
418 LONGLONG ord = second->objectInformation.CreateTime.QuadPart - first->objectInformation.CreateTime.QuadPart;
419 if (ord > 0)
420 return MAKE_HRESULT(0, 0, (USHORT) 1);
421 if (ord < 0)
422 return MAKE_HRESULT(0, 0, (USHORT) -1);
423
424 return S_OK;
425 }
426 case NTOBJECT_COLUMN_LINKTARGET:
427 {
428 // Can't sort by value
429 return E_INVALIDARG;
430 }
431 default:
432 {
433 DbgPrint("Unsupported sorting mode.\n");
434 return E_INVALIDARG;
435 }
436 }
437 }
438
439 return E_INVALIDARG;
440 }
441
442 static HRESULT CompareIDs(LPARAM lParam, const NtPidlEntry * first, LPCITEMIDLIST pcidl)
443 {
444 LPCITEMIDLIST p = pcidl;
445 NtPidlEntry * second = (NtPidlEntry*) &(p->mkid);
446 if ((second->cb < sizeof(NtPidlEntry)) || (second->magic != NT_OBJECT_PIDL_MAGIC))
447 return E_INVALIDARG;
448
449 return CompareIDs(lParam, first, second);
450 }
451
452 static HRESULT CompareIDs(LPARAM lParam, LPCITEMIDLIST pcidl1, LPCITEMIDLIST pcidl2)
453 {
454 LPCITEMIDLIST p = pcidl1;
455 NtPidlEntry * first = (NtPidlEntry*) &(p->mkid);
456 if ((first->cb < sizeof(NtPidlEntry)) || (first->magic != NT_OBJECT_PIDL_MAGIC))
457 return E_INVALIDARG;
458
459 return CompareIDs(lParam, first, pcidl2);
460 }
461
462 static ULONG ConvertAttributes(const NtPidlEntry * entry, PULONG inMask)
463 {
464 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
465 ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK;
466
467 if (entry->objectType == DIRECTORY_OBJECT)
468 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
469
470 if (entry->objectType == SYMBOLICLINK_OBJECT)
471 flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
472
473 if (entry->objectType == KEY_OBJECT)
474 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
475
476 return flags & mask;
477 }
478
479 BOOL IsFolder(LPCITEMIDLIST pcidl)
480 {
481 const NtPidlEntry * entry;
482 HRESULT hr = FindPidlInList(pcidl, &entry);
483 if (FAILED_UNEXPECTEDLY(hr))
484 return FALSE;
485
486 return (entry->objectType == DIRECTORY_OBJECT) ||
487 (entry->objectType == SYMBOLICLINK_OBJECT) ||
488 (entry->objectType == KEY_OBJECT);
489 }
490 };
491
492 class CNtObjectFolderEnum :
493 public CComObjectRootEx<CComMultiThreadModelNoCS>,
494 public IEnumIDList
495 {
496 private:
497 CComPtr<CNtObjectFolder> m_Folder;
498
499 HWND m_HwndOwner;
500 SHCONTF m_Flags;
501
502 UINT m_Index;
503 UINT m_Count;
504
505 public:
506 CNtObjectFolderEnum() :
507 m_HwndOwner(NULL),
508 m_Flags(0),
509 m_Index(0),
510 m_Count(0)
511 {
512 }
513
514 virtual ~CNtObjectFolderEnum()
515 {
516 }
517
518 HRESULT Initialize(CNtObjectFolder * folder, HWND hwndOwner, SHCONTF flags)
519 {
520 m_Folder = folder;
521
522 m_Folder->GetManager().GetCount(&m_Count);
523
524 m_HwndOwner = hwndOwner;
525 m_Flags = flags;
526
527 return Reset();
528 }
529
530 virtual HRESULT STDMETHODCALLTYPE Next(
531 ULONG celt,
532 LPITEMIDLIST *rgelt,
533 ULONG *pceltFetched)
534 {
535 if (pceltFetched)
536 *pceltFetched = 0;
537
538 if (m_Index >= m_Count)
539 return S_FALSE;
540
541 for (int i = 0; i < (int) celt;)
542 {
543 NtPidlEntry * tinfo;
544 BOOL flagsOk = FALSE;
545
546 do {
547 HRESULT hr = m_Folder->GetManager().GetPidl(m_Index++, &tinfo);
548 if (FAILED_UNEXPECTEDLY(hr))
549 return hr;
550
551 switch (tinfo->objectType)
552 {
553 case SYMBOLICLINK_OBJECT:
554 case DIRECTORY_OBJECT:
555 case KEY_OBJECT:
556 flagsOk = (m_Flags & SHCONTF_FOLDERS) != 0;
557 break;
558 default:
559 flagsOk = (m_Flags & SHCONTF_NONFOLDERS) != 0;
560 break;
561 }
562 } while (m_Index < m_Count && !flagsOk);
563
564 if (flagsOk)
565 {
566 if (rgelt)
567 rgelt[i] = m_Folder->GetManager().CreatePidlFromItem(tinfo);
568 i++;
569 }
570
571 if (m_Index == m_Count)
572 {
573 if (pceltFetched)
574 *pceltFetched = i;
575 return (i == (int) celt) ? S_OK : S_FALSE;
576 }
577 }
578
579 if (pceltFetched) *pceltFetched = celt;
580 return S_OK;
581 }
582
583 virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt)
584 {
585 return Next(celt, NULL, NULL);
586 }
587
588 virtual HRESULT STDMETHODCALLTYPE Reset()
589 {
590 m_Index = 0;
591 return S_OK;
592 }
593
594 virtual HRESULT STDMETHODCALLTYPE Clone(IEnumIDList **ppenum)
595 {
596 return ShellObjectCreatorInit<CNtObjectFolderEnum>(m_Folder, m_HwndOwner, m_Flags, IID_PPV_ARG(IEnumIDList, ppenum));
597 }
598
599 DECLARE_NOT_AGGREGATABLE(CNtObjectFolderEnum)
600 DECLARE_PROTECT_FINAL_CONSTRUCT()
601
602 BEGIN_COM_MAP(CNtObjectFolderEnum)
603 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
604 END_COM_MAP()
605
606 };
607
608 //-----------------------------------------------------------------------------
609 // CNtObjectFolder
610
611 CNtObjectFolder::CNtObjectFolder() :
612 m_PidlManager(NULL),
613 m_shellPidl(NULL)
614 {
615 }
616
617 CNtObjectFolder::~CNtObjectFolder()
618 {
619 if (m_shellPidl)
620 ILFree(m_shellPidl);
621 if (m_PidlManager)
622 delete m_PidlManager;
623 }
624
625 // IShellFolder
626 HRESULT STDMETHODCALLTYPE CNtObjectFolder::ParseDisplayName(
627 HWND hwndOwner,
628 LPBC pbcReserved,
629 LPOLESTR lpszDisplayName,
630 ULONG *pchEaten,
631 LPITEMIDLIST *ppidl,
632 ULONG *pdwAttributes)
633 {
634 NtPidlEntry * info;
635
636 if (!ppidl)
637 return E_POINTER;
638
639 if (pchEaten)
640 *pchEaten = 0;
641
642 if (pdwAttributes)
643 *pdwAttributes = 0;
644
645 TRACE("CNtObjectFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
646
647 HRESULT hr = m_PidlManager->FindByName(lpszDisplayName, &info);
648 if (FAILED(hr))
649 {
650 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
651 }
652
653 *ppidl = m_PidlManager->CreatePidlFromItem(info);
654
655 if (pchEaten)
656 *pchEaten = wcslen(info->entryName);
657
658 if (pdwAttributes)
659 *pdwAttributes = m_PidlManager->ConvertAttributes(info, pdwAttributes);
660
661 return S_OK;
662 }
663
664 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects(
665 HWND hwndOwner,
666 SHCONTF grfFlags,
667 IEnumIDList **ppenumIDList)
668 {
669 return ShellObjectCreatorInit<CNtObjectFolderEnum>(this, hwndOwner, grfFlags, IID_PPV_ARG(IEnumIDList, ppenumIDList));
670 }
671
672 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToObject(
673 LPCITEMIDLIST pidl,
674 LPBC pbcReserved,
675 REFIID riid,
676 void **ppvOut)
677 {
678 const NtPidlEntry * info;
679
680 if (IsEqualIID(riid, IID_IShellFolder))
681 {
682 HRESULT hr = m_PidlManager->FindPidlInList(pidl, &info);
683 if (FAILED_UNEXPECTEDLY(hr))
684 return hr;
685
686 if (!(info->objectInformation.GrantedAccess & (STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY)))
687 return E_ACCESSDENIED;
688
689 WCHAR path[MAX_PATH];
690
691 StringCbCopyW(path, _countof(path), m_NtPath);
692
693 PathAppendW(path, info->entryName);
694
695 LPITEMIDLIST first = ILCloneFirst(pidl);
696 LPCITEMIDLIST rest = ILGetNext(pidl);
697
698 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
699
700 if (info->objectType == SYMBOLICLINK_OBJECT)
701 {
702 NtPidlSymlinkData * symlink = (NtPidlSymlinkData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
703
704 if (symlink->targetNameLength > 0)
705 {
706 if (symlink->targetName[1] == L':' && isalphaW(symlink->targetName[0]))
707 {
708 ERR("TODO: Navigating to WIN32 PATH from NT PATH.\n");
709 return E_NOTIMPL;
710 }
711
712 StringCbCopyW(path, _countof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
713 PathAppend(path, symlink->targetName);
714
715 CComPtr<IShellFolder> psfDesktop;
716 hr = SHGetDesktopFolder(&psfDesktop);
717 if (FAILED_UNEXPECTEDLY(hr))
718 return hr;
719
720 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &first, NULL);
721 if (FAILED_UNEXPECTEDLY(hr))
722 return hr;
723 }
724 else
725 {
726 return E_UNEXPECTED;
727 }
728 }
729
730 CComPtr<IShellFolder> psfChild;
731
732 if (info->objectType == KEY_OBJECT)
733 {
734 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY)NULL, IID_PPV_ARG(IShellFolder, &psfChild));
735 }
736 else
737 {
738 hr = ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, &psfChild));
739 }
740
741 ILFree(fullPidl);
742 ILFree(first);
743
744 if (rest->mkid.cb > 0)
745 {
746 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
747 }
748
749 return psfChild->QueryInterface(riid, ppvOut);
750 }
751
752 return E_NOTIMPL;
753 }
754
755 HRESULT STDMETHODCALLTYPE CNtObjectFolder::BindToStorage(
756 LPCITEMIDLIST pidl,
757 LPBC pbcReserved,
758 REFIID riid,
759 void **ppvObj)
760 {
761 UNIMPLEMENTED;
762 return E_NOTIMPL;
763 }
764
765 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CompareIDs(
766 LPARAM lParam,
767 LPCITEMIDLIST pidl1,
768 LPCITEMIDLIST pidl2)
769 {
770 TRACE("CompareIDs\n");
771
772 HRESULT hr = m_PidlManager->CompareIDs(lParam, pidl1, pidl2);
773 if (hr != S_OK)
774 return hr;
775
776 LPCITEMIDLIST rest1 = ILGetNext(pidl1);
777 LPCITEMIDLIST rest2 = ILGetNext(pidl2);
778
779 bool hasNext1 = (rest1->mkid.cb > 0);
780 bool hasNext2 = (rest2->mkid.cb > 0);
781
782 if (hasNext1 || hasNext2)
783 {
784 if (hasNext1 && !hasNext2)
785 return MAKE_HRESULT(0, 0, (USHORT) -1);
786
787 if (hasNext2 && !hasNext1)
788 return MAKE_HRESULT(0, 0, (USHORT) 1);
789
790 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
791
792 CComPtr<IShellFolder> psfNext;
793 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
794 if (FAILED_UNEXPECTEDLY(hr))
795 return hr;
796
797 return psfNext->CompareIDs(lParam, rest1, rest2);
798 }
799
800 return S_OK;
801 }
802
803 HRESULT STDMETHODCALLTYPE CNtObjectFolder::CreateViewObject(
804 HWND hwndOwner,
805 REFIID riid,
806 void **ppvOut)
807 {
808 if (!IsEqualIID(riid, IID_IShellView))
809 return E_NOINTERFACE;
810
811 SFV_CREATE sfv;
812 sfv.cbSize = sizeof(sfv);
813 sfv.pshf = this;
814 sfv.psvOuter = NULL;
815 sfv.psfvcb = this;
816
817 return SHCreateShellFolderView(&sfv, (IShellView**) ppvOut);
818 }
819
820 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetAttributesOf(
821 UINT cidl,
822 PCUITEMID_CHILD_ARRAY apidl,
823 SFGAOF *rgfInOut)
824 {
825 const NtPidlEntry * info;
826
827 TRACE("GetAttributesOf\n");
828
829 if (cidl == 0)
830 {
831 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
832 return S_OK;
833 }
834
835 for (int i = 0; i < (int) cidl; i++)
836 {
837 PCUITEMID_CHILD pidl = apidl[i];
838
839 HRESULT hr = m_PidlManager->FindPidlInList(pidl, &info);
840 if (FAILED_UNEXPECTEDLY(hr))
841 return hr;
842
843 // Update attributes.
844 *rgfInOut = m_PidlManager->ConvertAttributes(info, rgfInOut);
845 }
846
847 return S_OK;
848 }
849
850 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetUIObjectOf(
851 HWND hwndOwner,
852 UINT cidl,
853 PCUITEMID_CHILD_ARRAY apidl,
854 REFIID riid,
855 UINT *prgfInOut,
856 void **ppvOut)
857 {
858 TRACE("GetUIObjectOf\n");
859
860 if (IsEqualIID(riid, IID_IContextMenu) ||
861 IsEqualIID(riid, IID_IContextMenu2) ||
862 IsEqualIID(riid, IID_IContextMenu3))
863 {
864 CComPtr<IContextMenu> pcm;
865
866 HKEY keys [1];
867
868 int nkeys = _countof(keys);
869 if (cidl == 1 && m_PidlManager->IsFolder(apidl[0]))
870 {
871 RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
872 }
873 else
874 {
875 nkeys = 0;
876 }
877
878 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
879 if (FAILED_UNEXPECTEDLY(hr))
880 return hr;
881
882 return pcm->QueryInterface(riid, ppvOut);
883 }
884
885 if (IsEqualIID(riid, IID_IExtractIconW))
886 {
887 return ShellObjectCreatorInit<CNtObjectFolderExtractIcon>(m_shellPidl, cidl, apidl, riid, ppvOut);
888 }
889
890 if (IsEqualIID(riid, IID_IDataObject))
891 {
892 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**)ppvOut);
893 }
894
895 if (IsEqualIID(riid, IID_IQueryAssociations))
896 {
897 if (cidl == 1 && m_PidlManager->IsFolder(apidl[0]))
898 {
899 CComPtr<IQueryAssociations> pqa;
900 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
901 if (FAILED_UNEXPECTEDLY(hr))
902 return hr;
903
904 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
905 if (FAILED_UNEXPECTEDLY(hr))
906 return hr;
907
908 return pqa->QueryInterface(riid, ppvOut);
909 }
910 }
911
912 return E_NOTIMPL;
913 }
914
915 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDisplayNameOf(
916 LPCITEMIDLIST pidl,
917 SHGDNF uFlags,
918 STRRET *lpName)
919 {
920 const NtPidlEntry * info;
921
922 TRACE("GetDisplayNameOf %p\n", pidl);
923
924 HRESULT hr = m_PidlManager->FindPidlInList(pidl, &info);
925 if (FAILED_UNEXPECTEDLY(hr))
926 return hr;
927
928 if ((GET_SHGDN_RELATION(uFlags) == SHGDN_NORMAL) &&
929 (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING))
930 {
931 WCHAR path[MAX_PATH] = { 0 };
932
933 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
934 if (FAILED_UNEXPECTEDLY(hr))
935 return hr;
936
937 PathAppendW(path, info->entryName);
938
939 hr = MakeStrRetFromString(path, lpName);
940 if (FAILED_UNEXPECTEDLY(hr))
941 return hr;
942
943 LPCITEMIDLIST pidlFirst = ILCloneFirst(pidl);
944 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
945
946 if (pidlNext && pidlNext->mkid.cb > 0)
947 {
948 CComPtr<IShellFolder> psfChild;
949 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
950 if (FAILED_UNEXPECTEDLY(hr))
951 return hr;
952
953 WCHAR temp[MAX_PATH];
954 STRRET childName;
955
956 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
957 if (FAILED_UNEXPECTEDLY(hr))
958 return hr;
959
960 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
961 if (FAILED_UNEXPECTEDLY(hr))
962 return hr;
963
964 PathAppendW(path, temp);
965 }
966
967 ILFree((LPITEMIDLIST)pidlFirst);
968 }
969 else
970 {
971 MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
972 }
973
974 return S_OK;
975 }
976
977 HRESULT STDMETHODCALLTYPE CNtObjectFolder::SetNameOf(
978 HWND hwnd,
979 LPCITEMIDLIST pidl,
980 LPCOLESTR lpszName,
981 SHGDNF uFlags,
982 LPITEMIDLIST *ppidlOut)
983 {
984 UNIMPLEMENTED;
985 return E_NOTIMPL;
986 }
987
988 // IPersist
989 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetClassID(CLSID *lpClassId)
990 {
991 if (!lpClassId)
992 return E_POINTER;
993
994 *lpClassId = CLSID_NtObjectFolder;
995 return S_OK;
996 }
997
998 // IPersistFolder
999 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl)
1000 {
1001 m_shellPidl = ILClone(pidl);
1002
1003 PCWSTR ntPath = L"\\";
1004
1005 if (!m_PidlManager)
1006 {
1007 m_PidlManager = new CNtObjectPidlManager();
1008
1009 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
1010 }
1011
1012 return m_PidlManager->Initialize(m_NtPath);
1013 }
1014
1015 // Internal
1016 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath)
1017 {
1018 m_shellPidl = ILClone(pidl);
1019
1020 if (!m_PidlManager)
1021 m_PidlManager = new CNtObjectPidlManager();
1022
1023 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
1024 return m_PidlManager->Initialize(m_NtPath);
1025 }
1026
1027 // IPersistFolder2
1028 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetCurFolder(LPITEMIDLIST * pidl)
1029 {
1030 if (pidl)
1031 *pidl = ILClone(m_shellPidl);
1032 if (!m_shellPidl)
1033 return S_FALSE;
1034 return S_OK;
1035 }
1036
1037 // IShellFolder2
1038 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultSearchGUID(
1039 GUID *lpguid)
1040 {
1041 UNIMPLEMENTED;
1042 return E_NOTIMPL;
1043 }
1044
1045 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumSearches(
1046 IEnumExtraSearch **ppenum)
1047 {
1048 UNIMPLEMENTED;
1049 return E_NOTIMPL;
1050 }
1051
1052 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumn(
1053 DWORD dwReserved,
1054 ULONG *pSort,
1055 ULONG *pDisplay)
1056 {
1057 if (pSort)
1058 *pSort = 0;
1059 if (pDisplay)
1060 *pDisplay = 0;
1061 return S_OK;
1062 }
1063
1064 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
1065 UINT iColumn,
1066 SHCOLSTATEF *pcsFlags)
1067 {
1068 switch (iColumn)
1069 {
1070 case NTOBJECT_COLUMN_NAME:
1071 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
1072 return S_OK;
1073 case NTOBJECT_COLUMN_TYPE:
1074 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
1075 return S_OK;
1076 case NTOBJECT_COLUMN_CREATEDATE:
1077 *pcsFlags = SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT;
1078 return S_OK;
1079 case NTOBJECT_COLUMN_LINKTARGET:
1080 *pcsFlags = SHCOLSTATE_TYPE_STR;
1081 return S_OK;
1082 }
1083
1084 return E_INVALIDARG;
1085 }
1086
1087 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
1088 LPCITEMIDLIST pidl,
1089 const SHCOLUMNID *pscid,
1090 VARIANT *pv)
1091 {
1092 const NtPidlEntry * info;
1093
1094 TRACE("GetDetailsEx\n");
1095
1096 if (pidl)
1097 {
1098 HRESULT hr = m_PidlManager->FindPidlInList(pidl, &info);
1099 if (FAILED_UNEXPECTEDLY(hr))
1100 return hr;
1101
1102 static const GUID storage = PSGUID_STORAGE;
1103 if (IsEqualGUID(pscid->fmtid, storage))
1104 {
1105 if (pscid->pid == PID_STG_NAME)
1106 {
1107 return MakeVariantString(pv, info->entryName);
1108 }
1109 else if (pscid->pid == PID_STG_STORAGETYPE)
1110 {
1111 if (info->objectType < 0)
1112 {
1113 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1114
1115 if (td->typeNameLength > 0)
1116 {
1117 return MakeVariantString(pv, td->typeName);
1118 }
1119 else
1120 {
1121 return MakeVariantString(pv, L"Unknown");
1122 }
1123 }
1124 else
1125 {
1126 return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
1127 }
1128 }
1129 else if (pscid->pid == PID_STG_WRITETIME)
1130 {
1131 DOUBLE varTime;
1132 SYSTEMTIME stime;
1133 FileTimeToSystemTime((FILETIME*) &(info->objectInformation.CreateTime), &stime);
1134 SystemTimeToVariantTime(&stime, &varTime);
1135
1136 V_VT(pv) = VT_DATE;
1137 V_DATE(pv) = varTime;
1138 return S_OK;
1139 }
1140 }
1141 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
1142 {
1143 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET)
1144 {
1145 if (info->objectType == SYMBOLICLINK_OBJECT)
1146 {
1147 NtPidlSymlinkData * symlink = (NtPidlSymlinkData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1148
1149 if (symlink->targetNameLength > 0)
1150 {
1151 return MakeVariantString(pv, symlink->targetName);
1152 }
1153 }
1154
1155 V_VT(pv) = VT_EMPTY;
1156 return S_OK;
1157 }
1158 }
1159 }
1160
1161 return E_INVALIDARG;
1162 }
1163
1164 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
1165 LPCITEMIDLIST pidl,
1166 UINT iColumn,
1167 SHELLDETAILS *psd)
1168 {
1169 const NtPidlEntry * info;
1170
1171 TRACE("GetDetailsOf\n");
1172
1173 if (pidl)
1174 {
1175 HRESULT hr = m_PidlManager->FindPidlInList(pidl, &info);
1176 if (FAILED_UNEXPECTEDLY(hr))
1177 return hr;
1178
1179 switch (iColumn)
1180 {
1181 case NTOBJECT_COLUMN_NAME:
1182 psd->fmt = LVCFMT_LEFT;
1183
1184 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
1185 return S_OK;
1186 case NTOBJECT_COLUMN_TYPE:
1187 psd->fmt = LVCFMT_LEFT;
1188
1189 if (info->objectType < 0)
1190 {
1191 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1192
1193 if (td->typeNameLength > 0)
1194 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
1195 else
1196 MakeStrRetFromString(L"Unknown", &(psd->str));
1197 }
1198 else
1199 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
1200 return S_OK;
1201 case NTOBJECT_COLUMN_CREATEDATE:
1202 psd->fmt = LVCFMT_LEFT;
1203
1204 if (info->objectInformation.CreateTime.QuadPart != 0)
1205 {
1206 WCHAR dbuff[128];
1207 PWSTR tbuff;
1208 SYSTEMTIME stime;
1209 FileTimeToSystemTime((LPFILETIME) &(info->objectInformation.CreateTime), &stime);
1210 GetDateFormat(LOCALE_USER_DEFAULT, 0, &stime, NULL, dbuff, _countof(dbuff));
1211 tbuff = dbuff + wcslen(dbuff);
1212 *tbuff++ = L' ';
1213 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &stime, NULL, tbuff, _countof(dbuff) - (tbuff - dbuff));
1214
1215 MakeStrRetFromString(dbuff, &(psd->str));
1216 return S_OK;
1217 }
1218
1219 MakeStrRetFromString(L"", &(psd->str));
1220 return S_OK;
1221
1222 case NTOBJECT_COLUMN_LINKTARGET:
1223 psd->fmt = LVCFMT_LEFT;
1224
1225 if (info->objectType == SYMBOLICLINK_OBJECT)
1226 {
1227 NtPidlSymlinkData * symlink = (NtPidlSymlinkData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1228
1229 if (symlink->targetNameLength > 0)
1230 {
1231 MakeStrRetFromString(symlink->targetName, symlink->targetNameLength, &(psd->str));
1232 return S_OK;
1233 }
1234 }
1235
1236 MakeStrRetFromString(L"", &(psd->str));
1237 return S_OK;
1238 }
1239 }
1240 else
1241 {
1242 switch (iColumn)
1243 {
1244 case NTOBJECT_COLUMN_NAME:
1245 psd->fmt = LVCFMT_LEFT;
1246 psd->cxChar = 30;
1247
1248 // TODO: Make localizable
1249 MakeStrRetFromString(L"Object Name", &(psd->str));
1250 return S_OK;
1251 case NTOBJECT_COLUMN_TYPE:
1252 psd->fmt = LVCFMT_LEFT;
1253 psd->cxChar = 20;
1254
1255 // TODO: Make localizable
1256 MakeStrRetFromString(L"Object Type", &(psd->str));
1257 return S_OK;
1258 case NTOBJECT_COLUMN_CREATEDATE:
1259 psd->fmt = LVCFMT_LEFT;
1260 psd->cxChar = 20;
1261
1262 // TODO: Make localizable
1263 MakeStrRetFromString(L"Creation Time", &(psd->str));
1264 return S_OK;
1265 case NTOBJECT_COLUMN_LINKTARGET:
1266 psd->fmt = LVCFMT_LEFT;
1267 psd->cxChar = 30;
1268
1269 // TODO: Make localizable
1270 MakeStrRetFromString(L"Symlink Target", &(psd->str));
1271 return S_OK;
1272 }
1273 }
1274
1275 return E_INVALIDARG;
1276 }
1277
1278 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
1279 UINT iColumn,
1280 SHCOLUMNID *pscid)
1281 {
1282 static const GUID storage = PSGUID_STORAGE;
1283 switch (iColumn)
1284 {
1285 case NTOBJECT_COLUMN_NAME:
1286 pscid->fmtid = storage;
1287 pscid->pid = PID_STG_NAME;
1288 return S_OK;
1289 case NTOBJECT_COLUMN_TYPE:
1290 pscid->fmtid = storage;
1291 pscid->pid = PID_STG_STORAGETYPE;
1292 return S_OK;
1293 case NTOBJECT_COLUMN_CREATEDATE:
1294 pscid->fmtid = storage;
1295 pscid->pid = PID_STG_WRITETIME;
1296 return S_OK;
1297 case NTOBJECT_COLUMN_LINKTARGET:
1298 pscid->fmtid = GUID_NtObjectColumns;
1299 pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
1300 return S_OK;
1301 }
1302 return E_INVALIDARG;
1303 }
1304
1305 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1306 {
1307 switch (uMsg)
1308 {
1309 case SFVM_DEFVIEWMODE:
1310 {
1311 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
1312 *pViewMode = FVM_DETAILS;
1313 return S_OK;
1314 }
1315 case SFVM_COLUMNCLICK:
1316 return S_FALSE;
1317 case SFVM_BACKGROUNDENUM:
1318 return S_OK;
1319 case SFVM_DEFITEMCOUNT:
1320 return m_PidlManager->GetCount((UINT*) lParam);
1321 }
1322 return E_NOTIMPL;
1323 }
1324
1325 HRESULT CNtObjectFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1326 {
1327 switch (uMsg)
1328 {
1329 case DFM_MERGECONTEXTMENU:
1330 return S_OK;
1331 case DFM_INVOKECOMMAND:
1332 case DFM_INVOKECOMMANDEX:
1333 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1334 return S_FALSE;
1335 }
1336 return E_NOTIMPL;
1337 }