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