[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, 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(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, RegPidlEntry * first, 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, 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(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 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(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 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 RegPidlEntry * info;
870 HRESULT hr;
871
872 TRACE("GetAttributesOf\n");
873
874 if (cidl == 0)
875 {
876 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
877 return S_OK;
878 }
879
880 for (int i = 0; i < (int) cidl; i++)
881 {
882 PCUITEMID_CHILD pidl = apidl[i];
883
884 hr = m_PidlManager->FindPidlInList(pidl, &info);
885 if (FAILED_UNEXPECTEDLY(hr))
886 return hr;
887
888 // Update attributes.
889 *rgfInOut = m_PidlManager->ConvertAttributes(info, rgfInOut);
890 }
891
892 return S_OK;
893 }
894
895 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetUIObjectOf(
896 HWND hwndOwner,
897 UINT cidl,
898 PCUITEMID_CHILD_ARRAY apidl,
899 REFIID riid,
900 UINT *prgfInOut,
901 void **ppvOut)
902 {
903 TRACE("GetUIObjectOf\n");
904
905 if (IsEqualIID(riid, IID_IContextMenu) ||
906 IsEqualIID(riid, IID_IContextMenu2) ||
907 IsEqualIID(riid, IID_IContextMenu3))
908 {
909 CComPtr<IContextMenu> pcm;
910
911 HKEY keys [1];
912
913 int nkeys = _countof(keys);
914 if (cidl == 1 && m_PidlManager->IsFolder(apidl[0]))
915 {
916 RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
917 }
918 else
919 {
920 nkeys = 0;
921 }
922
923 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
924 if (FAILED_UNEXPECTEDLY(hr))
925 return hr;
926
927 return pcm->QueryInterface(riid, ppvOut);
928 }
929
930 if (IsEqualIID(riid, IID_IExtractIconW))
931 {
932 return ShellObjectCreatorInit<CRegistryFolderExtractIcon>(m_shellPidl, cidl, apidl, riid, ppvOut);
933 }
934
935 if (IsEqualIID(riid, IID_IDataObject))
936 {
937 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**) ppvOut);
938 }
939
940 if (IsEqualIID(riid, IID_IQueryAssociations))
941 {
942 if (cidl == 1 && m_PidlManager->IsFolder(apidl[0]))
943 {
944 CComPtr<IQueryAssociations> pqa;
945 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
946 if (FAILED_UNEXPECTEDLY(hr))
947 return hr;
948
949 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.RegFolder", NULL, hwndOwner);
950 if (FAILED_UNEXPECTEDLY(hr))
951 return hr;
952
953 return pqa->QueryInterface(riid, ppvOut);
954 }
955 }
956
957 return E_NOTIMPL;
958 }
959
960 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDisplayNameOf(
961 LPCITEMIDLIST pidl,
962 SHGDNF uFlags,
963 STRRET *lpName)
964 {
965 RegPidlEntry * info;
966 HRESULT hr;
967
968 TRACE("GetDisplayNameOf %p\n", pidl);
969
970 hr = m_PidlManager->FindPidlInList(pidl, &info);
971 if (FAILED_UNEXPECTEDLY(hr))
972 return hr;
973
974 if ((GET_SHGDN_RELATION(uFlags) == SHGDN_NORMAL) &&
975 (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING))
976 {
977 WCHAR path[MAX_PATH] = { 0 };
978
979 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
980 if (FAILED_UNEXPECTEDLY(hr))
981 return hr;
982
983 PathAppendW(path, info->entryName);
984
985 hr = MakeStrRetFromString(path, lpName);
986 if (FAILED_UNEXPECTEDLY(hr))
987 return hr;
988
989 LPCITEMIDLIST pidlFirst = ILCloneFirst(pidl);
990 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
991
992 if (pidlNext && pidlNext->mkid.cb > 0)
993 {
994 CComPtr<IShellFolder> psfChild;
995 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
996 if (FAILED_UNEXPECTEDLY(hr))
997 return hr;
998
999 WCHAR temp[MAX_PATH];
1000 STRRET childName;
1001
1002 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
1003 if (FAILED_UNEXPECTEDLY(hr))
1004 return hr;
1005
1006 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
1007 if (FAILED_UNEXPECTEDLY(hr))
1008 return hr;
1009
1010 PathAppendW(path, temp);
1011 }
1012
1013 ILFree((LPITEMIDLIST) pidlFirst);
1014 }
1015 else
1016 {
1017 MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
1018 }
1019
1020 return S_OK;
1021 }
1022
1023 HRESULT STDMETHODCALLTYPE CRegistryFolder::SetNameOf(
1024 HWND hwnd,
1025 LPCITEMIDLIST pidl,
1026 LPCOLESTR lpszName,
1027 SHGDNF uFlags,
1028 LPITEMIDLIST *ppidlOut)
1029 {
1030 UNIMPLEMENTED;
1031 return E_NOTIMPL;
1032 }
1033
1034 // IPersist
1035 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetClassID(CLSID *lpClassId)
1036 {
1037 if (!lpClassId)
1038 return E_POINTER;
1039
1040 *lpClassId = CLSID_RegistryFolder;
1041 return S_OK;
1042 }
1043
1044 // IPersistFolder
1045 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl)
1046 {
1047 m_shellPidl = ILClone(pidl);
1048 m_hRoot = NULL;
1049
1050 PCWSTR ntPath = L"";
1051
1052 if (!m_PidlManager)
1053 {
1054 m_PidlManager = new CRegistryPidlManager();
1055
1056 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
1057 }
1058
1059 return m_PidlManager->Initialize(m_NtPath, m_hRoot);
1060 }
1061
1062 // Internal
1063 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath, HKEY hRoot)
1064 {
1065 m_shellPidl = ILClone(pidl);
1066 m_hRoot = hRoot;
1067
1068 if (!m_PidlManager)
1069 m_PidlManager = new CRegistryPidlManager();
1070
1071 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
1072 return m_PidlManager->Initialize(m_NtPath, m_hRoot);
1073 }
1074
1075 // IPersistFolder2
1076 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetCurFolder(LPITEMIDLIST * pidl)
1077 {
1078 if (pidl)
1079 *pidl = ILClone(m_shellPidl);
1080 if (!m_shellPidl)
1081 return S_FALSE;
1082 return S_OK;
1083 }
1084
1085 // IShellFolder2
1086 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultSearchGUID(
1087 GUID *lpguid)
1088 {
1089 UNIMPLEMENTED;
1090 return E_NOTIMPL;
1091 }
1092
1093 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumSearches(
1094 IEnumExtraSearch **ppenum)
1095 {
1096 UNIMPLEMENTED;
1097 return E_NOTIMPL;
1098 }
1099
1100 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumn(
1101 DWORD dwReserved,
1102 ULONG *pSort,
1103 ULONG *pDisplay)
1104 {
1105 if (pSort)
1106 *pSort = 0;
1107 if (pDisplay)
1108 *pDisplay = 0;
1109 return S_OK;
1110 }
1111
1112 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumnState(
1113 UINT iColumn,
1114 SHCOLSTATEF *pcsFlags)
1115 {
1116 switch (iColumn)
1117 {
1118 case REGISTRY_COLUMN_NAME:
1119 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
1120 return S_OK;
1121 case REGISTRY_COLUMN_TYPE:
1122 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
1123 return S_OK;
1124 case REGISTRY_COLUMN_VALUE:
1125 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
1126 return S_OK;
1127 }
1128
1129 return E_INVALIDARG;
1130 }
1131
1132 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsEx(
1133 LPCITEMIDLIST pidl,
1134 const SHCOLUMNID *pscid,
1135 VARIANT *pv)
1136 {
1137 RegPidlEntry * info;
1138 HRESULT hr;
1139
1140 TRACE("GetDetailsEx\n");
1141
1142 if (pidl)
1143 {
1144 hr = m_PidlManager->FindPidlInList(pidl, &info);
1145 if (FAILED_UNEXPECTEDLY(hr))
1146 return hr;
1147
1148 static const GUID storage = PSGUID_STORAGE;
1149 if (IsEqualGUID(pscid->fmtid, storage))
1150 {
1151 if (pscid->pid == PID_STG_NAME)
1152 {
1153 if (info->entryNameLength > 0)
1154 {
1155 return MakeVariantString(pv, info->entryName);
1156 }
1157 return MakeVariantString(pv, L"(Default)");
1158 }
1159 else if (pscid->pid == PID_STG_STORAGETYPE)
1160 {
1161 if (info->entryType == REG_ENTRY_ROOT)
1162 {
1163 return MakeVariantString(pv, L"Key");
1164 }
1165
1166 if (info->entryType == REG_ENTRY_KEY)
1167 {
1168 if (info->contentsLength > 0)
1169 {
1170 PWSTR td = (PWSTR)(((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1171
1172 return MakeVariantString(pv, td);
1173 }
1174 return MakeVariantString(pv, L"Key");
1175 }
1176
1177 return MakeVariantString(pv, RegistryTypeNames[info->contentType]);
1178 }
1179 else if (pscid->pid == PID_STG_CONTENTS)
1180 {
1181 PCWSTR strValueContents;
1182
1183 hr = m_PidlManager->FormatContentsForDisplay(info, &strValueContents);
1184 if (FAILED_UNEXPECTEDLY(hr))
1185 return hr;
1186
1187 if (hr == S_FALSE)
1188 {
1189 V_VT(pv) = VT_EMPTY;
1190 return S_OK;
1191 }
1192
1193 hr = MakeVariantString(pv, strValueContents);
1194
1195 CoTaskMemFree((PVOID) strValueContents);
1196
1197 return hr;
1198
1199 }
1200 }
1201 }
1202
1203 return E_INVALIDARG;
1204 }
1205
1206 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsOf(
1207 LPCITEMIDLIST pidl,
1208 UINT iColumn,
1209 SHELLDETAILS *psd)
1210 {
1211 RegPidlEntry * info;
1212 HRESULT hr;
1213
1214 TRACE("GetDetailsOf\n");
1215
1216 if (pidl)
1217 {
1218 hr = m_PidlManager->FindPidlInList(pidl, &info);
1219 if (FAILED_UNEXPECTEDLY(hr))
1220 return hr;
1221
1222 switch (iColumn)
1223 {
1224 case REGISTRY_COLUMN_NAME:
1225 psd->fmt = LVCFMT_LEFT;
1226
1227 if (info->entryNameLength > 0)
1228 {
1229 return MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
1230 }
1231 return MakeStrRetFromString(L"(Default)", &(psd->str));
1232
1233 case REGISTRY_COLUMN_TYPE:
1234 psd->fmt = LVCFMT_LEFT;
1235
1236 if (info->entryType == REG_ENTRY_ROOT)
1237 {
1238 return MakeStrRetFromString(L"Key", &(psd->str));
1239 }
1240
1241 if (info->entryType == REG_ENTRY_KEY)
1242 {
1243 if (info->contentsLength > 0)
1244 {
1245 PWSTR td = (PWSTR) (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1246
1247 return MakeStrRetFromString(td, info->contentsLength, &(psd->str));
1248 }
1249
1250 return MakeStrRetFromString(L"Key", &(psd->str));
1251 }
1252
1253 return MakeStrRetFromString(RegistryTypeNames[info->entryType], &(psd->str));
1254
1255 case REGISTRY_COLUMN_VALUE:
1256 psd->fmt = LVCFMT_LEFT;
1257
1258 PCWSTR strValueContents;
1259
1260 hr = m_PidlManager->FormatContentsForDisplay(info, &strValueContents);
1261 if (FAILED_UNEXPECTEDLY(hr))
1262 return hr;
1263
1264 if (hr == S_FALSE)
1265 {
1266 return MakeStrRetFromString(L"(Empty)", &(psd->str));
1267 }
1268
1269 hr = MakeStrRetFromString(strValueContents, &(psd->str));
1270
1271 CoTaskMemFree((PVOID) strValueContents);
1272
1273 return hr;
1274 }
1275 }
1276 else
1277 {
1278 switch (iColumn)
1279 {
1280 case REGISTRY_COLUMN_NAME:
1281 psd->fmt = LVCFMT_LEFT;
1282 psd->cxChar = 30;
1283
1284 // TODO: Make localizable
1285 MakeStrRetFromString(L"Object Name", &(psd->str));
1286 return S_OK;
1287 case REGISTRY_COLUMN_TYPE:
1288 psd->fmt = LVCFMT_LEFT;
1289 psd->cxChar = 20;
1290
1291 // TODO: Make localizable
1292 MakeStrRetFromString(L"Content Type", &(psd->str));
1293 return S_OK;
1294 case REGISTRY_COLUMN_VALUE:
1295 psd->fmt = LVCFMT_LEFT;
1296 psd->cxChar = 20;
1297
1298 // TODO: Make localizable
1299 MakeStrRetFromString(L"Value", &(psd->str));
1300 return S_OK;
1301 }
1302 }
1303
1304 return E_INVALIDARG;
1305 }
1306
1307 HRESULT STDMETHODCALLTYPE CRegistryFolder::MapColumnToSCID(
1308 UINT iColumn,
1309 SHCOLUMNID *pscid)
1310 {
1311 static const GUID storage = PSGUID_STORAGE;
1312 switch (iColumn)
1313 {
1314 case REGISTRY_COLUMN_NAME:
1315 pscid->fmtid = storage;
1316 pscid->pid = PID_STG_NAME;
1317 return S_OK;
1318 case REGISTRY_COLUMN_TYPE:
1319 pscid->fmtid = storage;
1320 pscid->pid = PID_STG_STORAGETYPE;
1321 return S_OK;
1322 case REGISTRY_COLUMN_VALUE:
1323 pscid->fmtid = storage;
1324 pscid->pid = PID_STG_CONTENTS;
1325 return S_OK;
1326 }
1327 return E_INVALIDARG;
1328 }
1329
1330 HRESULT STDMETHODCALLTYPE CRegistryFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1331 {
1332 switch (uMsg)
1333 {
1334 case SFVM_DEFVIEWMODE:
1335 {
1336 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
1337 *pViewMode = FVM_DETAILS;
1338 return S_OK;
1339 }
1340 case SFVM_COLUMNCLICK:
1341 return S_FALSE;
1342 case SFVM_BACKGROUNDENUM:
1343 return S_OK;
1344 case SFVM_DEFITEMCOUNT:
1345 return m_PidlManager->GetCount((UINT*) lParam);
1346 }
1347 return E_NOTIMPL;
1348 }
1349
1350 HRESULT CRegistryFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1351 {
1352 switch (uMsg)
1353 {
1354 case DFM_MERGECONTEXTMENU:
1355 return S_OK;
1356 case DFM_INVOKECOMMAND:
1357 case DFM_INVOKECOMMANDEX:
1358 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1359 return S_FALSE;
1360 }
1361 return E_NOTIMPL;
1362 }