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