6a688c10349cbf6093e1bf5e44182fde8b81bef5
[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/regfolder.cpp
5 * PURPOSE: NT Object Namespace shell extension
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 */
8
9 #include "precomp.h"
10 #include "ntobjenum.h"
11 #include <ntquery.h>
12 #include "util.h"
13
14 #define DFM_MERGECONTEXTMENU 1 // uFlags LPQCMINFO
15 #define DFM_INVOKECOMMAND 2 // idCmd pszArgs
16 #define DFM_INVOKECOMMANDEX 12 // idCmd PDFMICS
17 #define DFM_GETDEFSTATICID 14 // idCmd * 0
18
19 #define SHCIDS_ALLFIELDS 0x80000000L
20 #define SHCIDS_CANONICALONLY 0x10000000L
21
22 #define GET_SHGDN_FOR(dwFlags) ((DWORD)dwFlags & (DWORD)0x0000FF00)
23 #define GET_SHGDN_RELATION(dwFlags) ((DWORD)dwFlags & (DWORD)0x000000FF)
24
25 WINE_DEFAULT_DEBUG_CHANNEL(ntobjshex);
26
27 // {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 CRegistryPidlHelper
128 {
129 public:
130 static HRESULT CompareIDs(LPARAM lParam, const RegPidlEntry * first, const RegPidlEntry * second)
131 {
132 if ((lParam & 0xFFFF0000) == SHCIDS_ALLFIELDS)
133 {
134 if (lParam != 0)
135 return E_INVALIDARG;
136
137 int minsize = min(first->cb, second->cb);
138 int ord = memcmp(second, first, minsize);
139
140 if (ord != 0)
141 return MAKE_HRESULT(0, 0, (USHORT) ord);
142
143 if (second->cb > first->cb)
144 return MAKE_HRESULT(0, 0, (USHORT) 1);
145 if (second->cb < first->cb)
146 return MAKE_HRESULT(0, 0, (USHORT) -1);
147 }
148 else
149 {
150 bool canonical = ((lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY);
151
152 switch (lParam & 0xFFFF)
153 {
154 case REGISTRY_COLUMN_NAME:
155 {
156 bool f1 = (first->entryType == REG_ENTRY_KEY) || (first->entryType == REG_ENTRY_ROOT);
157 bool f2 = (second->entryType == REG_ENTRY_KEY) || (second->entryType == REG_ENTRY_ROOT);
158
159 if (f1 && !f2)
160 return MAKE_HRESULT(0, 0, (USHORT) -1);
161 if (f2 && !f1)
162 return MAKE_HRESULT(0, 0, (USHORT) 1);
163
164 if (canonical)
165 {
166 // Shortcut: avoid comparing contents if not necessary when the results are not for display.
167 if (second->entryNameLength > first->entryNameLength)
168 return MAKE_HRESULT(0, 0, (USHORT) 1);
169 if (second->entryNameLength < first->entryNameLength)
170 return MAKE_HRESULT(0, 0, (USHORT) -1);
171
172 int minlength = min(first->entryNameLength, second->entryNameLength);
173 if (minlength > 0)
174 {
175 int ord = memcmp(first->entryName, second->entryName, minlength);
176 if (ord != 0)
177 return MAKE_HRESULT(0, 0, (USHORT) ord);
178 }
179 return S_OK;
180 }
181 else
182 {
183 int minlength = min(first->entryNameLength, second->entryNameLength);
184 if (minlength > 0)
185 {
186 int ord = StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR));
187 if (ord != 0)
188 return MAKE_HRESULT(0, 0, (USHORT) ord);
189 }
190
191 if (second->entryNameLength > first->entryNameLength)
192 return MAKE_HRESULT(0, 0, (USHORT) 1);
193 if (second->entryNameLength < first->entryNameLength)
194 return MAKE_HRESULT(0, 0, (USHORT) -1);
195
196 return S_OK;
197 }
198 }
199 case REGISTRY_COLUMN_TYPE:
200 {
201 int ord = second->contentType - first->contentType;
202 if (ord > 0)
203 return MAKE_HRESULT(0, 0, (USHORT) 1);
204 if (ord < 0)
205 return MAKE_HRESULT(0, 0, (USHORT) -1);
206
207 return S_OK;
208 }
209 case REGISTRY_COLUMN_VALUE:
210 {
211 // Can't sort by value
212 return E_INVALIDARG;
213 }
214 default:
215 {
216 DbgPrint("Unsupported sorting mode.\n");
217 return E_INVALIDARG;
218 }
219 }
220 }
221
222 return E_INVALIDARG;
223 }
224
225 static HRESULT CompareIDs(LPARAM lParam, const RegPidlEntry * first, LPCITEMIDLIST pcidl)
226 {
227 LPCITEMIDLIST p = pcidl;
228 RegPidlEntry * second = (RegPidlEntry*) &(p->mkid);
229 if ((second->cb < sizeof(RegPidlEntry)) || (second->magic != REGISTRY_PIDL_MAGIC))
230 return E_INVALIDARG;
231
232 return CompareIDs(lParam, first, second);
233 }
234
235 static HRESULT CompareIDs(LPARAM lParam, LPCITEMIDLIST pcidl1, LPCITEMIDLIST pcidl2)
236 {
237 LPCITEMIDLIST p = pcidl1;
238 RegPidlEntry * first = (RegPidlEntry*) &(p->mkid);
239 if ((first->cb < sizeof(RegPidlEntry)) || (first->magic != REGISTRY_PIDL_MAGIC))
240 return E_INVALIDARG;
241
242 return CompareIDs(lParam, first, pcidl2);
243 }
244
245 static ULONG ConvertAttributes(const RegPidlEntry * entry, PULONG inMask)
246 {
247 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
248 ULONG flags = 0;
249
250 if ((entry->entryType == REG_ENTRY_KEY) ||
251 (entry->entryType == REG_ENTRY_ROOT))
252 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
253
254 return flags & mask;
255 }
256
257 static BOOL IsFolder(LPCITEMIDLIST pcidl)
258 {
259 RegPidlEntry * entry = (RegPidlEntry*) &(pcidl->mkid);
260 if ((entry->cb < sizeof(RegPidlEntry)) || (entry->magic != REGISTRY_PIDL_MAGIC))
261 return FALSE;
262
263 return (entry->entryType == REG_ENTRY_KEY) ||
264 (entry->entryType == REG_ENTRY_ROOT);
265 }
266
267 static HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const RegPidlEntry ** pentry)
268 {
269 RegPidlEntry * entry = (RegPidlEntry*) &(pcidl->mkid);
270
271 if (entry->cb < sizeof(RegPidlEntry))
272 {
273 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(RegPidlEntry));
274 return E_INVALIDARG;
275 }
276
277 if (entry->magic != REGISTRY_PIDL_MAGIC)
278 {
279 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, REGISTRY_PIDL_MAGIC);
280 return E_INVALIDARG;
281 }
282
283 *pentry = entry;
284 return S_OK;
285 }
286
287 static HRESULT FormatValueData(DWORD contentType, PVOID td, DWORD contentsLength, PCWSTR * strContents)
288 {
289 switch (contentType)
290 {
291 case 0:
292 {
293 PCWSTR strTodo = L"";
294 DWORD bufferLength = (wcslen(strTodo) + 1) * sizeof(WCHAR);
295 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
296 StringCbCopyW(strValue, bufferLength, strTodo);
297 *strContents = strValue;
298 return S_OK;
299 }
300 case REG_SZ:
301 case REG_EXPAND_SZ:
302 {
303 PWSTR strValue = (PWSTR) CoTaskMemAlloc(contentsLength + sizeof(WCHAR));
304 StringCbCopyNW(strValue, contentsLength + sizeof(WCHAR), (LPCWSTR) td, contentsLength);
305 *strContents = strValue;
306 return S_OK;
307 }
308 case REG_DWORD:
309 {
310 DWORD bufferLength = 64 * sizeof(WCHAR);
311 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
312 StringCbPrintfW(strValue, bufferLength, L"0x%08x (%d)",
313 *(DWORD*) td, *(DWORD*) td);
314 *strContents = strValue;
315 return S_OK;
316 }
317 case REG_QWORD:
318 {
319 DWORD bufferLength = 64 * sizeof(WCHAR);
320 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
321 StringCbPrintfW(strValue, bufferLength, L"0x%016llx (%d)",
322 *(LARGE_INTEGER*) td, ((LARGE_INTEGER*) td)->QuadPart);
323 *strContents = strValue;
324 return S_OK;
325 }
326 default:
327 {
328 PCWSTR strTodo = L"<TODO: Convert value for display>";
329 DWORD bufferLength = (wcslen(strTodo) + 1) * sizeof(WCHAR);
330 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
331 StringCbCopyW(strValue, bufferLength, strTodo);
332 *strContents = strValue;
333 return S_OK;
334 }
335 }
336 }
337
338 static HRESULT FormatContentsForDisplay(const RegPidlEntry * info, LPCWSTR ntPath, PCWSTR * strContents)
339 {
340 PVOID td = (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
341
342 if (info->entryType == REG_ENTRY_VALUE_WITH_CONTENT)
343 {
344 if (info->contentsLength > 0)
345 {
346 return FormatValueData(info->contentType, td, info->contentsLength, strContents);
347 }
348 }
349 else if (info->entryType == REG_ENTRY_VALUE)
350 {
351 PVOID valueData;
352 DWORD valueLength;
353 HRESULT hr = ReadRegistryValue(NULL, ntPath, info->entryName, &valueData, &valueLength);
354 if (FAILED_UNEXPECTEDLY(hr))
355 {
356 PCWSTR strEmpty = L"(Error reading value)";
357 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
358 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
359 StringCbCopyW(strValue, bufferLength, strEmpty);
360 *strContents = strValue;
361 return S_OK;
362 }
363
364 if (valueLength > 0)
365 {
366 hr = FormatValueData(info->contentType, valueData, valueLength, strContents);
367
368 CoTaskMemFree(valueData);
369
370 return hr;
371 }
372 }
373 else
374 {
375 PCWSTR strEmpty = L"";
376 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
377 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
378 StringCbCopyW(strValue, bufferLength, strEmpty);
379 *strContents = strValue;
380 return S_OK;
381 }
382
383 PCWSTR strEmpty = L"(Empty)";
384 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
385 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
386 StringCbCopyW(strValue, bufferLength, strEmpty);
387 *strContents = strValue;
388 return S_OK;
389 }
390 };
391
392 //-----------------------------------------------------------------------------
393 // CRegistryFolder
394
395 CRegistryFolder::CRegistryFolder() :
396 m_shellPidl(NULL)
397 {
398 }
399
400 CRegistryFolder::~CRegistryFolder()
401 {
402 if (m_shellPidl)
403 ILFree(m_shellPidl);
404 }
405
406 // IShellFolder
407 HRESULT STDMETHODCALLTYPE CRegistryFolder::ParseDisplayName(
408 HWND hwndOwner,
409 LPBC pbcReserved,
410 LPOLESTR lpszDisplayName,
411 ULONG *pchEaten,
412 LPITEMIDLIST *ppidl,
413 ULONG *pdwAttributes)
414 {
415 if (!ppidl)
416 return E_POINTER;
417
418 if (pchEaten)
419 *pchEaten = 0;
420
421 if (pdwAttributes)
422 *pdwAttributes = 0;
423
424 TRACE("CRegistryFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
425
426 const RegPidlEntry * info;
427 IEnumIDList * it;
428 HRESULT hr = GetEnumNTDirectory(m_NtPath, &it);
429 if (FAILED(hr))
430 {
431 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
432 }
433
434 while (TRUE)
435 {
436 hr = it->Next(1, ppidl, NULL);
437
438 if (FAILED(hr))
439 return hr;
440
441 if (hr != S_OK)
442 break;
443
444 hr = CRegistryPidlHelper::GetInfoFromPidl(*ppidl, &info);
445 if (FAILED_UNEXPECTEDLY(hr))
446 return hr;
447
448 if (StrCmpW(info->entryName, lpszDisplayName) == 0)
449 break;
450 }
451
452 if (hr != S_OK)
453 {
454 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
455 }
456
457 if (pchEaten || pdwAttributes)
458 {
459 if (pchEaten)
460 *pchEaten = wcslen(info->entryName);
461
462 if (pdwAttributes)
463 *pdwAttributes = CRegistryPidlHelper::ConvertAttributes(info, pdwAttributes);
464 }
465
466 return S_OK;
467 }
468
469 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumObjects(
470 HWND hwndOwner,
471 SHCONTF grfFlags,
472 IEnumIDList **ppenumIDList)
473 {
474 if (m_NtPath[0] == 0 && m_hRoot == NULL)
475 {
476 return GetEnumRegistryRoot(ppenumIDList);
477 }
478 else
479 {
480 return GetEnumRegistryKey(m_NtPath, m_hRoot, ppenumIDList);
481 }
482 }
483
484 HRESULT STDMETHODCALLTYPE CRegistryFolder::BindToObject(
485 LPCITEMIDLIST pidl,
486 LPBC pbcReserved,
487 REFIID riid,
488 void **ppvOut)
489 {
490 const RegPidlEntry * info;
491
492 if (IsEqualIID(riid, IID_IShellFolder))
493 {
494 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
495 if (FAILED_UNEXPECTEDLY(hr))
496 return hr;
497
498 LPITEMIDLIST first = ILCloneFirst(pidl);
499 LPCITEMIDLIST rest = ILGetNext(pidl);
500
501 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
502
503 CComPtr<IShellFolder> psfChild;
504 if (wcslen(m_NtPath) == 0 && m_hRoot == NULL)
505 {
506 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, L"", info->rootKey, IID_PPV_ARG(IShellFolder, &psfChild));
507 }
508 else
509 {
510 WCHAR path[MAX_PATH];
511
512 StringCbCopyW(path, _countof(path), m_NtPath);
513
514 PathAppendW(path, info->entryName);
515
516 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, m_hRoot, IID_PPV_ARG(IShellFolder, &psfChild));
517 }
518
519 ILFree(fullPidl);
520 ILFree(first);
521
522 if (rest->mkid.cb > 0)
523 {
524 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
525 }
526
527 return psfChild->QueryInterface(riid, ppvOut);
528 }
529
530 return E_NOTIMPL;
531 }
532
533 HRESULT STDMETHODCALLTYPE CRegistryFolder::BindToStorage(
534 LPCITEMIDLIST pidl,
535 LPBC pbcReserved,
536 REFIID riid,
537 void **ppvObj)
538 {
539 UNIMPLEMENTED;
540 return E_NOTIMPL;
541 }
542
543 HRESULT STDMETHODCALLTYPE CRegistryFolder::CompareIDs(
544 LPARAM lParam,
545 LPCITEMIDLIST pidl1,
546 LPCITEMIDLIST pidl2)
547 {
548 TRACE("CompareIDs\n");
549
550 HRESULT hr = CRegistryPidlHelper::CompareIDs(lParam, pidl1, pidl2);
551 if (hr != S_OK)
552 return hr;
553
554 LPCITEMIDLIST rest1 = ILGetNext(pidl1);
555 LPCITEMIDLIST rest2 = ILGetNext(pidl2);
556
557 bool hasNext1 = (rest1->mkid.cb > 0);
558 bool hasNext2 = (rest2->mkid.cb > 0);
559
560 if (hasNext1 || hasNext2)
561 {
562 if (hasNext1 && !hasNext2)
563 return MAKE_HRESULT(0, 0, (USHORT) -1);
564
565 if (hasNext2 && !hasNext1)
566 return MAKE_HRESULT(0, 0, (USHORT) 1);
567
568 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
569
570 CComPtr<IShellFolder> psfNext;
571 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
572 if (FAILED_UNEXPECTEDLY(hr))
573 return hr;
574
575 return psfNext->CompareIDs(lParam, rest1, rest2);
576 }
577
578 return S_OK;
579 }
580
581 HRESULT STDMETHODCALLTYPE CRegistryFolder::CreateViewObject(
582 HWND hwndOwner,
583 REFIID riid,
584 void **ppvOut)
585 {
586 if (!IsEqualIID(riid, IID_IShellView))
587 return E_NOINTERFACE;
588
589 SFV_CREATE sfv;
590 sfv.cbSize = sizeof(sfv);
591 sfv.pshf = this;
592 sfv.psvOuter = NULL;
593 sfv.psfvcb = this;
594
595 return SHCreateShellFolderView(&sfv, (IShellView**) ppvOut);
596 }
597
598 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetAttributesOf(
599 UINT cidl,
600 PCUITEMID_CHILD_ARRAY apidl,
601 SFGAOF *rgfInOut)
602 {
603 const RegPidlEntry * info;
604
605 TRACE("GetAttributesOf\n");
606
607 if (cidl == 0)
608 {
609 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
610 return S_OK;
611 }
612
613 for (int i = 0; i < (int) cidl; i++)
614 {
615 PCUITEMID_CHILD pidl = apidl[i];
616
617 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
618 if (FAILED_UNEXPECTEDLY(hr))
619 return hr;
620
621 // Update attributes.
622 *rgfInOut = CRegistryPidlHelper::ConvertAttributes(info, rgfInOut);
623 }
624
625 return S_OK;
626 }
627
628 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetUIObjectOf(
629 HWND hwndOwner,
630 UINT cidl,
631 PCUITEMID_CHILD_ARRAY apidl,
632 REFIID riid,
633 UINT *prgfInOut,
634 void **ppvOut)
635 {
636 TRACE("GetUIObjectOf\n");
637
638 if (IsEqualIID(riid, IID_IContextMenu) ||
639 IsEqualIID(riid, IID_IContextMenu2) ||
640 IsEqualIID(riid, IID_IContextMenu3))
641 {
642 CComPtr<IContextMenu> pcm;
643
644 DWORD res;
645 HKEY keys[1];
646
647 int nkeys = _countof(keys);
648 if (cidl == 1 && CRegistryPidlHelper::IsFolder(apidl[0]))
649 {
650 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
651 if (!NT_SUCCESS(res))
652 return HRESULT_FROM_NT(res);
653 }
654 else
655 {
656 nkeys = 0;
657 }
658
659 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
660 if (FAILED_UNEXPECTEDLY(hr))
661 return hr;
662
663 return pcm->QueryInterface(riid, ppvOut);
664 }
665
666 if (IsEqualIID(riid, IID_IExtractIconW))
667 {
668 return ShellObjectCreatorInit<CRegistryFolderExtractIcon>(m_shellPidl, cidl, apidl, riid, ppvOut);
669 }
670
671 if (IsEqualIID(riid, IID_IDataObject))
672 {
673 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**) ppvOut);
674 }
675
676 if (IsEqualIID(riid, IID_IQueryAssociations))
677 {
678 if (cidl == 1 && CRegistryPidlHelper::IsFolder(apidl[0]))
679 {
680 CComPtr<IQueryAssociations> pqa;
681 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
682 if (FAILED_UNEXPECTEDLY(hr))
683 return hr;
684
685 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.RegFolder", NULL, hwndOwner);
686 if (FAILED_UNEXPECTEDLY(hr))
687 return hr;
688
689 return pqa->QueryInterface(riid, ppvOut);
690 }
691 }
692
693 return E_NOTIMPL;
694 }
695
696 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDisplayNameOf(
697 LPCITEMIDLIST pidl,
698 SHGDNF uFlags,
699 STRRET *lpName)
700 {
701 const RegPidlEntry * info;
702
703 TRACE("GetDisplayNameOf %p\n", pidl);
704
705 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
706 if (FAILED_UNEXPECTEDLY(hr))
707 return hr;
708
709 if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
710 {
711 hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
712 if (FAILED_UNEXPECTEDLY(hr))
713 return hr;
714 }
715
716 WCHAR path[MAX_PATH] = { 0 };
717
718 if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
719 {
720 if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
721 {
722 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
723 if (FAILED_UNEXPECTEDLY(hr))
724 return hr;
725 }
726 }
727
728 PathAppendW(path, info->entryName);
729
730 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
731 if (pidlNext && pidlNext->mkid.cb > 0)
732 {
733 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
734
735 CComPtr<IShellFolder> psfChild;
736 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
737 if (FAILED_UNEXPECTEDLY(hr))
738 return hr;
739
740 WCHAR temp[MAX_PATH];
741 STRRET childName;
742
743 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
744 if (FAILED_UNEXPECTEDLY(hr))
745 return hr;
746
747 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
748 if (FAILED_UNEXPECTEDLY(hr))
749 return hr;
750
751 PathAppendW(path, temp);
752
753 ILFree(pidlFirst);
754 }
755
756 hr = MakeStrRetFromString(path, lpName);
757 if (FAILED_UNEXPECTEDLY(hr))
758 return hr;
759
760 return S_OK;
761 }
762
763 HRESULT STDMETHODCALLTYPE CRegistryFolder::SetNameOf(
764 HWND hwnd,
765 LPCITEMIDLIST pidl,
766 LPCOLESTR lpszName,
767 SHGDNF uFlags,
768 LPITEMIDLIST *ppidlOut)
769 {
770 UNIMPLEMENTED;
771 return E_NOTIMPL;
772 }
773
774 // IPersist
775 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetClassID(CLSID *lpClassId)
776 {
777 if (!lpClassId)
778 return E_POINTER;
779
780 *lpClassId = CLSID_RegistryFolder;
781 return S_OK;
782 }
783
784 // IPersistFolder
785 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl)
786 {
787 m_shellPidl = ILClone(pidl);
788 m_hRoot = NULL;
789
790 StringCbCopy(m_NtPath, _countof(m_NtPath), L"");
791 return S_OK;
792 }
793
794 // Internal
795 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath, HKEY hRoot)
796 {
797 m_shellPidl = ILClone(pidl);
798 m_hRoot = hRoot;
799
800 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
801 return S_OK;
802 }
803
804 // IPersistFolder2
805 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetCurFolder(LPITEMIDLIST * pidl)
806 {
807 if (pidl)
808 *pidl = ILClone(m_shellPidl);
809 if (!m_shellPidl)
810 return S_FALSE;
811 return S_OK;
812 }
813
814 // IShellFolder2
815 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultSearchGUID(
816 GUID *lpguid)
817 {
818 UNIMPLEMENTED;
819 return E_NOTIMPL;
820 }
821
822 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumSearches(
823 IEnumExtraSearch **ppenum)
824 {
825 UNIMPLEMENTED;
826 return E_NOTIMPL;
827 }
828
829 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumn(
830 DWORD dwReserved,
831 ULONG *pSort,
832 ULONG *pDisplay)
833 {
834 if (pSort)
835 *pSort = 0;
836 if (pDisplay)
837 *pDisplay = 0;
838 return S_OK;
839 }
840
841 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumnState(
842 UINT iColumn,
843 SHCOLSTATEF *pcsFlags)
844 {
845 switch (iColumn)
846 {
847 case REGISTRY_COLUMN_NAME:
848 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
849 return S_OK;
850 case REGISTRY_COLUMN_TYPE:
851 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
852 return S_OK;
853 case REGISTRY_COLUMN_VALUE:
854 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
855 return S_OK;
856 }
857
858 return E_INVALIDARG;
859 }
860
861 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsEx(
862 LPCITEMIDLIST pidl,
863 const SHCOLUMNID *pscid,
864 VARIANT *pv)
865 {
866 const RegPidlEntry * info;
867
868 TRACE("GetDetailsEx\n");
869
870 if (pidl)
871 {
872 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
873 if (FAILED_UNEXPECTEDLY(hr))
874 return hr;
875
876 static const GUID storage = PSGUID_STORAGE;
877 if (IsEqualGUID(pscid->fmtid, storage))
878 {
879 if (pscid->pid == PID_STG_NAME)
880 {
881 if (info->entryNameLength > 0)
882 {
883 return MakeVariantString(pv, info->entryName);
884 }
885 return MakeVariantString(pv, L"(Default)");
886 }
887 else if (pscid->pid == PID_STG_STORAGETYPE)
888 {
889 if (info->entryType == REG_ENTRY_ROOT)
890 {
891 return MakeVariantString(pv, L"Key");
892 }
893
894 if (info->entryType == REG_ENTRY_KEY)
895 {
896 if (info->contentsLength > 0)
897 {
898 PWSTR td = (PWSTR) (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
899
900 return MakeVariantString(pv, td);
901 }
902 return MakeVariantString(pv, L"Key");
903 }
904
905 return MakeVariantString(pv, RegistryTypeNames[info->contentType]);
906 }
907 else if (pscid->pid == PID_STG_CONTENTS)
908 {
909 PCWSTR strValueContents;
910
911 hr = CRegistryPidlHelper::FormatContentsForDisplay(info, m_NtPath, &strValueContents);
912 if (FAILED_UNEXPECTEDLY(hr))
913 return hr;
914
915 if (hr == S_FALSE)
916 {
917 V_VT(pv) = VT_EMPTY;
918 return S_OK;
919 }
920
921 hr = MakeVariantString(pv, strValueContents);
922
923 CoTaskMemFree((PVOID) strValueContents);
924
925 return hr;
926
927 }
928 }
929 }
930
931 return E_INVALIDARG;
932 }
933
934 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsOf(
935 LPCITEMIDLIST pidl,
936 UINT iColumn,
937 SHELLDETAILS *psd)
938 {
939 const RegPidlEntry * info;
940
941 TRACE("GetDetailsOf\n");
942
943 if (pidl)
944 {
945 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
946 if (FAILED_UNEXPECTEDLY(hr))
947 return hr;
948
949 switch (iColumn)
950 {
951 case REGISTRY_COLUMN_NAME:
952 psd->fmt = LVCFMT_LEFT;
953
954 if (info->entryNameLength > 0)
955 {
956 return MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
957 }
958 return MakeStrRetFromString(L"(Default)", &(psd->str));
959
960 case REGISTRY_COLUMN_TYPE:
961 psd->fmt = LVCFMT_LEFT;
962
963 if (info->entryType == REG_ENTRY_ROOT)
964 {
965 return MakeStrRetFromString(L"Key", &(psd->str));
966 }
967
968 if (info->entryType == REG_ENTRY_KEY)
969 {
970 if (info->contentsLength > 0)
971 {
972 PWSTR td = (PWSTR) (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
973
974 return MakeStrRetFromString(td, info->contentsLength, &(psd->str));
975 }
976
977 return MakeStrRetFromString(L"Key", &(psd->str));
978 }
979
980 return MakeStrRetFromString(RegistryTypeNames[info->entryType], &(psd->str));
981
982 case REGISTRY_COLUMN_VALUE:
983 psd->fmt = LVCFMT_LEFT;
984
985 PCWSTR strValueContents;
986
987 hr = CRegistryPidlHelper::FormatContentsForDisplay(info, m_NtPath, &strValueContents);
988 if (FAILED_UNEXPECTEDLY(hr))
989 return hr;
990
991 if (hr == S_FALSE)
992 {
993 return MakeStrRetFromString(L"(Empty)", &(psd->str));
994 }
995
996 hr = MakeStrRetFromString(strValueContents, &(psd->str));
997
998 CoTaskMemFree((PVOID) strValueContents);
999
1000 return hr;
1001 }
1002 }
1003 else
1004 {
1005 switch (iColumn)
1006 {
1007 case REGISTRY_COLUMN_NAME:
1008 psd->fmt = LVCFMT_LEFT;
1009 psd->cxChar = 30;
1010
1011 // TODO: Make localizable
1012 MakeStrRetFromString(L"Object Name", &(psd->str));
1013 return S_OK;
1014 case REGISTRY_COLUMN_TYPE:
1015 psd->fmt = LVCFMT_LEFT;
1016 psd->cxChar = 20;
1017
1018 // TODO: Make localizable
1019 MakeStrRetFromString(L"Content Type", &(psd->str));
1020 return S_OK;
1021 case REGISTRY_COLUMN_VALUE:
1022 psd->fmt = LVCFMT_LEFT;
1023 psd->cxChar = 20;
1024
1025 // TODO: Make localizable
1026 MakeStrRetFromString(L"Value", &(psd->str));
1027 return S_OK;
1028 }
1029 }
1030
1031 return E_INVALIDARG;
1032 }
1033
1034 HRESULT STDMETHODCALLTYPE CRegistryFolder::MapColumnToSCID(
1035 UINT iColumn,
1036 SHCOLUMNID *pscid)
1037 {
1038 static const GUID storage = PSGUID_STORAGE;
1039 switch (iColumn)
1040 {
1041 case REGISTRY_COLUMN_NAME:
1042 pscid->fmtid = storage;
1043 pscid->pid = PID_STG_NAME;
1044 return S_OK;
1045 case REGISTRY_COLUMN_TYPE:
1046 pscid->fmtid = storage;
1047 pscid->pid = PID_STG_STORAGETYPE;
1048 return S_OK;
1049 case REGISTRY_COLUMN_VALUE:
1050 pscid->fmtid = storage;
1051 pscid->pid = PID_STG_CONTENTS;
1052 return S_OK;
1053 }
1054 return E_INVALIDARG;
1055 }
1056
1057 HRESULT STDMETHODCALLTYPE CRegistryFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1058 {
1059 switch (uMsg)
1060 {
1061 case SFVM_DEFVIEWMODE:
1062 {
1063 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
1064 *pViewMode = FVM_DETAILS;
1065 return S_OK;
1066 }
1067 case SFVM_COLUMNCLICK:
1068 return S_FALSE;
1069 case SFVM_BACKGROUNDENUM:
1070 return S_OK;
1071 }
1072 return E_NOTIMPL;
1073 }
1074
1075 HRESULT CRegistryFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1076 {
1077 switch (uMsg)
1078 {
1079 case DFM_MERGECONTEXTMENU:
1080 return S_OK;
1081 case DFM_INVOKECOMMAND:
1082 case DFM_INVOKECOMMANDEX:
1083 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1084 return S_FALSE;
1085 }
1086 return E_NOTIMPL;
1087 }