[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/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_MULTI_SZ:
309 {
310 PCWSTR separator = L" "; // To match regedit
311 int sepChars = wcslen(separator);
312 int strings = 0;
313 int stringChars = 0;
314
315 PCWSTR strData = (PCWSTR)td;
316 while (*strData)
317 {
318 int l = wcslen(strData);
319 stringChars += l;
320 strData += l + 1; // Skips null-terminator
321 strings++;
322 }
323
324 int cch = stringChars + (strings - 1) * sepChars + 1;
325
326 PWSTR strValue = (PWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
327
328 strValue[0] = 0;
329
330 strData = (PCWSTR)td;
331 while (*strData)
332 {
333 StrCatW(strValue, strData);
334 strData += wcslen(strData) + 1;
335 if (*strData)
336 StrCatW(strValue, separator);
337 }
338
339 *strContents = strValue;
340 return S_OK;
341 }
342 case REG_DWORD:
343 {
344 DWORD bufferLength = 64 * sizeof(WCHAR);
345 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
346 StringCbPrintfW(strValue, bufferLength, L"0x%08x (%d)",
347 *(DWORD*) td, *(DWORD*) td);
348 *strContents = strValue;
349 return S_OK;
350 }
351 case REG_QWORD:
352 {
353 DWORD bufferLength = 64 * sizeof(WCHAR);
354 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
355 StringCbPrintfW(strValue, bufferLength, L"0x%016llx (%lld)",
356 *(LARGE_INTEGER*) td, ((LARGE_INTEGER*) td)->QuadPart);
357 *strContents = strValue;
358 return S_OK;
359 }
360 case REG_BINARY:
361 {
362 DWORD bufferLength = (contentsLength * 3 + 1) * sizeof(WCHAR);
363 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
364 PWSTR strTemp = strValue;
365 PBYTE data = (PBYTE)td;
366 for (int i = 0; i < contentsLength; i++)
367 {
368 StringCbPrintfW(strTemp, bufferLength, L"%02x ", data[i]);
369 strTemp += 3;
370 bufferLength -= 3;
371 }
372 *strContents = strValue;
373 return S_OK;
374 }
375 default:
376 {
377 PCWSTR strFormat = L"<Unimplemented value type %d>";
378 DWORD bufferLength = (wcslen(strFormat) + 15) * sizeof(WCHAR);
379 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
380 StringCbPrintfW(strValue, bufferLength, strFormat, contentType);
381 *strContents = strValue;
382 return S_OK;
383 }
384 }
385 }
386
387 static HRESULT FormatContentsForDisplay(const RegPidlEntry * info, HKEY rootKey, LPCWSTR ntPath, PCWSTR * strContents)
388 {
389 PVOID td = (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
390
391 if (info->entryType == REG_ENTRY_VALUE_WITH_CONTENT)
392 {
393 if (info->contentsLength > 0)
394 {
395 return FormatValueData(info->contentType, td, info->contentsLength, strContents);
396 }
397 }
398 else if (info->entryType == REG_ENTRY_VALUE)
399 {
400 PVOID valueData;
401 DWORD valueLength;
402 HRESULT hr = ReadRegistryValue(rootKey, ntPath, info->entryName, &valueData, &valueLength);
403 if (FAILED_UNEXPECTEDLY(hr))
404 {
405 PCWSTR strEmpty = L"(Error reading value)";
406 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
407 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
408 StringCbCopyW(strValue, bufferLength, strEmpty);
409 *strContents = strValue;
410 return S_OK;
411 }
412
413 if (valueLength > 0)
414 {
415 hr = FormatValueData(info->contentType, valueData, valueLength, strContents);
416
417 CoTaskMemFree(valueData);
418
419 return hr;
420 }
421 }
422 else
423 {
424 PCWSTR strEmpty = L"";
425 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
426 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
427 StringCbCopyW(strValue, bufferLength, strEmpty);
428 *strContents = strValue;
429 return S_OK;
430 }
431
432 PCWSTR strEmpty = L"(Empty)";
433 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
434 PWSTR strValue = (PWSTR) CoTaskMemAlloc(bufferLength);
435 StringCbCopyW(strValue, bufferLength, strEmpty);
436 *strContents = strValue;
437 return S_OK;
438 }
439 };
440
441 //-----------------------------------------------------------------------------
442 // CRegistryFolder
443
444 CRegistryFolder::CRegistryFolder() :
445 m_shellPidl(NULL)
446 {
447 }
448
449 CRegistryFolder::~CRegistryFolder()
450 {
451 if (m_shellPidl)
452 ILFree(m_shellPidl);
453 }
454
455 // IShellFolder
456 HRESULT STDMETHODCALLTYPE CRegistryFolder::ParseDisplayName(
457 HWND hwndOwner,
458 LPBC pbcReserved,
459 LPOLESTR lpszDisplayName,
460 ULONG *pchEaten,
461 LPITEMIDLIST *ppidl,
462 ULONG *pdwAttributes)
463 {
464 if (!ppidl)
465 return E_POINTER;
466
467 if (pchEaten)
468 *pchEaten = 0;
469
470 if (pdwAttributes)
471 *pdwAttributes = 0;
472
473 TRACE("CRegistryFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
474
475 const RegPidlEntry * info;
476 IEnumIDList * it;
477 HRESULT hr = GetEnumNTDirectory(m_NtPath, &it);
478 if (FAILED(hr))
479 {
480 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
481 }
482
483 while (TRUE)
484 {
485 hr = it->Next(1, ppidl, NULL);
486
487 if (FAILED(hr))
488 return hr;
489
490 if (hr != S_OK)
491 break;
492
493 hr = CRegistryPidlHelper::GetInfoFromPidl(*ppidl, &info);
494 if (FAILED_UNEXPECTEDLY(hr))
495 return hr;
496
497 if (StrCmpW(info->entryName, lpszDisplayName) == 0)
498 break;
499 }
500
501 if (hr != S_OK)
502 {
503 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
504 }
505
506 if (pchEaten || pdwAttributes)
507 {
508 if (pchEaten)
509 *pchEaten = wcslen(info->entryName);
510
511 if (pdwAttributes)
512 *pdwAttributes = CRegistryPidlHelper::ConvertAttributes(info, pdwAttributes);
513 }
514
515 return S_OK;
516 }
517
518 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumObjects(
519 HWND hwndOwner,
520 SHCONTF grfFlags,
521 IEnumIDList **ppenumIDList)
522 {
523 if (m_NtPath[0] == 0 && m_hRoot == NULL)
524 {
525 return GetEnumRegistryRoot(ppenumIDList);
526 }
527 else
528 {
529 return GetEnumRegistryKey(m_NtPath, m_hRoot, ppenumIDList);
530 }
531 }
532
533 HRESULT STDMETHODCALLTYPE CRegistryFolder::BindToObject(
534 LPCITEMIDLIST pidl,
535 LPBC pbcReserved,
536 REFIID riid,
537 void **ppvOut)
538 {
539 const RegPidlEntry * info;
540
541 if (IsEqualIID(riid, IID_IShellFolder))
542 {
543 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
544 if (FAILED_UNEXPECTEDLY(hr))
545 return hr;
546
547 LPITEMIDLIST first = ILCloneFirst(pidl);
548 LPCITEMIDLIST rest = ILGetNext(pidl);
549
550 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
551
552 CComPtr<IShellFolder> psfChild;
553 if (wcslen(m_NtPath) == 0 && m_hRoot == NULL)
554 {
555 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, L"", info->rootKey, IID_PPV_ARG(IShellFolder, &psfChild));
556 }
557 else
558 {
559 WCHAR path[MAX_PATH];
560
561 StringCbCopyW(path, _countof(path), m_NtPath);
562
563 PathAppendW(path, info->entryName);
564
565 hr = ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, m_hRoot, IID_PPV_ARG(IShellFolder, &psfChild));
566 }
567
568 ILFree(fullPidl);
569 ILFree(first);
570
571 if (rest->mkid.cb > 0)
572 {
573 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
574 }
575
576 return psfChild->QueryInterface(riid, ppvOut);
577 }
578
579 return E_NOTIMPL;
580 }
581
582 HRESULT STDMETHODCALLTYPE CRegistryFolder::BindToStorage(
583 LPCITEMIDLIST pidl,
584 LPBC pbcReserved,
585 REFIID riid,
586 void **ppvObj)
587 {
588 UNIMPLEMENTED;
589 return E_NOTIMPL;
590 }
591
592 HRESULT STDMETHODCALLTYPE CRegistryFolder::CompareIDs(
593 LPARAM lParam,
594 LPCITEMIDLIST pidl1,
595 LPCITEMIDLIST pidl2)
596 {
597 TRACE("CompareIDs\n");
598
599 HRESULT hr = CRegistryPidlHelper::CompareIDs(lParam, pidl1, pidl2);
600 if (hr != S_OK)
601 return hr;
602
603 LPCITEMIDLIST rest1 = ILGetNext(pidl1);
604 LPCITEMIDLIST rest2 = ILGetNext(pidl2);
605
606 bool hasNext1 = (rest1->mkid.cb > 0);
607 bool hasNext2 = (rest2->mkid.cb > 0);
608
609 if (hasNext1 || hasNext2)
610 {
611 if (hasNext1 && !hasNext2)
612 return MAKE_HRESULT(0, 0, (USHORT) -1);
613
614 if (hasNext2 && !hasNext1)
615 return MAKE_HRESULT(0, 0, (USHORT) 1);
616
617 LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
618
619 CComPtr<IShellFolder> psfNext;
620 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
621 if (FAILED_UNEXPECTEDLY(hr))
622 return hr;
623
624 return psfNext->CompareIDs(lParam, rest1, rest2);
625 }
626
627 return S_OK;
628 }
629
630 HRESULT STDMETHODCALLTYPE CRegistryFolder::CreateViewObject(
631 HWND hwndOwner,
632 REFIID riid,
633 void **ppvOut)
634 {
635 if (!IsEqualIID(riid, IID_IShellView))
636 return E_NOINTERFACE;
637
638 SFV_CREATE sfv;
639 sfv.cbSize = sizeof(sfv);
640 sfv.pshf = this;
641 sfv.psvOuter = NULL;
642 sfv.psfvcb = this;
643
644 return SHCreateShellFolderView(&sfv, (IShellView**) ppvOut);
645 }
646
647 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetAttributesOf(
648 UINT cidl,
649 PCUITEMID_CHILD_ARRAY apidl,
650 SFGAOF *rgfInOut)
651 {
652 const RegPidlEntry * info;
653
654 TRACE("GetAttributesOf\n");
655
656 if (cidl == 0)
657 {
658 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
659 return S_OK;
660 }
661
662 for (int i = 0; i < (int) cidl; i++)
663 {
664 PCUITEMID_CHILD pidl = apidl[i];
665
666 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
667 if (FAILED_UNEXPECTEDLY(hr))
668 return hr;
669
670 // Update attributes.
671 *rgfInOut = CRegistryPidlHelper::ConvertAttributes(info, rgfInOut);
672 }
673
674 return S_OK;
675 }
676
677 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetUIObjectOf(
678 HWND hwndOwner,
679 UINT cidl,
680 PCUITEMID_CHILD_ARRAY apidl,
681 REFIID riid,
682 UINT *prgfInOut,
683 void **ppvOut)
684 {
685 TRACE("GetUIObjectOf\n");
686
687 if (IsEqualIID(riid, IID_IContextMenu) ||
688 IsEqualIID(riid, IID_IContextMenu2) ||
689 IsEqualIID(riid, IID_IContextMenu3))
690 {
691 CComPtr<IContextMenu> pcm;
692
693 DWORD res;
694 HKEY keys[1];
695
696 int nkeys = _countof(keys);
697 if (cidl == 1 && CRegistryPidlHelper::IsFolder(apidl[0]))
698 {
699 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
700 if (!NT_SUCCESS(res))
701 return HRESULT_FROM_NT(res);
702 }
703 else
704 {
705 nkeys = 0;
706 }
707
708 HRESULT hr = CDefFolderMenu_Create2(m_shellPidl, hwndOwner, cidl, apidl, this, DefCtxMenuCallback, nkeys, keys, &pcm);
709 if (FAILED_UNEXPECTEDLY(hr))
710 return hr;
711
712 return pcm->QueryInterface(riid, ppvOut);
713 }
714
715 if (IsEqualIID(riid, IID_IExtractIconW))
716 {
717 return ShellObjectCreatorInit<CRegistryFolderExtractIcon>(m_shellPidl, cidl, apidl, riid, ppvOut);
718 }
719
720 if (IsEqualIID(riid, IID_IDataObject))
721 {
722 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**) ppvOut);
723 }
724
725 if (IsEqualIID(riid, IID_IQueryAssociations))
726 {
727 if (cidl == 1 && CRegistryPidlHelper::IsFolder(apidl[0]))
728 {
729 CComPtr<IQueryAssociations> pqa;
730 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
731 if (FAILED_UNEXPECTEDLY(hr))
732 return hr;
733
734 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.RegFolder", NULL, hwndOwner);
735 if (FAILED_UNEXPECTEDLY(hr))
736 return hr;
737
738 return pqa->QueryInterface(riid, ppvOut);
739 }
740 }
741
742 return E_NOTIMPL;
743 }
744
745 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDisplayNameOf(
746 LPCITEMIDLIST pidl,
747 SHGDNF uFlags,
748 STRRET *lpName)
749 {
750 const RegPidlEntry * info;
751
752 TRACE("GetDisplayNameOf %p\n", pidl);
753
754 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
755 if (FAILED_UNEXPECTEDLY(hr))
756 return hr;
757
758 if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
759 {
760 hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
761 if (FAILED_UNEXPECTEDLY(hr))
762 return hr;
763 }
764
765 WCHAR path[MAX_PATH] = { 0 };
766
767 if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
768 {
769 if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
770 {
771 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
772 if (FAILED_UNEXPECTEDLY(hr))
773 return hr;
774 }
775 }
776
777 PathAppendW(path, info->entryName);
778
779 LPCITEMIDLIST pidlNext = ILGetNext(pidl);
780 if (pidlNext && pidlNext->mkid.cb > 0)
781 {
782 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
783
784 CComPtr<IShellFolder> psfChild;
785 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
786 if (FAILED_UNEXPECTEDLY(hr))
787 return hr;
788
789 WCHAR temp[MAX_PATH];
790 STRRET childName;
791
792 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
793 if (FAILED_UNEXPECTEDLY(hr))
794 return hr;
795
796 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
797 if (FAILED_UNEXPECTEDLY(hr))
798 return hr;
799
800 PathAppendW(path, temp);
801
802 ILFree(pidlFirst);
803 }
804
805 hr = MakeStrRetFromString(path, lpName);
806 if (FAILED_UNEXPECTEDLY(hr))
807 return hr;
808
809 return S_OK;
810 }
811
812 HRESULT STDMETHODCALLTYPE CRegistryFolder::SetNameOf(
813 HWND hwnd,
814 LPCITEMIDLIST pidl,
815 LPCOLESTR lpszName,
816 SHGDNF uFlags,
817 LPITEMIDLIST *ppidlOut)
818 {
819 UNIMPLEMENTED;
820 return E_NOTIMPL;
821 }
822
823 // IPersist
824 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetClassID(CLSID *lpClassId)
825 {
826 if (!lpClassId)
827 return E_POINTER;
828
829 *lpClassId = CLSID_RegistryFolder;
830 return S_OK;
831 }
832
833 // IPersistFolder
834 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl)
835 {
836 m_shellPidl = ILClone(pidl);
837 m_hRoot = NULL;
838
839 StringCbCopy(m_NtPath, _countof(m_NtPath), L"");
840 return S_OK;
841 }
842
843 // Internal
844 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath, HKEY hRoot)
845 {
846 m_shellPidl = ILClone(pidl);
847 m_hRoot = hRoot;
848
849 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
850 return S_OK;
851 }
852
853 // IPersistFolder2
854 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetCurFolder(LPITEMIDLIST * pidl)
855 {
856 if (pidl)
857 *pidl = ILClone(m_shellPidl);
858 if (!m_shellPidl)
859 return S_FALSE;
860 return S_OK;
861 }
862
863 // IShellFolder2
864 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultSearchGUID(
865 GUID *lpguid)
866 {
867 UNIMPLEMENTED;
868 return E_NOTIMPL;
869 }
870
871 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumSearches(
872 IEnumExtraSearch **ppenum)
873 {
874 UNIMPLEMENTED;
875 return E_NOTIMPL;
876 }
877
878 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumn(
879 DWORD dwReserved,
880 ULONG *pSort,
881 ULONG *pDisplay)
882 {
883 if (pSort)
884 *pSort = 0;
885 if (pDisplay)
886 *pDisplay = 0;
887 return S_OK;
888 }
889
890 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumnState(
891 UINT iColumn,
892 SHCOLSTATEF *pcsFlags)
893 {
894 switch (iColumn)
895 {
896 case REGISTRY_COLUMN_NAME:
897 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
898 return S_OK;
899 case REGISTRY_COLUMN_TYPE:
900 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
901 return S_OK;
902 case REGISTRY_COLUMN_VALUE:
903 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
904 return S_OK;
905 }
906
907 return E_INVALIDARG;
908 }
909
910 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsEx(
911 LPCITEMIDLIST pidl,
912 const SHCOLUMNID *pscid,
913 VARIANT *pv)
914 {
915 const RegPidlEntry * info;
916
917 TRACE("GetDetailsEx\n");
918
919 if (pidl)
920 {
921 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
922 if (FAILED_UNEXPECTEDLY(hr))
923 return hr;
924
925 static const GUID storage = PSGUID_STORAGE;
926 if (IsEqualGUID(pscid->fmtid, storage))
927 {
928 if (pscid->pid == PID_STG_NAME)
929 {
930 if (info->entryNameLength > 0)
931 {
932 return MakeVariantString(pv, info->entryName);
933 }
934 return MakeVariantString(pv, L"(Default)");
935 }
936 else if (pscid->pid == PID_STG_STORAGETYPE)
937 {
938 if (info->entryType == REG_ENTRY_ROOT)
939 {
940 return MakeVariantString(pv, L"Key");
941 }
942
943 if (info->entryType == REG_ENTRY_KEY)
944 {
945 if (info->contentsLength > 0)
946 {
947 PWSTR td = (PWSTR) (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
948
949 return MakeVariantString(pv, td);
950 }
951 return MakeVariantString(pv, L"Key");
952 }
953
954 return MakeVariantString(pv, RegistryTypeNames[info->contentType]);
955 }
956 else if (pscid->pid == PID_STG_CONTENTS)
957 {
958 PCWSTR strValueContents;
959
960 hr = CRegistryPidlHelper::FormatContentsForDisplay(info, m_hRoot, m_NtPath, &strValueContents);
961 if (FAILED_UNEXPECTEDLY(hr))
962 return hr;
963
964 if (hr == S_FALSE)
965 {
966 V_VT(pv) = VT_EMPTY;
967 return S_OK;
968 }
969
970 hr = MakeVariantString(pv, strValueContents);
971
972 CoTaskMemFree((PVOID) strValueContents);
973
974 return hr;
975
976 }
977 }
978 }
979
980 return E_INVALIDARG;
981 }
982
983 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsOf(
984 LPCITEMIDLIST pidl,
985 UINT iColumn,
986 SHELLDETAILS *psd)
987 {
988 const RegPidlEntry * info;
989
990 TRACE("GetDetailsOf\n");
991
992 if (pidl)
993 {
994 HRESULT hr = CRegistryPidlHelper::GetInfoFromPidl(pidl, &info);
995 if (FAILED_UNEXPECTEDLY(hr))
996 return hr;
997
998 switch (iColumn)
999 {
1000 case REGISTRY_COLUMN_NAME:
1001 psd->fmt = LVCFMT_LEFT;
1002
1003 if (info->entryNameLength > 0)
1004 {
1005 return MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
1006 }
1007 return MakeStrRetFromString(L"(Default)", &(psd->str));
1008
1009 case REGISTRY_COLUMN_TYPE:
1010 psd->fmt = LVCFMT_LEFT;
1011
1012 if (info->entryType == REG_ENTRY_ROOT)
1013 {
1014 return MakeStrRetFromString(L"Key", &(psd->str));
1015 }
1016
1017 if (info->entryType == REG_ENTRY_KEY)
1018 {
1019 if (info->contentsLength > 0)
1020 {
1021 PWSTR td = (PWSTR) (((PBYTE) info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
1022
1023 return MakeStrRetFromString(td, info->contentsLength, &(psd->str));
1024 }
1025
1026 return MakeStrRetFromString(L"Key", &(psd->str));
1027 }
1028
1029 return MakeStrRetFromString(RegistryTypeNames[info->entryType], &(psd->str));
1030
1031 case REGISTRY_COLUMN_VALUE:
1032 psd->fmt = LVCFMT_LEFT;
1033
1034 PCWSTR strValueContents;
1035
1036 hr = CRegistryPidlHelper::FormatContentsForDisplay(info, m_hRoot, m_NtPath, &strValueContents);
1037 if (FAILED_UNEXPECTEDLY(hr))
1038 return hr;
1039
1040 if (hr == S_FALSE)
1041 {
1042 return MakeStrRetFromString(L"(Empty)", &(psd->str));
1043 }
1044
1045 hr = MakeStrRetFromString(strValueContents, &(psd->str));
1046
1047 CoTaskMemFree((PVOID) strValueContents);
1048
1049 return hr;
1050 }
1051 }
1052 else
1053 {
1054 switch (iColumn)
1055 {
1056 case REGISTRY_COLUMN_NAME:
1057 psd->fmt = LVCFMT_LEFT;
1058 psd->cxChar = 30;
1059
1060 // TODO: Make localizable
1061 MakeStrRetFromString(L"Object Name", &(psd->str));
1062 return S_OK;
1063 case REGISTRY_COLUMN_TYPE:
1064 psd->fmt = LVCFMT_LEFT;
1065 psd->cxChar = 20;
1066
1067 // TODO: Make localizable
1068 MakeStrRetFromString(L"Content Type", &(psd->str));
1069 return S_OK;
1070 case REGISTRY_COLUMN_VALUE:
1071 psd->fmt = LVCFMT_LEFT;
1072 psd->cxChar = 20;
1073
1074 // TODO: Make localizable
1075 MakeStrRetFromString(L"Value", &(psd->str));
1076 return S_OK;
1077 }
1078 }
1079
1080 return E_INVALIDARG;
1081 }
1082
1083 HRESULT STDMETHODCALLTYPE CRegistryFolder::MapColumnToSCID(
1084 UINT iColumn,
1085 SHCOLUMNID *pscid)
1086 {
1087 static const GUID storage = PSGUID_STORAGE;
1088 switch (iColumn)
1089 {
1090 case REGISTRY_COLUMN_NAME:
1091 pscid->fmtid = storage;
1092 pscid->pid = PID_STG_NAME;
1093 return S_OK;
1094 case REGISTRY_COLUMN_TYPE:
1095 pscid->fmtid = storage;
1096 pscid->pid = PID_STG_STORAGETYPE;
1097 return S_OK;
1098 case REGISTRY_COLUMN_VALUE:
1099 pscid->fmtid = storage;
1100 pscid->pid = PID_STG_CONTENTS;
1101 return S_OK;
1102 }
1103 return E_INVALIDARG;
1104 }
1105
1106 HRESULT STDMETHODCALLTYPE CRegistryFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1107 {
1108 switch (uMsg)
1109 {
1110 case SFVM_DEFVIEWMODE:
1111 {
1112 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*) lParam;
1113 *pViewMode = FVM_DETAILS;
1114 return S_OK;
1115 }
1116 case SFVM_COLUMNCLICK:
1117 return S_FALSE;
1118 case SFVM_BACKGROUNDENUM:
1119 return S_OK;
1120 }
1121 return E_NOTIMPL;
1122 }
1123
1124 HRESULT CRegistryFolder::DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
1125 {
1126 switch (uMsg)
1127 {
1128 case DFM_MERGECONTEXTMENU:
1129 return S_OK;
1130 case DFM_INVOKECOMMAND:
1131 case DFM_INVOKECOMMANDEX:
1132 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
1133 return S_FALSE;
1134 }
1135 return E_NOTIMPL;
1136 }