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