xref: /reactos/dll/win32/shell32/CNewMenu.cpp (revision d6eebaa4)
1 /*
2  * provides new shell item service
3  *
4  * Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org)
5  * Copyright 2009 Andrew Hill
6  * Copyright 2012 Rafal Harabien
7  * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "precomp.h"
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(shell);
27 
28 CNewMenu::CNewMenu() :
29     m_pidlFolder(NULL),
30     m_pItems(NULL),
31     m_pLinkItem(NULL),
32     m_pSite(NULL),
33     m_idCmdFirst(0),
34     m_idCmdFolder(-1),
35     m_idCmdLink(-1),
36     m_bCustomIconFolder(FALSE),
37     m_bCustomIconLink(FALSE),
38     m_hIconFolder(NULL),
39     m_hIconLink(NULL)
40 {
41 }
42 
43 CNewMenu::~CNewMenu()
44 {
45     UnloadAllItems();
46 
47     if (m_bCustomIconFolder && m_hIconFolder)
48         DestroyIcon(m_hIconFolder);
49     if (m_bCustomIconLink && m_hIconLink)
50         DestroyIcon(m_hIconLink);
51     if (m_pidlFolder)
52         ILFree(m_pidlFolder);
53 }
54 
55 void CNewMenu::UnloadItem(SHELLNEW_ITEM *pItem)
56 {
57     /* Note: free allows NULL as argument */
58     free(pItem->pData);
59     free(pItem->pwszDesc);
60     free(pItem->pwszExt);
61 
62     if (pItem->hIcon)
63         DestroyIcon(pItem->hIcon);
64 
65     HeapFree(GetProcessHeap(), 0, pItem);
66 }
67 
68 void CNewMenu::UnloadAllItems()
69 {
70     SHELLNEW_ITEM *pCurItem;
71 
72     /* Unload the handler items, including the link item */
73     while (m_pItems)
74     {
75         pCurItem = m_pItems;
76         m_pItems = m_pItems->pNext;
77         UnloadItem(pCurItem);
78     }
79     m_pItems = NULL;
80     m_pLinkItem = NULL;
81 }
82 
83 CNewMenu::SHELLNEW_ITEM *CNewMenu::LoadItem(LPCWSTR pwszExt)
84 {
85     HKEY hKey;
86     WCHAR wszBuf[MAX_PATH];
87     PBYTE pData = NULL;
88     DWORD cbData;
89 
90     StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s\\ShellNew", pwszExt);
91 
92     TRACE("LoadItem Keyname %s Name %s\n", debugstr_w(pwszExt), debugstr_w(wszBuf));
93 
94     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
95     {
96         TRACE("Failed to open key\n");
97         return NULL;
98     }
99 
100     /* Find first valid value */
101     struct
102     {
103         LPCWSTR pszName;
104         SHELLNEW_TYPE Type;
105         BOOL bNeedData;
106         BOOL bStr;
107     } Types[] = {
108         {L"FileName", SHELLNEW_TYPE_FILENAME, TRUE, TRUE},
109         {L"Command", SHELLNEW_TYPE_COMMAND, TRUE, TRUE},
110         {L"Data", SHELLNEW_TYPE_DATA, TRUE, FALSE},
111         {L"NullFile", SHELLNEW_TYPE_NULLFILE, FALSE},
112         {NULL}
113     };
114     UINT i;
115 
116     for (i = 0; Types[i].pszName; ++i)
117     {
118         /* Note: We are using ANSI function because strings can be treated as data */
119         cbData = 0;
120         DWORD dwFlags = Types[i].bStr ? RRF_RT_REG_SZ : RRF_RT_ANY;
121         DWORD dwType;
122         if (RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, NULL, NULL, &cbData) == ERROR_SUCCESS)
123         {
124             if (Types[i].bNeedData && cbData > 0)
125             {
126                 pData = (PBYTE)malloc(cbData);
127                 RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, &dwType, pData, &cbData);
128                 if (!Types[i].bStr && (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
129                 {
130                     PBYTE pData2 = (PBYTE)malloc(cbData);
131                     cbData = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pData, -1, (LPSTR)pData2, cbData, NULL, NULL);
132                     free(pData);
133                     pData = pData2;
134                 }
135             }
136             break;
137         }
138     }
139     RegCloseKey(hKey);
140 
141     /* Was any key found? */
142     if (!Types[i].pszName)
143     {
144         free(pData);
145         return NULL;
146     }
147 
148     SHFILEINFOW fi;
149     if (!SHGetFileInfoW(pwszExt, FILE_ATTRIBUTE_NORMAL, &fi, sizeof(fi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME | SHGFI_ICON | SHGFI_SMALLICON))
150     {
151         free(pData);
152         return NULL;
153     }
154 
155     /* Create new item */
156     SHELLNEW_ITEM *pNewItem = static_cast<SHELLNEW_ITEM *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SHELLNEW_ITEM)));
157     if (!pNewItem)
158     {
159         free(pData);
160         return NULL;
161     }
162 
163     TRACE("new item %ls\n", fi.szTypeName);
164     pNewItem->Type = Types[i].Type;
165     pNewItem->pData = pData;
166     pNewItem->cbData = pData ? cbData : 0;
167     pNewItem->pwszExt = _wcsdup(pwszExt);
168     pNewItem->pwszDesc = _wcsdup(fi.szTypeName);
169     if (fi.hIcon)
170         pNewItem->hIcon = fi.hIcon;
171 
172     return pNewItem;
173 }
174 
175 BOOL
176 CNewMenu::CacheItems()
177 {
178     HKEY hKey;
179     DWORD dwSize = 0;
180     DWORD dwIndex = 0;
181     LPWSTR lpValue;
182     LPWSTR lpValues;
183     WCHAR wszName[MAX_PATH];
184     SHELLNEW_ITEM *pNewItem;
185     SHELLNEW_ITEM *pCurItem = NULL;
186 
187     /* Enumerate all extensions */
188     while (RegEnumKeyW(HKEY_CLASSES_ROOT, dwIndex++, wszName, _countof(wszName)) == ERROR_SUCCESS)
189     {
190         if (wszName[0] != L'.')
191             continue;
192 
193         pNewItem = LoadItem(wszName);
194         if (pNewItem)
195         {
196             dwSize += wcslen(wszName) + 1;
197             if (!m_pLinkItem && _wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
198             {
199                 /* The unique link handler */
200                 m_pLinkItem = pNewItem;
201             }
202 
203             /* Add at the end of the list */
204             if (pCurItem)
205             {
206                 pCurItem->pNext = pNewItem;
207                 pCurItem = pNewItem;
208             }
209             else
210             {
211                 pCurItem = m_pItems = pNewItem;
212             }
213         }
214     }
215 
216     dwSize++;
217 
218     lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize * sizeof(WCHAR));
219     if (!lpValues)
220         return FALSE;
221 
222     for (pCurItem = m_pItems, lpValue = lpValues; pCurItem; pCurItem = pCurItem->pNext)
223     {
224         memcpy(lpValue, pCurItem->pwszExt, (wcslen(pCurItem->pwszExt) + 1) * sizeof(WCHAR));
225         lpValue += wcslen(pCurItem->pwszExt) + 1;
226     }
227 
228     if (RegCreateKeyEx(HKEY_CURRENT_USER, ShellNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
229     {
230         HeapFree(GetProcessHeap(), 0, lpValues);
231         return FALSE;
232     }
233 
234     if (RegSetValueExW(hKey, L"Classes", NULL, REG_MULTI_SZ, (LPBYTE)lpValues, dwSize * sizeof(WCHAR)) != ERROR_SUCCESS)
235     {
236         HeapFree(GetProcessHeap(), 0, lpValues);
237         RegCloseKey(hKey);
238         return FALSE;
239     }
240 
241     HeapFree(GetProcessHeap(), 0, lpValues);
242     RegCloseKey(hKey);
243 
244     return TRUE;
245 }
246 
247 BOOL
248 CNewMenu::LoadCachedItems()
249 {
250     LPWSTR wszName;
251     LPWSTR lpValues;
252     DWORD dwSize;
253     HKEY hKey;
254     SHELLNEW_ITEM *pNewItem;
255     SHELLNEW_ITEM *pCurItem = NULL;
256 
257     if (RegOpenKeyExW(HKEY_CURRENT_USER, ShellNewKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
258         return FALSE;
259 
260     if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, NULL, &dwSize) != ERROR_SUCCESS)
261         return FALSE;
262 
263     lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwSize);
264     if (!lpValues)
265         return FALSE;
266 
267     if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, (LPBYTE)lpValues, &dwSize) != ERROR_SUCCESS)
268     {
269         HeapFree(GetProcessHeap(), 0, lpValues);
270         return FALSE;
271     }
272 
273     wszName = lpValues;
274 
275     for (; *wszName != '\0'; wszName += wcslen(wszName) + 1)
276     {
277         pNewItem = LoadItem(wszName);
278         if (pNewItem)
279         {
280             if (!m_pLinkItem && _wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
281             {
282                 /* The unique link handler */
283                 m_pLinkItem = pNewItem;
284             }
285 
286             /* Add at the end of the list */
287             if (pCurItem)
288             {
289                 pCurItem->pNext = pNewItem;
290                 pCurItem = pNewItem;
291             }
292             else
293             {
294                 pCurItem = m_pItems = pNewItem;
295             }
296         }
297     }
298 
299     HeapFree(GetProcessHeap(), 0, lpValues);
300     RegCloseKey(hKey);
301 
302     return TRUE;
303 }
304 
305 BOOL
306 CNewMenu::LoadAllItems()
307 {
308     // TODO: We need to find a way to refresh the cache from time to time, when
309     // e.g. new extensions with ShellNew handlers have been added or removed.
310 
311     /* If there are any unload them */
312     UnloadAllItems();
313 
314     if (!LoadCachedItems())
315     {
316         CacheItems();
317     }
318 
319     return (m_pItems != NULL);
320 }
321 
322 UINT
323 CNewMenu::InsertShellNewItems(HMENU hMenu, UINT idCmdFirst, UINT Pos)
324 {
325     MENUITEMINFOW mii;
326     UINT idCmd = idCmdFirst;
327     WCHAR wszBuf[256];
328 
329     if (m_pItems == NULL)
330     {
331         if (!LoadAllItems())
332             return 0;
333     }
334 
335     ZeroMemory(&mii, sizeof(mii));
336     mii.cbSize = sizeof(mii);
337 
338     m_idCmdFirst = idCmd;
339 
340     /* Insert the new folder action */
341     if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWFOLDER, wszBuf, _countof(wszBuf)))
342         wszBuf[0] = 0;
343     mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING;
344     mii.dwTypeData = wszBuf;
345     mii.cch = wcslen(mii.dwTypeData);
346     mii.wID = idCmd;
347     mii.hbmpItem = HBMMENU_CALLBACK;
348     if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
349         m_idCmdFolder = idCmd++;
350 
351     /* Insert the new shortcut action */
352     if (m_pLinkItem)
353     {
354         if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWLINK, wszBuf, _countof(wszBuf)))
355             wszBuf[0] = 0;
356         mii.dwTypeData = wszBuf;
357         mii.cch = wcslen(mii.dwTypeData);
358         mii.wID = idCmd;
359         if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
360             m_idCmdLink = idCmd++;
361     }
362 
363     /* Insert a seperator for the custom new action */
364     mii.fMask = MIIM_TYPE | MIIM_ID;
365     mii.fType = MFT_SEPARATOR;
366     mii.wID = -1;
367     InsertMenuItemW(hMenu, Pos++, TRUE, &mii);
368 
369     /* Insert the rest of the items */
370     mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING | MIIM_DATA;
371     mii.fType = 0;
372 
373     for (SHELLNEW_ITEM *pCurItem = m_pItems; pCurItem; pCurItem = pCurItem->pNext)
374     {
375         /* Skip shortcut item */
376         if (pCurItem == m_pLinkItem)
377             continue;
378 
379         TRACE("szDesc %s\n", debugstr_w(pCurItem->pwszDesc));
380         mii.dwItemData = (ULONG_PTR)pCurItem;
381         mii.dwTypeData = pCurItem->pwszDesc;
382         mii.cch = wcslen(mii.dwTypeData);
383         mii.wID = idCmd;
384         if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
385             ++idCmd;
386     }
387 
388     return idCmd - idCmdFirst;
389 }
390 
391 CNewMenu::SHELLNEW_ITEM *CNewMenu::FindItemFromIdOffset(UINT IdOffset)
392 {
393     /* Folder */
394     if (m_idCmdFirst + IdOffset == m_idCmdFolder)
395         return NULL;
396 
397     /* Shortcut */
398     if (m_idCmdFirst + IdOffset == m_idCmdLink)
399         return m_pLinkItem;
400 
401     /* Find shell new item - Retrieve menu item info */
402     MENUITEMINFOW mii;
403     ZeroMemory(&mii, sizeof(mii));
404     mii.cbSize = sizeof(mii);
405     mii.fMask = MIIM_DATA;
406 
407     if (GetMenuItemInfoW(m_hSubMenu, m_idCmdFirst + IdOffset, FALSE, &mii) && mii.dwItemData)
408         return (SHELLNEW_ITEM *)mii.dwItemData;
409     else
410         return NULL;
411 }
412 
413 HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName, BOOL bRename)
414 {
415     CComPtr<IShellBrowser> lpSB;
416     CComPtr<IShellView> lpSV;
417     HRESULT hr = E_FAIL;
418     LPITEMIDLIST pidl;
419     PITEMID_CHILD pidlNewItem;
420     DWORD dwSelectFlags;
421 
422     dwSelectFlags = SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
423     if (bRename)
424         dwSelectFlags |= SVSI_EDIT;
425 
426     /* Notify the view object about the new item */
427     SHChangeNotify(wEventId, uFlags | SHCNF_FLUSH, (LPCVOID)pszName, NULL);
428 
429     if (!m_pSite)
430         return S_OK;
431 
432     /* Get a pointer to the shell view */
433     hr = IUnknown_QueryService(m_pSite, SID_IFolderView, IID_PPV_ARG(IShellView, &lpSV));
434     if (FAILED_UNEXPECTEDLY(hr))
435         return S_OK;
436 
437     /* Attempt to get the pidl of the new item */
438     hr = SHILCreateFromPathW(pszName, &pidl, NULL);
439     if (FAILED_UNEXPECTEDLY(hr))
440         return hr;
441 
442     pidlNewItem = ILFindLastID(pidl);
443 
444     hr = lpSV->SelectItem(pidlNewItem, dwSelectFlags);
445 
446     SHFree(pidl);
447 
448     return hr;
449 }
450 
451 // Code is duplicated in CDefaultContextMenu
452 HRESULT CNewMenu::CreateNewFolder(LPCMINVOKECOMMANDINFO lpici)
453 {
454     WCHAR wszPath[MAX_PATH];
455     WCHAR wszName[MAX_PATH];
456     WCHAR wszNewFolder[25];
457     HRESULT hr;
458 
459     /* Get folder path */
460     hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
461     if (FAILED_UNEXPECTEDLY(hr))
462         return hr;
463 
464     if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
465         return E_FAIL;
466 
467     /* Create the name of the new directory */
468     if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
469         return E_FAIL;
470 
471     /* Create the new directory and show the appropriate dialog in case of error */
472     if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
473         return E_FAIL;
474 
475     /* Show and select the new item in the def view */
476     SelectNewItem(SHCNE_MKDIR, SHCNF_PATHW, wszName, TRUE);
477 
478     return S_OK;
479 }
480 
481 HRESULT CNewMenu::NewItemByCommand(SHELLNEW_ITEM *pItem, LPCWSTR wszPath)
482 {
483     WCHAR wszBuf[MAX_PATH];
484     LPWSTR Ptr, pwszCmd;
485     WCHAR wszTemp[MAX_PATH];
486     STARTUPINFOW si;
487     PROCESS_INFORMATION pi;
488 
489     if (!ExpandEnvironmentStringsW((LPWSTR)pItem->pData, wszBuf, _countof(wszBuf)))
490     {
491         TRACE("ExpandEnvironmentStrings failed\n");
492         return E_FAIL;
493     }
494 
495     /* Expand command parameter, FIXME: there can be more modifiers */
496     Ptr = wcsstr(wszBuf, L"%1");
497     if (Ptr)
498     {
499         Ptr[1] = L's';
500         StringCbPrintfW(wszTemp, sizeof(wszTemp), wszBuf, wszPath);
501         pwszCmd = wszTemp;
502     }
503     else
504     {
505         pwszCmd = wszBuf;
506     }
507 
508     /* Create process */
509     ZeroMemory(&si, sizeof(si));
510     si.cb = sizeof(si);
511     if (CreateProcessW(NULL, pwszCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
512     {
513         CloseHandle(pi.hProcess);
514         CloseHandle(pi.hThread);
515         return S_OK;
516     }
517     else
518     {
519         ERR("Failed to create process\n");
520         return E_FAIL;
521     }
522 }
523 
524 HRESULT CNewMenu::NewItemByNonCommand(SHELLNEW_ITEM *pItem, LPWSTR wszName,
525                                       DWORD cchNameMax, LPCWSTR wszPath)
526 {
527     BOOL bSuccess = TRUE;
528 
529     CStringW strNewItem;
530     strNewItem.Format(IDS_NEWITEMFORMAT, pItem->pwszDesc);
531     strNewItem += pItem->pwszExt;
532 
533     /* Create the name of the new file */
534     if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, strNewItem))
535         return E_FAIL;
536 
537     /* Create new file */
538     HANDLE hFile = CreateFileW(wszName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
539     if (hFile != INVALID_HANDLE_VALUE)
540     {
541         if (pItem->Type == SHELLNEW_TYPE_DATA)
542         {
543             /* Write a content */
544             DWORD cbWritten;
545             WriteFile(hFile, pItem->pData, pItem->cbData, &cbWritten, NULL);
546         }
547 
548         /* Close file now */
549         CloseHandle(hFile);
550     }
551     else
552     {
553         bSuccess = FALSE;
554     }
555 
556     if (pItem->Type == SHELLNEW_TYPE_FILENAME)
557     {
558         /* Copy file */
559         if (!CopyFileW((LPWSTR)pItem->pData, wszName, FALSE))
560             ERR("Copy file failed: %ls\n", (LPWSTR)pItem->pData);
561     }
562 
563     /* Show message if we failed */
564     if (bSuccess)
565     {
566         TRACE("Notifying fs %s\n", debugstr_w(wszName));
567         SelectNewItem(SHCNE_CREATE, SHCNF_PATHW, wszName, pItem != m_pLinkItem);
568     }
569     else
570     {
571         CStringW Caption(MAKEINTRESOURCEW(IDS_CREATEFILE_CAPTION));
572         CStringW Message(MAKEINTRESOURCEW(IDS_CREATEFILE_DENIED));
573         Message.FormatMessage(Message.GetString(), wszName);
574         MessageBoxW(0, Message, Caption, MB_ICONEXCLAMATION | MB_OK);
575     }
576 
577     return S_OK;
578 }
579 
580 HRESULT CNewMenu::CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi)
581 {
582     HRESULT hr;
583     WCHAR wszPath[MAX_PATH], wszName[MAX_PATH];
584 
585     /* Get folder path */
586     hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
587     if (FAILED_UNEXPECTEDLY(hr))
588         return hr;
589 
590     if (pItem == m_pLinkItem)
591     {
592         NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
593         NewItemByCommand(pItem, wszName);
594         return S_OK;
595     }
596 
597     switch (pItem->Type)
598     {
599         case SHELLNEW_TYPE_COMMAND:
600             NewItemByCommand(pItem, wszPath);
601             break;
602 
603         case SHELLNEW_TYPE_DATA:
604         case SHELLNEW_TYPE_FILENAME:
605         case SHELLNEW_TYPE_NULLFILE:
606             NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
607             break;
608 
609         case SHELLNEW_TYPE_INVALID:
610             ERR("Invalid type\n");
611             break;
612     }
613 
614     return S_OK;
615 }
616 
617 HRESULT STDMETHODCALLTYPE CNewMenu::SetSite(IUnknown *pUnkSite)
618 {
619     m_pSite = pUnkSite;
620     return S_OK;
621 }
622 
623 HRESULT STDMETHODCALLTYPE CNewMenu::GetSite(REFIID riid, void **ppvSite)
624 {
625     return m_pSite->QueryInterface(riid, ppvSite);
626 }
627 
628 HRESULT
629 WINAPI
630 CNewMenu::QueryContextMenu(HMENU hMenu,
631                            UINT indexMenu,
632                            UINT idCmdFirst,
633                            UINT idCmdLast,
634                            UINT uFlags)
635 {
636     MENUITEMINFOW mii;
637     UINT cItems = 0;
638     WCHAR wszNew[200];
639 
640     TRACE("%p %p %u %u %u %u\n", this,
641           hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
642 
643     if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszNew, _countof(wszNew)))
644         return E_FAIL;
645 
646     m_hSubMenu = CreateMenu();
647     if (!m_hSubMenu)
648         return E_FAIL;
649 
650     cItems = InsertShellNewItems(m_hSubMenu, idCmdFirst, 0);
651 
652     ZeroMemory(&mii, sizeof(mii));
653     mii.cbSize = sizeof(mii);
654     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
655     mii.fType = MFT_STRING;
656     mii.wID = -1;
657     mii.dwTypeData = wszNew;
658     mii.cch = wcslen(mii.dwTypeData);
659     mii.fState = MFS_ENABLED;
660     mii.hSubMenu = m_hSubMenu;
661 
662     if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
663         return E_FAIL;
664 
665     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cItems);
666 }
667 
668 HRESULT
669 WINAPI
670 CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
671 {
672     HRESULT hr = E_FAIL;
673 
674     if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdFolder)
675     {
676         hr = CreateNewFolder(lpici);
677     }
678     else
679     {
680         SHELLNEW_ITEM *pItem = FindItemFromIdOffset(LOWORD(lpici->lpVerb));
681         if (pItem)
682             hr = CreateNewItem(pItem, lpici);
683     }
684 
685     TRACE("CNewMenu::InvokeCommand %x\n", hr);
686     return hr;
687 }
688 
689 HRESULT
690 WINAPI
691 CNewMenu::GetCommandString(UINT_PTR idCmd,
692                            UINT uType,
693                            UINT *pwReserved,
694                            LPSTR pszName,
695                            UINT cchMax)
696 {
697     FIXME("%p %lu %u %p %p %u\n", this,
698           idCmd, uType, pwReserved, pszName, cchMax );
699 
700     return E_NOTIMPL;
701 }
702 
703 HRESULT
704 WINAPI
705 CNewMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
706 {
707     return S_OK;
708 }
709 
710 HRESULT
711 WINAPI
712 CNewMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
713 {
714     switch (uMsg)
715     {
716     case WM_MEASUREITEM:
717         {
718             MEASUREITEMSTRUCT* lpmis = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
719             if (!lpmis || lpmis->CtlType != ODT_MENU)
720                 break;
721 
722             if (lpmis->itemWidth < (UINT)GetSystemMetrics(SM_CXMENUCHECK))
723                 lpmis->itemWidth = GetSystemMetrics(SM_CXMENUCHECK);
724             if (lpmis->itemHeight < 16)
725                 lpmis->itemHeight = 16;
726 
727             if (plResult)
728                 *plResult = TRUE;
729             break;
730         }
731     case WM_DRAWITEM:
732         {
733             DRAWITEMSTRUCT* lpdis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
734             if (!lpdis || lpdis->CtlType != ODT_MENU)
735                 break;
736 
737             DWORD id = LOWORD(lpdis->itemID);
738             HICON hIcon = NULL;
739             if (m_idCmdFirst + id == m_idCmdFolder)
740             {
741                 hIcon = m_hIconFolder;
742             }
743             else if (m_idCmdFirst + id == m_idCmdLink)
744             {
745                 hIcon = m_hIconLink;
746             }
747             else
748             {
749                 SHELLNEW_ITEM *pItem = FindItemFromIdOffset(id);
750                 if (pItem)
751                     hIcon = pItem->hIcon;
752             }
753 
754             if (!hIcon)
755                 break;
756 
757             DrawIconEx(lpdis->hDC,
758                        2,
759                        lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - 16) / 2,
760                        hIcon,
761                        16,
762                        16,
763                        0, NULL, DI_NORMAL);
764 
765             if(plResult)
766                 *plResult = TRUE;
767         }
768     }
769 
770     return S_OK;
771 }
772 
773 HRESULT WINAPI
774 CNewMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
775                      IDataObject *pdtobj, HKEY hkeyProgID)
776 {
777     const INT cx = GetSystemMetrics(SM_CXSMICON), cy = GetSystemMetrics(SM_CYSMICON);
778     WCHAR wszIconPath[MAX_PATH];
779     int icon_idx;
780 
781     m_pidlFolder = ILClone(pidlFolder);
782 
783     /* Load folder and shortcut icons */
784     if (HLM_GetIconW(IDI_SHELL_FOLDER - 1, wszIconPath, _countof(wszIconPath), &icon_idx))
785     {
786         ::ExtractIconExW(wszIconPath, icon_idx, &m_hIconFolder, NULL, 1);
787         m_bCustomIconFolder = TRUE;
788     }
789     else
790     {
791         m_hIconFolder = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_FOLDER), IMAGE_ICON, cx, cy, LR_SHARED);
792     }
793 
794     if (HLM_GetIconW(IDI_SHELL_SHORTCUT - 1, wszIconPath, _countof(wszIconPath), &icon_idx))
795     {
796         ::ExtractIconExW(wszIconPath, icon_idx, &m_hIconLink, NULL, 1);
797         m_bCustomIconLink = TRUE;
798     }
799     else
800     {
801         m_hIconLink = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT), IMAGE_ICON, cx, cy, LR_SHARED);
802     }
803 
804     return S_OK;
805 }
806