1 /*
2 * PROJECT: NT Object Namespace shell extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: NT Object Namespace folder class implementation
5 * COPYRIGHT: Copyright 2015-2017 David Quintana <gigaherz@gmail.com>
6 */
7
8 #include "precomp.h"
9
10 #include <wine/unicode.h>
11
12 // {845B0FB2-66E0-416B-8F91-314E23F7C12D}
13 const GUID CLSID_NtObjectFolder = { 0x845b0fb2, 0x66e0, 0x416b, { 0x8f, 0x91, 0x31, 0x4e, 0x23, 0xf7, 0xc1, 0x2d } };
14
15 // {F4C430C3-3A8D-4B56-A018-E598DA60C2E0}
16 static const GUID GUID_NtObjectColumns = { 0xf4c430c3, 0x3a8d, 0x4b56, { 0xa0, 0x18, 0xe5, 0x98, 0xda, 0x60, 0xc2, 0xe0 } };
17
18 enum NtObjectColumns
19 {
20 NTOBJECT_COLUMN_NAME = 0,
21 NTOBJECT_COLUMN_TYPE,
22 NTOBJECT_COLUMN_LINKTARGET,
23 NTOBJECT_COLUMN_END
24 };
25
CNtObjectFolderExtractIcon()26 CNtObjectFolderExtractIcon::CNtObjectFolderExtractIcon() :
27 m_NtPath(NULL),
28 m_pcidlChild(NULL)
29 {
30
31 }
32
~CNtObjectFolderExtractIcon()33 CNtObjectFolderExtractIcon::~CNtObjectFolderExtractIcon()
34 {
35 if (m_pcidlChild)
36 ILFree((LPITEMIDLIST) m_pcidlChild);
37 }
38
Initialize(LPCWSTR ntPath,PCIDLIST_ABSOLUTE parent,UINT cidl,PCUITEMID_CHILD_ARRAY apidl)39 HRESULT CNtObjectFolderExtractIcon::Initialize(LPCWSTR ntPath, PCIDLIST_ABSOLUTE parent, UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
40 {
41 m_NtPath = ntPath;
42 if (cidl != 1)
43 return E_INVALIDARG;
44 m_pcidlChild = ILClone(apidl[0]);
45 return S_OK;
46 }
47
GetIconLocation(UINT uFlags,LPWSTR szIconFile,UINT cchMax,INT * piIndex,UINT * pwFlags)48 HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::GetIconLocation(
49 UINT uFlags,
50 LPWSTR szIconFile,
51 UINT cchMax,
52 INT *piIndex,
53 UINT *pwFlags)
54 {
55 const NtPidlEntry * entry = (NtPidlEntry *) m_pcidlChild;
56
57 if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC))
58 return E_INVALIDARG;
59
60 UINT flags = 0;
61
62 switch (entry->objectType)
63 {
64 case DIRECTORY_OBJECT:
65 case SYMBOLICLINK_OBJECT:
66 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
67 *piIndex = -((uFlags & GIL_OPENICON) ? IDI_NTOBJECTDIROPEN : IDI_NTOBJECTDIR);
68 *pwFlags = flags;
69 return S_OK;
70
71 case DEVICE_OBJECT:
72 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
73 *piIndex = -IDI_NTOBJECTDEVICE;
74 *pwFlags = flags;
75 return S_OK;
76
77 case PORT_OBJECT:
78 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
79 *piIndex = -IDI_NTOBJECTPORT;
80 *pwFlags = flags;
81 return S_OK;
82
83 case KEY_OBJECT:
84 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
85 *piIndex = -IDI_REGISTRYKEY;
86 *pwFlags = flags;
87 return S_OK;
88
89 default:
90 GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
91 *piIndex = -IDI_NTOBJECTITEM;
92 *pwFlags = flags;
93 return S_OK;
94 }
95 }
96
Extract(LPCWSTR pszFile,UINT nIconIndex,HICON * phiconLarge,HICON * phiconSmall,UINT nIconSize)97 HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::Extract(
98 LPCWSTR pszFile,
99 UINT nIconIndex,
100 HICON *phiconLarge,
101 HICON *phiconSmall,
102 UINT nIconSize)
103 {
104 return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize);
105 }
106
107 //-----------------------------------------------------------------------------
108 // CNtObjectFolder
109
CNtObjectFolder()110 CNtObjectFolder::CNtObjectFolder()
111 {
112 }
113
~CNtObjectFolder()114 CNtObjectFolder::~CNtObjectFolder()
115 {
116 }
117
118 // IShellFolder
119
EnumObjects(HWND hwndOwner,SHCONTF grfFlags,IEnumIDList ** ppenumIDList)120 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects(
121 HWND hwndOwner,
122 SHCONTF grfFlags,
123 IEnumIDList **ppenumIDList)
124 {
125 return GetEnumNTDirectory(m_NtPath, ppenumIDList);
126 }
127
IsSymLink(const NtPidlEntry * info)128 BOOL STDMETHODCALLTYPE CNtObjectFolder::IsSymLink(const NtPidlEntry * info)
129 {
130 return info->objectType == SYMBOLICLINK_OBJECT;
131 }
132
ResolveSymLink(const NtPidlEntry * info,LPITEMIDLIST * fullPidl)133 HRESULT STDMETHODCALLTYPE CNtObjectFolder::ResolveSymLink(
134 const NtPidlEntry * info,
135 LPITEMIDLIST * fullPidl)
136 {
137 WCHAR wbLink[MAX_PATH] = { 0 };
138 UNICODE_STRING link;
139 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
140
141 *fullPidl = NULL;
142
143 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
144 if (FAILED_UNEXPECTEDLY(hr))
145 return hr;
146
147 WCHAR path[MAX_PATH];
148
149 if (link.Length == 0)
150 return E_UNEXPECTED;
151
152 if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0]))
153 {
154 StringCbCopyNW(path, sizeof(path), link.Buffer, link.Length);
155
156 CComPtr<IShellFolder> psfDesktop;
157 hr = SHGetDesktopFolder(&psfDesktop);
158 if (FAILED_UNEXPECTEDLY(hr))
159 return hr;
160
161 return psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, fullPidl, NULL);
162 }
163
164 StringCbCopyW(path, sizeof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
165 PathAppend(path, link.Buffer);
166
167 CComPtr<IShellFolder> psfDesktop;
168 hr = SHGetDesktopFolder(&psfDesktop);
169 if (FAILED_UNEXPECTEDLY(hr))
170 return hr;
171
172 LPITEMIDLIST pidl;
173
174 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &pidl, NULL);
175 if (FAILED(hr))
176 return hr;
177
178 *fullPidl = pidl;
179
180 DumpIdList(pidl);
181
182 return S_OK;
183 }
184
InternalBindToObject(PWSTR path,const NtPidlEntry * info,LPITEMIDLIST first,LPCITEMIDLIST rest,LPITEMIDLIST fullPidl,LPBC pbcReserved,IShellFolder ** ppsfChild)185 HRESULT STDMETHODCALLTYPE CNtObjectFolder::InternalBindToObject(
186 PWSTR path,
187 const NtPidlEntry * info,
188 LPITEMIDLIST first,
189 LPCITEMIDLIST rest,
190 LPITEMIDLIST fullPidl,
191 LPBC pbcReserved,
192 IShellFolder** ppsfChild)
193 {
194
195 if (info->objectType == KEY_OBJECT)
196 {
197 return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, ppsfChild));
198 }
199
200 return ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, ppsfChild));
201 }
202
203 // IPersistFolder
Initialize(PCIDLIST_ABSOLUTE pidl)204 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
205 {
206 m_shellPidl = ILClone(pidl);
207
208 StringCbCopyW(m_NtPath, sizeof(m_NtPath), L"\\");
209
210 return S_OK;
211 }
212
213 // Internal
Initialize(PCIDLIST_ABSOLUTE pidl,PCWSTR ntPath)214 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(PCIDLIST_ABSOLUTE pidl, PCWSTR ntPath)
215 {
216 m_shellPidl = ILClone(pidl);
217
218 StringCbCopyW(m_NtPath, sizeof(m_NtPath), ntPath);
219
220 return S_OK;
221 }
222
GetDefaultColumnState(UINT iColumn,SHCOLSTATEF * pcsFlags)223 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
224 UINT iColumn,
225 SHCOLSTATEF *pcsFlags)
226 {
227 switch (iColumn)
228 {
229 case NTOBJECT_COLUMN_NAME:
230 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
231 return S_OK;
232
233 case NTOBJECT_COLUMN_TYPE:
234 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
235 return S_OK;
236
237 case NTOBJECT_COLUMN_LINKTARGET:
238 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
239 return S_OK;
240 }
241
242 return E_INVALIDARG;
243 }
244
GetDetailsEx(LPCITEMIDLIST pidl,const SHCOLUMNID * pscid,VARIANT * pv)245 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
246 LPCITEMIDLIST pidl,
247 const SHCOLUMNID *pscid,
248 VARIANT *pv)
249 {
250 const NtPidlEntry * info;
251
252 TRACE("GetDetailsEx\n");
253
254 if (pidl)
255 {
256 HRESULT hr = GetInfoFromPidl(pidl, &info);
257 if (FAILED_UNEXPECTEDLY(hr))
258 return hr;
259
260 static const GUID storage = PSGUID_STORAGE;
261 if (IsEqualGUID(pscid->fmtid, storage))
262 {
263 if (pscid->pid == PID_STG_NAME)
264 {
265 return MakeVariantString(pv, info->entryName);
266 }
267 else if (pscid->pid == PID_STG_STORAGETYPE)
268 {
269 if (info->objectType < 0)
270 {
271 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
272
273 if (td->typeNameLength > 0)
274 {
275 return MakeVariantString(pv, td->typeName);
276 }
277 else
278 {
279 return MakeVariantString(pv, L"Unknown");
280 }
281 }
282 else
283 {
284 return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
285 }
286 }
287 }
288 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
289 {
290 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT)
291 {
292 WCHAR wbLink[MAX_PATH] = { 0 };
293 UNICODE_STRING link;
294 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
295
296 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
297
298 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
299 {
300 return MakeVariantString(pv, link.Buffer);
301 }
302 }
303
304 V_VT(pv) = VT_EMPTY;
305 return S_OK;
306 }
307 }
308
309 return E_INVALIDARG;
310 }
311
GetDetailsOf(LPCITEMIDLIST pidl,UINT iColumn,SHELLDETAILS * psd)312 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
313 LPCITEMIDLIST pidl,
314 UINT iColumn,
315 SHELLDETAILS *psd)
316 {
317 const NtPidlEntry * info;
318
319 TRACE("GetDetailsOf\n");
320
321 if (pidl)
322 {
323 HRESULT hr = GetInfoFromPidl(pidl, &info);
324 if (FAILED_UNEXPECTEDLY(hr))
325 return hr;
326
327 switch (iColumn)
328 {
329 case NTOBJECT_COLUMN_NAME:
330 {
331 psd->fmt = LVCFMT_LEFT;
332
333 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
334 return S_OK;
335 }
336
337 case NTOBJECT_COLUMN_TYPE:
338 {
339 psd->fmt = LVCFMT_LEFT;
340
341 if (info->objectType < 0)
342 {
343 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
344
345 if (td->typeNameLength > 0)
346 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
347 else
348 MakeStrRetFromString(L"Unknown", &(psd->str));
349 }
350 else
351 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
352 return S_OK;
353 }
354
355 case NTOBJECT_COLUMN_LINKTARGET:
356 {
357 psd->fmt = LVCFMT_LEFT;
358
359 if (info->objectType == SYMBOLICLINK_OBJECT)
360 {
361 WCHAR wbLink[MAX_PATH] = { 0 };
362 UNICODE_STRING link;
363 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
364
365 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
366
367 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
368 {
369 MakeStrRetFromString(link.Buffer, link.Length, &(psd->str));
370 return S_OK;
371 }
372 }
373
374 MakeStrRetFromString(L"", &(psd->str));
375 return S_OK;
376 }
377 }
378 }
379 else
380 {
381 switch (iColumn)
382 {
383 case NTOBJECT_COLUMN_NAME:
384 psd->fmt = LVCFMT_LEFT;
385 psd->cxChar = 30;
386
387 // TODO: Make localizable
388 MakeStrRetFromString(L"Object Name", &(psd->str));
389 return S_OK;
390
391 case NTOBJECT_COLUMN_TYPE:
392 psd->fmt = LVCFMT_LEFT;
393 psd->cxChar = 20;
394
395 // TODO: Make localizable
396 MakeStrRetFromString(L"Object Type", &(psd->str));
397 return S_OK;
398
399 case NTOBJECT_COLUMN_LINKTARGET:
400 psd->fmt = LVCFMT_LEFT;
401 psd->cxChar = 30;
402
403 // TODO: Make localizable
404 MakeStrRetFromString(L"Symlink Target", &(psd->str));
405 return S_OK;
406 }
407 }
408
409 return E_INVALIDARG;
410 }
411
MapColumnToSCID(UINT iColumn,SHCOLUMNID * pscid)412 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
413 UINT iColumn,
414 SHCOLUMNID *pscid)
415 {
416 static const GUID storage = PSGUID_STORAGE;
417 switch (iColumn)
418 {
419 case NTOBJECT_COLUMN_NAME:
420 pscid->fmtid = storage;
421 pscid->pid = PID_STG_NAME;
422 return S_OK;
423
424 case NTOBJECT_COLUMN_TYPE:
425 pscid->fmtid = storage;
426 pscid->pid = PID_STG_STORAGETYPE;
427 return S_OK;
428
429 case NTOBJECT_COLUMN_LINKTARGET:
430 pscid->fmtid = GUID_NtObjectColumns;
431 pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
432 return S_OK;
433 }
434 return E_INVALIDARG;
435 }
436
CompareIDs(LPARAM lParam,const NtPidlEntry * first,const NtPidlEntry * second)437 HRESULT CNtObjectFolder::CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second)
438 {
439 HRESULT hr;
440
441 DWORD sortMode = lParam & 0xFFFF0000;
442 DWORD column = lParam & 0x0000FFFF;
443
444 if (sortMode == SHCIDS_ALLFIELDS)
445 {
446 if (column != 0)
447 return E_INVALIDARG;
448
449 int minsize = min(first->cb, second->cb);
450 hr = MAKE_COMPARE_HRESULT(memcmp(second, first, minsize));
451 if (hr != S_EQUAL)
452 return hr;
453
454 return MAKE_COMPARE_HRESULT(second->cb - first->cb);
455 }
456
457 switch (column)
458 {
459 case NTOBJECT_COLUMN_NAME:
460 return CompareName(lParam, first, second);
461
462 case NTOBJECT_COLUMN_TYPE:
463 return MAKE_COMPARE_HRESULT(second->objectType - first->objectType);
464
465 case NTOBJECT_COLUMN_LINKTARGET:
466 {
467 if (first->objectType != SYMBOLICLINK_OBJECT && second->objectType != SYMBOLICLINK_OBJECT)
468 return CompareName(lParam, first, second);
469
470 if (first->objectType != SYMBOLICLINK_OBJECT || second->objectType != SYMBOLICLINK_OBJECT)
471 return first->objectType != SYMBOLICLINK_OBJECT ? S_GREATERTHAN : S_LESSTHAN;
472
473 WCHAR wbLink1[MAX_PATH] = { 0 }, wbLink2[MAX_PATH] = { 0 };
474 UNICODE_STRING firstLink, secondLink;
475 RtlInitEmptyUnicodeString(&firstLink, wbLink1, sizeof(wbLink1));
476
477 if (FAILED_UNEXPECTEDLY(GetNTObjectSymbolicLinkTarget(m_NtPath, first->entryName, &firstLink)))
478 return E_INVALIDARG;
479
480 RtlInitEmptyUnicodeString(&secondLink, wbLink2, sizeof(wbLink2));
481
482 if (FAILED_UNEXPECTEDLY(GetNTObjectSymbolicLinkTarget(m_NtPath, second->entryName, &secondLink)))
483 return E_INVALIDARG;
484
485 return MAKE_COMPARE_HRESULT(RtlCompareUnicodeString(&firstLink, &secondLink, TRUE));
486 }
487 }
488
489 DbgPrint("Unsupported sorting mode.\n");
490 return E_INVALIDARG;
491 }
492
ConvertAttributes(const NtPidlEntry * entry,PULONG inMask)493 ULONG CNtObjectFolder::ConvertAttributes(const NtPidlEntry * entry, PULONG inMask)
494 {
495 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
496 ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK;
497
498 if (entry->objectType == DIRECTORY_OBJECT)
499 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
500
501 if (entry->objectType == SYMBOLICLINK_OBJECT)
502 flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
503
504 if (entry->objectType == KEY_OBJECT)
505 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
506
507 return flags & mask;
508 }
509
IsFolder(const NtPidlEntry * info)510 BOOL CNtObjectFolder::IsFolder(const NtPidlEntry * info)
511 {
512 return (info->objectType == DIRECTORY_OBJECT) ||
513 (info->objectType == SYMBOLICLINK_OBJECT) ||
514 (info->objectType == KEY_OBJECT);
515 }
516
GetInfoFromPidl(LPCITEMIDLIST pcidl,const NtPidlEntry ** pentry)517 HRESULT CNtObjectFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl, const NtPidlEntry ** pentry)
518 {
519 if (!pcidl)
520 {
521 DbgPrint("PCIDL is NULL\n");
522 return E_INVALIDARG;
523 }
524
525 NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid);
526 if (entry->cb < sizeof(NtPidlEntry))
527 {
528 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(NtPidlEntry));
529 return E_INVALIDARG;
530 }
531
532 if (entry->magic != NT_OBJECT_PIDL_MAGIC)
533 {
534 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, NT_OBJECT_PIDL_MAGIC);
535 return E_INVALIDARG;
536 }
537
538 *pentry = entry;
539 return S_OK;
540 }
541