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