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>
11 // {1C6D6E08-2332-4A7B-A94D-6432DB2B5AE6}
12 const GUID CLSID_RegistryFolder
= { 0x1c6d6e08, 0x2332, 0x4a7b, { 0xa9, 0x4d, 0x64, 0x32, 0xdb, 0x2b, 0x5a, 0xe6 } };
14 // {18A4B504-F6D8-4D8A-8661-6296514C2CF0}
15 static const GUID GUID_RegistryColumns
= { 0x18a4b504, 0xf6d8, 0x4d8a, { 0x86, 0x61, 0x62, 0x96, 0x51, 0x4c, 0x2c, 0xf0 } };
19 REGISTRY_COLUMN_NAME
= 0,
21 REGISTRY_COLUMN_VALUE
,
25 // -------------------------------
26 // CRegistryFolderExtractIcon
27 CRegistryFolderExtractIcon::CRegistryFolderExtractIcon() :
34 CRegistryFolderExtractIcon::~CRegistryFolderExtractIcon()
37 ILFree((LPITEMIDLIST
)m_pcidlFolder
);
39 ILFree((LPITEMIDLIST
)m_pcidlChild
);
42 HRESULT
CRegistryFolderExtractIcon::Initialize(LPCWSTR ntPath
, PCIDLIST_ABSOLUTE parent
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
)
44 m_pcidlFolder
= ILClone(parent
);
47 m_pcidlChild
= ILClone(apidl
[0]);
51 HRESULT STDMETHODCALLTYPE
CRegistryFolderExtractIcon::GetIconLocation(
58 const RegPidlEntry
* entry
= (RegPidlEntry
*)m_pcidlChild
;
60 if ((entry
->cb
< sizeof(RegPidlEntry
)) || (entry
->magic
!= REGISTRY_PIDL_MAGIC
))
65 switch (entry
->entryType
)
69 GetModuleFileNameW(g_hInstance
, szIconFile
, cchMax
);
70 *piIndex
= -IDI_REGISTRYKEY
;
74 GetModuleFileNameW(g_hInstance
, szIconFile
, cchMax
);
75 *piIndex
= -IDI_REGISTRYVALUE
;
79 GetModuleFileNameW(g_hInstance
, szIconFile
, cchMax
);
80 *piIndex
= -IDI_NTOBJECTITEM
;
86 HRESULT STDMETHODCALLTYPE
CRegistryFolderExtractIcon::Extract(
93 return SHDefExtractIconW(pszFile
, nIconIndex
, 0, phiconLarge
, phiconSmall
, nIconSize
);
98 CRegistryFolder::CRegistryFolder()
102 CRegistryFolder::~CRegistryFolder()
107 HRESULT STDMETHODCALLTYPE
CRegistryFolder::EnumObjects(
110 IEnumIDList
**ppenumIDList
)
112 if (m_NtPath
[0] == 0 && m_hRoot
== NULL
)
114 return GetEnumRegistryRoot(ppenumIDList
);
118 return GetEnumRegistryKey(m_NtPath
, m_hRoot
, ppenumIDList
);
122 HRESULT STDMETHODCALLTYPE
CRegistryFolder::InternalBindToObject(
124 const RegPidlEntry
* info
,
127 LPITEMIDLIST fullPidl
,
129 IShellFolder
** ppsfChild
)
131 if (wcslen(m_NtPath
) == 0 && m_hRoot
== NULL
)
133 return ShellObjectCreatorInit
<CRegistryFolder
>(fullPidl
, L
"", info
->rootKey
, IID_PPV_ARG(IShellFolder
, ppsfChild
));
136 return ShellObjectCreatorInit
<CRegistryFolder
>(fullPidl
, path
, m_hRoot
, IID_PPV_ARG(IShellFolder
, ppsfChild
));
139 HRESULT STDMETHODCALLTYPE
CRegistryFolder::Initialize(LPCITEMIDLIST pidl
)
141 m_shellPidl
= ILClone(pidl
);
144 StringCbCopy(m_NtPath
, _countof(m_NtPath
), L
"");
148 HRESULT STDMETHODCALLTYPE
CRegistryFolder::Initialize(LPCITEMIDLIST pidl
, PCWSTR ntPath
, HKEY hRoot
)
150 m_shellPidl
= ILClone(pidl
);
153 StringCbCopy(m_NtPath
, _countof(m_NtPath
), ntPath
);
157 HRESULT STDMETHODCALLTYPE
CRegistryFolder::GetDefaultColumnState(
159 SHCOLSTATEF
*pcsFlags
)
163 case REGISTRY_COLUMN_NAME
:
164 *pcsFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
166 case REGISTRY_COLUMN_TYPE
:
167 *pcsFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
169 case REGISTRY_COLUMN_VALUE
:
170 *pcsFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
| SHCOLSTATE_SLOW
;
177 HRESULT STDMETHODCALLTYPE
CRegistryFolder::GetDetailsEx(
179 const SHCOLUMNID
*pscid
,
182 const RegPidlEntry
* info
;
184 TRACE("GetDetailsEx\n");
188 HRESULT hr
= GetInfoFromPidl(pidl
, &info
);
189 if (FAILED_UNEXPECTEDLY(hr
))
192 static const GUID storage
= PSGUID_STORAGE
;
193 if (IsEqualGUID(pscid
->fmtid
, storage
))
195 if (pscid
->pid
== PID_STG_NAME
)
197 if (info
->entryNameLength
> 0)
199 return MakeVariantString(pv
, info
->entryName
);
201 return MakeVariantString(pv
, L
"(Default)");
203 else if (pscid
->pid
== PID_STG_STORAGETYPE
)
205 if (info
->entryType
== REG_ENTRY_ROOT
)
207 return MakeVariantString(pv
, L
"Key");
210 if (info
->entryType
== REG_ENTRY_KEY
)
212 if (info
->contentsLength
> 0)
214 PWSTR td
= (PWSTR
)(((PBYTE
)info
) + FIELD_OFFSET(RegPidlEntry
, entryName
) + info
->entryNameLength
+ sizeof(WCHAR
));
216 return MakeVariantString(pv
, td
);
218 return MakeVariantString(pv
, L
"Key");
221 return MakeVariantString(pv
, RegistryTypeNames
[info
->contentType
]);
223 else if (pscid
->pid
== PID_STG_CONTENTS
)
225 PCWSTR strValueContents
;
227 hr
= FormatContentsForDisplay(info
, m_hRoot
, m_NtPath
, &strValueContents
);
228 if (FAILED_UNEXPECTEDLY(hr
))
237 hr
= MakeVariantString(pv
, strValueContents
);
239 CoTaskMemFree((PVOID
)strValueContents
);
250 HRESULT STDMETHODCALLTYPE
CRegistryFolder::GetDetailsOf(
255 const RegPidlEntry
* info
;
257 TRACE("GetDetailsOf\n");
261 HRESULT hr
= GetInfoFromPidl(pidl
, &info
);
262 if (FAILED_UNEXPECTEDLY(hr
))
267 case REGISTRY_COLUMN_NAME
:
268 psd
->fmt
= LVCFMT_LEFT
;
270 if (info
->entryNameLength
> 0)
272 return MakeStrRetFromString(info
->entryName
, info
->entryNameLength
, &(psd
->str
));
274 return MakeStrRetFromString(L
"(Default)", &(psd
->str
));
276 case REGISTRY_COLUMN_TYPE
:
277 psd
->fmt
= LVCFMT_LEFT
;
279 if (info
->entryType
== REG_ENTRY_ROOT
)
281 return MakeStrRetFromString(L
"Key", &(psd
->str
));
284 if (info
->entryType
== REG_ENTRY_KEY
)
286 if (info
->contentsLength
> 0)
288 PWSTR td
= (PWSTR
)(((PBYTE
)info
) + FIELD_OFFSET(RegPidlEntry
, entryName
) + info
->entryNameLength
+ sizeof(WCHAR
));
290 return MakeStrRetFromString(td
, info
->contentsLength
, &(psd
->str
));
293 return MakeStrRetFromString(L
"Key", &(psd
->str
));
296 return MakeStrRetFromString(RegistryTypeNames
[info
->entryType
], &(psd
->str
));
298 case REGISTRY_COLUMN_VALUE
:
299 psd
->fmt
= LVCFMT_LEFT
;
301 PCWSTR strValueContents
;
303 hr
= FormatContentsForDisplay(info
, m_hRoot
, m_NtPath
, &strValueContents
);
304 if (FAILED_UNEXPECTEDLY(hr
))
309 return MakeStrRetFromString(L
"(Empty)", &(psd
->str
));
312 hr
= MakeStrRetFromString(strValueContents
, &(psd
->str
));
314 CoTaskMemFree((PVOID
)strValueContents
);
323 case REGISTRY_COLUMN_NAME
:
324 psd
->fmt
= LVCFMT_LEFT
;
327 // TODO: Make localizable
328 MakeStrRetFromString(L
"Object Name", &(psd
->str
));
330 case REGISTRY_COLUMN_TYPE
:
331 psd
->fmt
= LVCFMT_LEFT
;
334 // TODO: Make localizable
335 MakeStrRetFromString(L
"Content Type", &(psd
->str
));
337 case REGISTRY_COLUMN_VALUE
:
338 psd
->fmt
= LVCFMT_LEFT
;
341 // TODO: Make localizable
342 MakeStrRetFromString(L
"Value", &(psd
->str
));
350 HRESULT STDMETHODCALLTYPE
CRegistryFolder::MapColumnToSCID(
354 static const GUID storage
= PSGUID_STORAGE
;
357 case REGISTRY_COLUMN_NAME
:
358 pscid
->fmtid
= storage
;
359 pscid
->pid
= PID_STG_NAME
;
361 case REGISTRY_COLUMN_TYPE
:
362 pscid
->fmtid
= storage
;
363 pscid
->pid
= PID_STG_STORAGETYPE
;
365 case REGISTRY_COLUMN_VALUE
:
366 pscid
->fmtid
= storage
;
367 pscid
->pid
= PID_STG_CONTENTS
;
373 HRESULT
CRegistryFolder::CompareIDs(LPARAM lParam
, const RegPidlEntry
* first
, const RegPidlEntry
* second
)
377 DWORD sortMode
= lParam
& 0xFFFF0000;
378 DWORD column
= lParam
& 0x0000FFFF;
380 if (sortMode
== SHCIDS_ALLFIELDS
)
385 int minsize
= min(first
->cb
, second
->cb
);
386 hr
= MAKE_COMPARE_HRESULT(memcmp(second
, first
, minsize
));
390 return MAKE_COMPARE_HRESULT(second
->cb
- first
->cb
);
395 case REGISTRY_COLUMN_NAME
:
396 return CompareName(lParam
, first
, second
);
398 case REGISTRY_COLUMN_TYPE
:
399 return MAKE_COMPARE_HRESULT(second
->contentType
- first
->contentType
);
401 case REGISTRY_COLUMN_VALUE
:
402 // Can't sort by link target yet
406 DbgPrint("Unsupported sorting mode.\n");
410 ULONG
CRegistryFolder::ConvertAttributes(const RegPidlEntry
* entry
, PULONG inMask
)
412 ULONG mask
= inMask
? *inMask
: 0xFFFFFFFF;
415 if ((entry
->entryType
== REG_ENTRY_KEY
) ||
416 (entry
->entryType
== REG_ENTRY_ROOT
))
417 flags
|= SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
| SFGAO_BROWSABLE
;
422 BOOL
CRegistryFolder::IsFolder(const RegPidlEntry
* info
)
424 return (info
->entryType
== REG_ENTRY_KEY
) ||(info
->entryType
== REG_ENTRY_ROOT
);
427 HRESULT
CRegistryFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl
, const RegPidlEntry
** pentry
)
429 RegPidlEntry
* entry
= (RegPidlEntry
*) &(pcidl
->mkid
);
431 if (entry
->cb
< sizeof(RegPidlEntry
))
433 DbgPrint("PCIDL too small %l (required %l)\n", entry
->cb
, sizeof(RegPidlEntry
));
437 if (entry
->magic
!= REGISTRY_PIDL_MAGIC
)
439 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry
->magic
, REGISTRY_PIDL_MAGIC
);
447 HRESULT
CRegistryFolder::FormatValueData(DWORD contentType
, PVOID td
, DWORD contentsLength
, PCWSTR
* strContents
)
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
;
463 PWSTR strValue
= (PWSTR
)CoTaskMemAlloc(contentsLength
+ sizeof(WCHAR
));
464 StringCbCopyNW(strValue
, contentsLength
+ sizeof(WCHAR
), (LPCWSTR
)td
, contentsLength
);
465 *strContents
= strValue
;
470 PCWSTR separator
= L
" "; // To match regedit
471 size_t sepChars
= wcslen(separator
);
475 PCWSTR strData
= (PCWSTR
)td
;
478 size_t len
= wcslen(strData
);
480 strData
+= len
+ 1; // Skips null-terminator
484 int cch
= stringChars
+ (strings
- 1) * sepChars
+ 1;
486 PWSTR strValue
= (PWSTR
)CoTaskMemAlloc(cch
* sizeof(WCHAR
));
490 strData
= (PCWSTR
)td
;
493 StrCatW(strValue
, strData
);
494 strData
+= wcslen(strData
) + 1;
496 StrCatW(strValue
, separator
);
499 *strContents
= strValue
;
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
;
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
;
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
++)
528 StringCbPrintfW(strTemp
, bufferLength
, L
"%02x ", data
[i
]);
532 *strContents
= strValue
;
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
;
547 HRESULT
CRegistryFolder::FormatContentsForDisplay(const RegPidlEntry
* info
, HKEY rootKey
, LPCWSTR ntPath
, PCWSTR
* strContents
)
549 PVOID td
= (((PBYTE
)info
) + FIELD_OFFSET(RegPidlEntry
, entryName
) + info
->entryNameLength
+ sizeof(WCHAR
));
551 if (info
->entryType
== REG_ENTRY_VALUE_WITH_CONTENT
)
553 if (info
->contentsLength
> 0)
555 return FormatValueData(info
->contentType
, td
, info
->contentsLength
, strContents
);
558 else if (info
->entryType
== REG_ENTRY_VALUE
)
562 HRESULT hr
= ReadRegistryValue(rootKey
, ntPath
, info
->entryName
, &valueData
, &valueLength
);
563 if (FAILED_UNEXPECTEDLY(hr
))
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
;
575 hr
= FormatValueData(info
->contentType
, valueData
, valueLength
, strContents
);
577 CoTaskMemFree(valueData
);
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
;
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
;