[NTOBJSHEX]
[reactos.git] / reactos / 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 WINE_DEFAULT_DEBUG_CHANNEL(ntobjshex);
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_pcidlChild(NULL),
29 m_NtPath(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::RedirectToSymLink(
131 const NtPidlEntry * info,
132 LPITEMIDLIST first,
133 LPCITEMIDLIST rest,
134 LPBC pbcReserved,
135 IShellFolder ** ppsfChild)
136 {
137 HRESULT hr;
138
139 WCHAR wbLink[MAX_PATH] = { 0 };
140 UNICODE_STRING link;
141 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
142
143 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 {
151 if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0]))
152 {
153 StringCbCopyNW(path, _countof(path), link.Buffer, link.Length);
154
155 CComPtr<IShellFolder> psfDesktop;
156 hr = SHGetDesktopFolder(&psfDesktop);
157 if (FAILED_UNEXPECTEDLY(hr))
158 return hr;
159
160 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &first, NULL);
161 if (FAILED_UNEXPECTEDLY(hr))
162 return hr;
163
164 hr = psfDesktop->BindToObject(rest, pbcReserved, IID_PPV_ARG(IShellFolder, ppsfChild));
165 if (FAILED(hr))
166 return hr;
167
168 return S_FALSE;;
169 }
170
171 StringCbCopyW(path, _countof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
172 PathAppend(path, link.Buffer);
173
174 CComPtr<IShellFolder> psfDesktop;
175 hr = SHGetDesktopFolder(&psfDesktop);
176 if (FAILED_UNEXPECTEDLY(hr))
177 return hr;
178
179 LPITEMIDLIST pidl;
180
181 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &pidl, NULL);
182 if (FAILED_UNEXPECTEDLY(hr))
183 return hr;
184
185 CComPtr<IShellFolder> psfChild;
186 hr = psfDesktop->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, ppsfChild));
187 ILFree(pidl);
188
189 if (FAILED(hr))
190 return hr;
191
192 return S_FALSE;;
193 }
194
195 return E_UNEXPECTED;
196 }
197
198 HRESULT STDMETHODCALLTYPE CNtObjectFolder::InternalBindToObject(
199 PWSTR path,
200 const NtPidlEntry * info,
201 LPITEMIDLIST first,
202 LPCITEMIDLIST rest,
203 LPITEMIDLIST fullPidl,
204 LPBC pbcReserved,
205 IShellFolder** ppsfChild)
206 {
207
208 if (info->objectType == KEY_OBJECT)
209 {
210 return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, ppsfChild));
211 }
212
213 return ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, ppsfChild));
214 }
215
216 // IPersistFolder
217 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl)
218 {
219 m_shellPidl = ILClone(pidl);
220
221 StringCbCopy(m_NtPath, _countof(m_NtPath), L"\\");
222
223 return S_OK;
224 }
225
226 // Internal
227 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath)
228 {
229 m_shellPidl = ILClone(pidl);
230
231 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath);
232
233 return S_OK;
234 }
235
236 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
237 UINT iColumn,
238 SHCOLSTATEF *pcsFlags)
239 {
240 switch (iColumn)
241 {
242 case NTOBJECT_COLUMN_NAME:
243 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
244 return S_OK;
245 case NTOBJECT_COLUMN_TYPE:
246 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
247 return S_OK;
248 case NTOBJECT_COLUMN_LINKTARGET:
249 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
250 return S_OK;
251 }
252
253 return E_INVALIDARG;
254 }
255
256 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
257 LPCITEMIDLIST pidl,
258 const SHCOLUMNID *pscid,
259 VARIANT *pv)
260 {
261 const NtPidlEntry * info;
262
263 TRACE("GetDetailsEx\n");
264
265 if (pidl)
266 {
267 HRESULT hr = GetInfoFromPidl(pidl, &info);
268 if (FAILED_UNEXPECTEDLY(hr))
269 return hr;
270
271 static const GUID storage = PSGUID_STORAGE;
272 if (IsEqualGUID(pscid->fmtid, storage))
273 {
274 if (pscid->pid == PID_STG_NAME)
275 {
276 return MakeVariantString(pv, info->entryName);
277 }
278 else if (pscid->pid == PID_STG_STORAGETYPE)
279 {
280 if (info->objectType < 0)
281 {
282 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
283
284 if (td->typeNameLength > 0)
285 {
286 return MakeVariantString(pv, td->typeName);
287 }
288 else
289 {
290 return MakeVariantString(pv, L"Unknown");
291 }
292 }
293 else
294 {
295 return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
296 }
297 }
298 }
299 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
300 {
301 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT)
302 {
303 WCHAR wbLink[MAX_PATH] = { 0 };
304 UNICODE_STRING link;
305 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
306
307 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
308
309 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
310 {
311 return MakeVariantString(pv, link.Buffer);
312 }
313 }
314
315 V_VT(pv) = VT_EMPTY;
316 return S_OK;
317 }
318 }
319
320 return E_INVALIDARG;
321 }
322
323 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
324 LPCITEMIDLIST pidl,
325 UINT iColumn,
326 SHELLDETAILS *psd)
327 {
328 const NtPidlEntry * info;
329
330 TRACE("GetDetailsOf\n");
331
332 if (pidl)
333 {
334 HRESULT hr = GetInfoFromPidl(pidl, &info);
335 if (FAILED_UNEXPECTEDLY(hr))
336 return hr;
337
338 switch (iColumn)
339 {
340 case NTOBJECT_COLUMN_NAME:
341 psd->fmt = LVCFMT_LEFT;
342
343 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
344 return S_OK;
345 case NTOBJECT_COLUMN_TYPE:
346 psd->fmt = LVCFMT_LEFT;
347
348 if (info->objectType < 0)
349 {
350 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
351
352 if (td->typeNameLength > 0)
353 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
354 else
355 MakeStrRetFromString(L"Unknown", &(psd->str));
356 }
357 else
358 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
359 return S_OK;
360 case NTOBJECT_COLUMN_LINKTARGET:
361 {
362 psd->fmt = LVCFMT_LEFT;
363
364 if (info->objectType == SYMBOLICLINK_OBJECT)
365 {
366 WCHAR wbLink[MAX_PATH] = { 0 };
367 UNICODE_STRING link;
368 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
369
370 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
371
372 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
373 {
374 MakeStrRetFromString(link.Buffer, link.Length, &(psd->str));
375 return S_OK;
376 }
377 }
378
379 MakeStrRetFromString(L"", &(psd->str));
380 return S_OK;
381 }
382 }
383 }
384 else
385 {
386 switch (iColumn)
387 {
388 case NTOBJECT_COLUMN_NAME:
389 psd->fmt = LVCFMT_LEFT;
390 psd->cxChar = 30;
391
392 // TODO: Make localizable
393 MakeStrRetFromString(L"Object Name", &(psd->str));
394 return S_OK;
395 case NTOBJECT_COLUMN_TYPE:
396 psd->fmt = LVCFMT_LEFT;
397 psd->cxChar = 20;
398
399 // TODO: Make localizable
400 MakeStrRetFromString(L"Object Type", &(psd->str));
401 return S_OK;
402 case NTOBJECT_COLUMN_LINKTARGET:
403 psd->fmt = LVCFMT_LEFT;
404 psd->cxChar = 30;
405
406 // TODO: Make localizable
407 MakeStrRetFromString(L"Symlink Target", &(psd->str));
408 return S_OK;
409 }
410 }
411
412 return E_INVALIDARG;
413 }
414
415 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
416 UINT iColumn,
417 SHCOLUMNID *pscid)
418 {
419 static const GUID storage = PSGUID_STORAGE;
420 switch (iColumn)
421 {
422 case NTOBJECT_COLUMN_NAME:
423 pscid->fmtid = storage;
424 pscid->pid = PID_STG_NAME;
425 return S_OK;
426 case NTOBJECT_COLUMN_TYPE:
427 pscid->fmtid = storage;
428 pscid->pid = PID_STG_STORAGETYPE;
429 return S_OK;
430 case NTOBJECT_COLUMN_LINKTARGET:
431 pscid->fmtid = GUID_NtObjectColumns;
432 pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
433 return S_OK;
434 }
435 return E_INVALIDARG;
436 }
437
438 HRESULT CNtObjectFolder::CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second)
439 {
440 HRESULT hr;
441
442 LPARAM sortMode = lParam & 0xFFFF0000;
443 LPARAM column = lParam & 0x0000FFFF;
444
445 if (sortMode == SHCIDS_ALLFIELDS)
446 {
447 if (column != 0)
448 return E_INVALIDARG;
449
450 int minsize = min(first->cb, second->cb);
451 hr = MAKE_COMPARE_HRESULT(memcmp(second, first, minsize));
452 if (hr != S_EQUAL)
453 return hr;
454
455 return MAKE_COMPARE_HRESULT(second->cb - first->cb);
456 }
457
458 switch (column)
459 {
460 case NTOBJECT_COLUMN_NAME:
461 return CompareName(lParam, first, second);
462
463 case NTOBJECT_COLUMN_TYPE:
464 return MAKE_COMPARE_HRESULT(second->objectType - first->objectType);
465
466 case NTOBJECT_COLUMN_LINKTARGET:
467 // Can't sort by link target yet
468 return E_INVALIDARG;
469 }
470
471 DbgPrint("Unsupported sorting mode.\n");
472 return E_INVALIDARG;
473 }
474
475 ULONG CNtObjectFolder::ConvertAttributes(const NtPidlEntry * entry, PULONG inMask)
476 {
477 ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
478 ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK;
479
480 if (entry->objectType == DIRECTORY_OBJECT)
481 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
482
483 if (entry->objectType == SYMBOLICLINK_OBJECT)
484 flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
485
486 if (entry->objectType == KEY_OBJECT)
487 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
488
489 return flags & mask;
490 }
491
492 BOOL CNtObjectFolder::IsFolder(LPCITEMIDLIST pcidl)
493 {
494 NtPidlEntry * info = (NtPidlEntry*) &(pcidl->mkid);
495 if ((info->cb < sizeof(NtPidlEntry)) || (info->magic != NT_OBJECT_PIDL_MAGIC))
496 return FALSE;
497
498 return IsFolder(info);
499 }
500
501 BOOL CNtObjectFolder::IsFolder(const NtPidlEntry * info)
502 {
503 return (info->objectType == DIRECTORY_OBJECT) ||
504 (info->objectType == SYMBOLICLINK_OBJECT) ||
505 (info->objectType == KEY_OBJECT);
506 }
507
508 HRESULT CNtObjectFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl, const NtPidlEntry ** pentry)
509 {
510 NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid);
511
512 if (entry->cb < sizeof(NtPidlEntry))
513 {
514 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(NtPidlEntry));
515 return E_INVALIDARG;
516 }
517
518 if (entry->magic != NT_OBJECT_PIDL_MAGIC)
519 {
520 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, NT_OBJECT_PIDL_MAGIC);
521 return E_INVALIDARG;
522 }
523
524 *pentry = entry;
525 return S_OK;
526 }