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