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 } 599