[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
11 WINE_DEFAULT_DEBUG_CHANNEL(ntobjshex);
12
13 // {1C6D6E08-2332-4A7B-A94D-6432DB2B5AE6}
14 const GUID CLSID_RegistryFolder = { 0x1c6d6e08, 0x2332, 0x4a7b, { 0xa9, 0x4d, 0x64, 0x32, 0xdb, 0x2b, 0x5a, 0xe6 } };
15
16 // {18A4B504-F6D8-4D8A-8661-6296514C2CF0}
17 static const GUID GUID_RegistryColumns = { 0x18a4b504, 0xf6d8, 0x4d8a, { 0x86, 0x61, 0x62, 0x96, 0x51, 0x4c, 0x2c, 0xf0 } };
18
19 enum RegistryColumns
20 {
21 REGISTRY_COLUMN_NAME = 0,
22 REGISTRY_COLUMN_TYPE,
23 REGISTRY_COLUMN_VALUE,
24 REGISTRY_COLUMN_END
25 };
26
27 // -------------------------------
28 // CRegistryFolderExtractIcon
29 CRegistryFolderExtractIcon::CRegistryFolderExtractIcon() :
30 m_pcidlFolder(NULL),
31 m_pcidlChild(NULL)
32 {
33
34 }
35
36 CRegistryFolderExtractIcon::~CRegistryFolderExtractIcon()
37 {
38 if (m_pcidlFolder)
39 ILFree((LPITEMIDLIST)m_pcidlFolder);
40 if (m_pcidlChild)
41 ILFree((LPITEMIDLIST)m_pcidlChild);
42 }
43
44 HRESULT CRegistryFolderExtractIcon::Initialize(LPCWSTR ntPath, PCIDLIST_ABSOLUTE parent, UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
45 {
46 m_pcidlFolder = ILClone(parent);
47 if (cidl != 1)
48 return E_INVALIDARG;
49 m_pcidlChild = ILClone(apidl[0]);
50 return S_OK;
51 }
52
53 HRESULT STDMETHODCALLTYPE CRegistryFolderExtractIcon::GetIconLocation(
54 UINT uFlags,
55 LPWSTR szIconFile,
56 UINT cchMax,
57 INT *piIndex,
58 UINT *pwFlags)
59 {
60 const RegPidlEntry * entry = (RegPidlEntry *)m_pcidlChild;
61
62 if ((entry->cb < sizeof(RegPidlEntry)) || (entry->magic != REGISTRY_PIDL_MAGIC))
63 return E_INVALIDARG;
64
65 UINT flags = 0;
66
67 switch (entry->entryType)
68 {
69 case REG_ENTRY_KEY:
70 case REG_ENTRY_ROOT:
71 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
72 *piIndex = -IDI_REGISTRYKEY;
73 *pwFlags = flags;
74 return S_OK;
75 case REG_ENTRY_VALUE:
76 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
77 *piIndex = -IDI_REGISTRYVALUE;
78 *pwFlags = flags;
79 return S_OK;
80 default:
81 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
82 *piIndex = -IDI_NTOBJECTITEM;
83 *pwFlags = flags;
84 return S_OK;
85 }
86 }
87
88 HRESULT STDMETHODCALLTYPE CRegistryFolderExtractIcon::Extract(
89 LPCWSTR pszFile,
90 UINT nIconIndex,
91 HICON *phiconLarge,
92 HICON *phiconSmall,
93 UINT nIconSize)
94 {
95 return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize);
96 }
97
98 // CRegistryFolder
99
100 CRegistryFolder::CRegistryFolder()
101 {
102 }
103
104 CRegistryFolder::~CRegistryFolder()
105 {
106 }
107
108 // IShellFolder
109 HRESULT STDMETHODCALLTYPE CRegistryFolder::EnumObjects(
110 HWND hwndOwner,
111 SHCONTF grfFlags,
112 IEnumIDList **ppenumIDList)
113 {
114 if (m_NtPath[0] == 0 && m_hRoot == NULL)
115 {
116 return GetEnumRegistryRoot(ppenumIDList);
117 }
118 else
119 {
120 return GetEnumRegistryKey(m_NtPath, m_hRoot, ppenumIDList);
121 }
122 }
123
124 HRESULT STDMETHODCALLTYPE CRegistryFolder::InternalBindToObject(
125 PWSTR path,
126 const RegPidlEntry * info,
127 LPITEMIDLIST first,
128 LPCITEMIDLIST rest,
129 LPITEMIDLIST fullPidl,
130 LPBC pbcReserved,
131 IShellFolder** ppsfChild)
132 {
133 if (wcslen(m_NtPath) == 0 && m_hRoot == NULL)
134 {
135 return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, L"", info->rootKey, IID_PPV_ARG(IShellFolder, ppsfChild));
136 }
137
138 return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, m_hRoot, IID_PPV_ARG(IShellFolder, ppsfChild));
139 }
140
141 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl)
142 {
143 m_shellPidl = ILClone(pidl);
144 m_hRoot = NULL;
145
146 StringCbCopy(m_NtPath, _countof(m_NtPath), L"");
147 return S_OK;
148 }
149
150 HRESULT STDMETHODCALLTYPE CRegistryFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath, HKEY hRoot)
151 {
152 m_shellPidl = ILClone(pidl);
153 m_hRoot = hRoot;
154
155 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
156 return S_OK;
157 }
158
159 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDefaultColumnState(
160 UINT iColumn,
161 SHCOLSTATEF *pcsFlags)
162 {
163 switch (iColumn)
164 {
165 case REGISTRY_COLUMN_NAME:
166 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
167 return S_OK;
168 case REGISTRY_COLUMN_TYPE:
169 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
170 return S_OK;
171 case REGISTRY_COLUMN_VALUE:
172 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
173 return S_OK;
174 }
175
176 return E_INVALIDARG;
177 }
178
179 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsEx(
180 LPCITEMIDLIST pidl,
181 const SHCOLUMNID *pscid,
182 VARIANT *pv)
183 {
184 const RegPidlEntry * info;
185
186 TRACE("GetDetailsEx\n");
187
188 if (pidl)
189 {
190 HRESULT hr = GetInfoFromPidl(pidl, &info);
191 if (FAILED_UNEXPECTEDLY(hr))
192 return hr;
193
194 static const GUID storage = PSGUID_STORAGE;
195 if (IsEqualGUID(pscid->fmtid, storage))
196 {
197 if (pscid->pid == PID_STG_NAME)
198 {
199 if (info->entryNameLength > 0)
200 {
201 return MakeVariantString(pv, info->entryName);
202 }
203 return MakeVariantString(pv, L"(Default)");
204 }
205 else if (pscid->pid == PID_STG_STORAGETYPE)
206 {
207 if (info->entryType == REG_ENTRY_ROOT)
208 {
209 return MakeVariantString(pv, L"Key");
210 }
211
212 if (info->entryType == REG_ENTRY_KEY)
213 {
214 if (info->contentsLength > 0)
215 {
216 PWSTR td = (PWSTR)(((PBYTE)info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
217
218 return MakeVariantString(pv, td);
219 }
220 return MakeVariantString(pv, L"Key");
221 }
222
223 return MakeVariantString(pv, RegistryTypeNames[info->contentType]);
224 }
225 else if (pscid->pid == PID_STG_CONTENTS)
226 {
227 PCWSTR strValueContents;
228
229 hr = FormatContentsForDisplay(info, m_hRoot, m_NtPath, &strValueContents);
230 if (FAILED_UNEXPECTEDLY(hr))
231 return hr;
232
233 if (hr == S_FALSE)
234 {
235 V_VT(pv) = VT_EMPTY;
236 return S_OK;
237 }
238
239 hr = MakeVariantString(pv, strValueContents);
240
241 CoTaskMemFree((PVOID)strValueContents);
242
243 return hr;
244
245 }
246 }
247 }
248
249 return E_INVALIDARG;
250 }
251
252 HRESULT STDMETHODCALLTYPE CRegistryFolder::GetDetailsOf(
253 LPCITEMIDLIST pidl,
254 UINT iColumn,
255 SHELLDETAILS *psd)
256 {
257 const RegPidlEntry * info;
258
259 TRACE("GetDetailsOf\n");
260
261 if (pidl)
262 {
263 HRESULT hr = GetInfoFromPidl(pidl, &info);
264 if (FAILED_UNEXPECTEDLY(hr))
265 return hr;
266
267 switch (iColumn)
268 {
269 case REGISTRY_COLUMN_NAME:
270 psd->fmt = LVCFMT_LEFT;
271
272 if (info->entryNameLength > 0)
273 {
274 return MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
275 }
276 return MakeStrRetFromString(L"(Default)", &(psd->str));
277
278 case REGISTRY_COLUMN_TYPE:
279 psd->fmt = LVCFMT_LEFT;
280
281 if (info->entryType == REG_ENTRY_ROOT)
282 {
283 return MakeStrRetFromString(L"Key", &(psd->str));
284 }
285
286 if (info->entryType == REG_ENTRY_KEY)
287 {
288 if (info->contentsLength > 0)
289 {
290 PWSTR td = (PWSTR)(((PBYTE)info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
291
292 return MakeStrRetFromString(td, info->contentsLength, &(psd->str));
293 }
294
295 return MakeStrRetFromString(L"Key", &(psd->str));
296 }
297
298 return MakeStrRetFromString(RegistryTypeNames[info->entryType], &(psd->str));
299
300 case REGISTRY_COLUMN_VALUE:
301 psd->fmt = LVCFMT_LEFT;
302
303 PCWSTR strValueContents;
304
305 hr = FormatContentsForDisplay(info, m_hRoot, m_NtPath, &strValueContents);
306 if (FAILED_UNEXPECTEDLY(hr))
307 return hr;
308
309 if (hr == S_FALSE)
310 {
311 return MakeStrRetFromString(L"(Empty)", &(psd->str));
312 }
313
314 hr = MakeStrRetFromString(strValueContents, &(psd->str));
315
316 CoTaskMemFree((PVOID)strValueContents);
317
318 return hr;
319 }
320 }
321 else
322 {
323 switch (iColumn)
324 {
325 case REGISTRY_COLUMN_NAME:
326 psd->fmt = LVCFMT_LEFT;
327 psd->cxChar = 30;
328
329 // TODO: Make localizable
330 MakeStrRetFromString(L"Object Name", &(psd->str));
331 return S_OK;
332 case REGISTRY_COLUMN_TYPE:
333 psd->fmt = LVCFMT_LEFT;
334 psd->cxChar = 20;
335
336 // TODO: Make localizable
337 MakeStrRetFromString(L"Content Type", &(psd->str));
338 return S_OK;
339 case REGISTRY_COLUMN_VALUE:
340 psd->fmt = LVCFMT_LEFT;
341 psd->cxChar = 20;
342
343 // TODO: Make localizable
344 MakeStrRetFromString(L"Value", &(psd->str));
345 return S_OK;
346 }
347 }
348
349 return E_INVALIDARG;
350 }
351
352 HRESULT STDMETHODCALLTYPE CRegistryFolder::MapColumnToSCID(
353 UINT iColumn,
354 SHCOLUMNID *pscid)
355 {
356 static const GUID storage = PSGUID_STORAGE;
357 switch (iColumn)
358 {
359 case REGISTRY_COLUMN_NAME:
360 pscid->fmtid = storage;
361 pscid->pid = PID_STG_NAME;
362 return S_OK;
363 case REGISTRY_COLUMN_TYPE:
364 pscid->fmtid = storage;
365 pscid->pid = PID_STG_STORAGETYPE;
366 return S_OK;
367 case REGISTRY_COLUMN_VALUE:
368 pscid->fmtid = storage;
369 pscid->pid = PID_STG_CONTENTS;
370 return S_OK;
371 }
372 return E_INVALIDARG;
373 }
374
375 HRESULT CRegistryFolder::CompareIDs(LPARAM lParam, const RegPidlEntry * first, const RegPidlEntry * second)
376 {
377 HRESULT hr;
378
379 LPARAM sortMode = lParam & 0xFFFF0000;
380 LPARAM column = lParam & 0x0000FFFF;
381
382 if (sortMode == SHCIDS_ALLFIELDS)
383 {
384 if (column != 0)
385 return E_INVALIDARG;
386
387 int minsize = min(first->cb, second->cb);
388 hr = MAKE_COMPARE_HRESULT(memcmp(second, first, minsize));
389 if (hr != S_EQUAL)
390 return hr;
391
392 return MAKE_COMPARE_HRESULT(second->cb - first->cb);
393 }
394
395 switch (column)
396 {
397 case REGISTRY_COLUMN_NAME:
398 return CompareName(lParam, first, second);
399
400 case REGISTRY_COLUMN_TYPE:
401 return MAKE_COMPARE_HRESULT(second->contentType - first->contentType);
402
403 case REGISTRY_COLUMN_VALUE:
404 // Can't sort by link target yet
405 return E_INVALIDARG;
406 }
407
408 DbgPrint("Unsupported sorting mode.\n");
409 return E_INVALIDARG;
410 }
411
412 ULONG CRegistryFolder::ConvertAttributes(const RegPidlEntry * entry, PULONG inMask)
413 {
414 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
415 ULONG flags = 0;
416
417 if ((entry->entryType == REG_ENTRY_KEY) ||
418 (entry->entryType == REG_ENTRY_ROOT))
419 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
420
421 return flags & mask;
422 }
423
424 BOOL CRegistryFolder::IsFolder(LPCITEMIDLIST pcidl)
425 {
426 RegPidlEntry * entry = (RegPidlEntry*) &(pcidl->mkid);
427 if ((entry->cb < sizeof(RegPidlEntry)) || (entry->magic != REGISTRY_PIDL_MAGIC))
428 return FALSE;
429
430 return IsFolder(entry);
431 }
432
433 BOOL CRegistryFolder::IsFolder(const RegPidlEntry * info)
434 {
435 return (info->entryType == REG_ENTRY_KEY) ||(info->entryType == REG_ENTRY_ROOT);
436 }
437
438 HRESULT CRegistryFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl, const RegPidlEntry ** pentry)
439 {
440 RegPidlEntry * entry = (RegPidlEntry*) &(pcidl->mkid);
441
442 if (entry->cb < sizeof(RegPidlEntry))
443 {
444 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(RegPidlEntry));
445 return E_INVALIDARG;
446 }
447
448 if (entry->magic != REGISTRY_PIDL_MAGIC)
449 {
450 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, REGISTRY_PIDL_MAGIC);
451 return E_INVALIDARG;
452 }
453
454 *pentry = entry;
455 return S_OK;
456 }
457
458 HRESULT CRegistryFolder::FormatValueData(DWORD contentType, PVOID td, DWORD contentsLength, PCWSTR * strContents)
459 {
460 switch (contentType)
461 {
462 case 0:
463 {
464 PCWSTR strTodo = L"";
465 DWORD bufferLength = (wcslen(strTodo) + 1) * sizeof(WCHAR);
466 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
467 StringCbCopyW(strValue, bufferLength, strTodo);
468 *strContents = strValue;
469 return S_OK;
470 }
471 case REG_SZ:
472 case REG_EXPAND_SZ:
473 {
474 PWSTR strValue = (PWSTR)CoTaskMemAlloc(contentsLength + sizeof(WCHAR));
475 StringCbCopyNW(strValue, contentsLength + sizeof(WCHAR), (LPCWSTR)td, contentsLength);
476 *strContents = strValue;
477 return S_OK;
478 }
479 case REG_MULTI_SZ:
480 {
481 PCWSTR separator = L" "; // To match regedit
482 size_t sepChars = wcslen(separator);
483 int strings = 0;
484 int stringChars = 0;
485
486 PCWSTR strData = (PCWSTR)td;
487 while (*strData)
488 {
489 size_t len = wcslen(strData);
490 stringChars += len;
491 strData += len + 1; // Skips null-terminator
492 strings++;
493 }
494
495 int cch = stringChars + (strings - 1) * sepChars + 1;
496
497 PWSTR strValue = (PWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
498
499 strValue[0] = 0;
500
501 strData = (PCWSTR)td;
502 while (*strData)
503 {
504 StrCatW(strValue, strData);
505 strData += wcslen(strData) + 1;
506 if (*strData)
507 StrCatW(strValue, separator);
508 }
509
510 *strContents = strValue;
511 return S_OK;
512 }
513 case REG_DWORD:
514 {
515 DWORD bufferLength = 64 * sizeof(WCHAR);
516 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
517 StringCbPrintfW(strValue, bufferLength, L"0x%08x (%d)",
518 *(DWORD*)td, *(DWORD*)td);
519 *strContents = strValue;
520 return S_OK;
521 }
522 case REG_QWORD:
523 {
524 DWORD bufferLength = 64 * sizeof(WCHAR);
525 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
526 StringCbPrintfW(strValue, bufferLength, L"0x%016llx (%lld)",
527 *(LARGE_INTEGER*)td, ((LARGE_INTEGER*)td)->QuadPart);
528 *strContents = strValue;
529 return S_OK;
530 }
531 case REG_BINARY:
532 {
533 DWORD bufferLength = (contentsLength * 3 + 1) * sizeof(WCHAR);
534 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
535 PWSTR strTemp = strValue;
536 PBYTE data = (PBYTE)td;
537 for (DWORD i = 0; i < contentsLength; i++)
538 {
539 StringCbPrintfW(strTemp, bufferLength, L"%02x ", data[i]);
540 strTemp += 3;
541 bufferLength -= 3;
542 }
543 *strContents = strValue;
544 return S_OK;
545 }
546 default:
547 {
548 PCWSTR strFormat = L"<Unimplemented value type %d>";
549 DWORD bufferLength = (wcslen(strFormat) + 15) * sizeof(WCHAR);
550 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
551 StringCbPrintfW(strValue, bufferLength, strFormat, contentType);
552 *strContents = strValue;
553 return S_OK;
554 }
555 }
556 }
557
558 HRESULT CRegistryFolder::FormatContentsForDisplay(const RegPidlEntry * info, HKEY rootKey, LPCWSTR ntPath, PCWSTR * strContents)
559 {
560 PVOID td = (((PBYTE)info) + FIELD_OFFSET(RegPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
561
562 if (info->entryType == REG_ENTRY_VALUE_WITH_CONTENT)
563 {
564 if (info->contentsLength > 0)
565 {
566 return FormatValueData(info->contentType, td, info->contentsLength, strContents);
567 }
568 }
569 else if (info->entryType == REG_ENTRY_VALUE)
570 {
571 PVOID valueData;
572 DWORD valueLength;
573 HRESULT hr = ReadRegistryValue(rootKey, ntPath, info->entryName, &valueData, &valueLength);
574 if (FAILED_UNEXPECTEDLY(hr))
575 {
576 PCWSTR strEmpty = L"(Error reading value)";
577 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
578 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
579 StringCbCopyW(strValue, bufferLength, strEmpty);
580 *strContents = strValue;
581 return S_OK;
582 }
583
584 if (valueLength > 0)
585 {
586 hr = FormatValueData(info->contentType, valueData, valueLength, strContents);
587
588 CoTaskMemFree(valueData);
589
590 return hr;
591 }
592 }
593 else
594 {
595 PCWSTR strEmpty = L"";
596 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
597 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
598 StringCbCopyW(strValue, bufferLength, strEmpty);
599 *strContents = strValue;
600 return S_OK;
601 }
602
603 PCWSTR strEmpty = L"(Empty)";
604 DWORD bufferLength = (wcslen(strEmpty) + 1) * sizeof(WCHAR);
605 PWSTR strValue = (PWSTR)CoTaskMemAlloc(bufferLength);
606 StringCbCopyW(strValue, bufferLength, strEmpty);
607 *strContents = strValue;
608 return S_OK;
609 }