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