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