2 * provides new shell item service
4 * Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org)
5 * Copyright 2009 Andrew Hill
6 * Copyright 2012 Rafal Harabien
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
46 void CNewMenu::UnloadItem(SHELLNEW_ITEM
*pItem
)
48 /* Note: free allows NULL as argument */
50 free(pItem
->pwszDesc
);
54 DestroyIcon(pItem
->hIcon
);
56 HeapFree(GetProcessHeap(), 0, pItem
);
59 void CNewMenu::UnloadAllItems()
61 SHELLNEW_ITEM
*pCurItem
;
63 /* Unload normal items */
67 m_pItems
= m_pItems
->pNext
;
72 /* Unload link item */
74 UnloadItem(m_pLinkItem
);
78 CNewMenu::SHELLNEW_ITEM
*CNewMenu::LoadItem(LPCWSTR pwszExt
)
81 WCHAR wszBuf
[MAX_PATH
];
85 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s\\ShellNew", pwszExt
);
87 TRACE("LoadItem Keyname %s Name %s\n", debugstr_w(pwszExt
), debugstr_w(wszBuf
));
89 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
91 TRACE("Failed to open key\n");
95 /* Find first valid value */
103 {L
"FileName", SHELLNEW_TYPE_FILENAME
, TRUE
, TRUE
},
104 {L
"Command", SHELLNEW_TYPE_COMMAND
, TRUE
, TRUE
},
105 {L
"Data", SHELLNEW_TYPE_DATA
, TRUE
, FALSE
},
106 {L
"NullFile", SHELLNEW_TYPE_NULLFILE
, FALSE
},
111 for (i
= 0; Types
[i
].pszName
; ++i
)
113 /* Note: We are using ANSI function because strings can be treated as data */
115 DWORD dwFlags
= Types
[i
].bStr
? RRF_RT_REG_SZ
: RRF_RT_ANY
;
117 if (RegGetValueW(hKey
, NULL
, Types
[i
].pszName
, dwFlags
, NULL
, NULL
, &cbData
) == ERROR_SUCCESS
)
119 if (Types
[i
].bNeedData
&& cbData
> 0)
121 pData
= (BYTE
*)malloc(cbData
);
122 RegGetValueW(hKey
, NULL
, Types
[i
].pszName
, dwFlags
, &dwType
, pData
, &cbData
);
123 if (!Types
[i
].bStr
&& (dwType
== REG_SZ
|| dwType
== REG_EXPAND_SZ
))
125 PBYTE pData2
= (PBYTE
)malloc(cbData
);
126 cbData
= WideCharToMultiByte(CP_ACP
, 0, (LPWSTR
)pData
, -1, (LPSTR
)pData2
, cbData
, NULL
, NULL
);
136 /* Was any key found? */
137 if (!Types
[i
].pszName
)
141 if (!SHGetFileInfoW(pwszExt
, FILE_ATTRIBUTE_NORMAL
, &fi
, sizeof(fi
), SHGFI_USEFILEATTRIBUTES
|SHGFI_TYPENAME
|SHGFI_ICON
|SHGFI_SMALLICON
))
144 /* Create new item */
145 SHELLNEW_ITEM
*pNewItem
= static_cast<SHELLNEW_ITEM
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SHELLNEW_ITEM
)));
152 TRACE("new item %ls\n", fi
.szTypeName
);
153 pNewItem
->Type
= Types
[i
].Type
;
154 pNewItem
->pData
= pData
;
155 pNewItem
->cbData
= pData
? cbData
: 0;
156 pNewItem
->pwszExt
= _wcsdup(pwszExt
);
157 pNewItem
->pwszDesc
= _wcsdup(fi
.szTypeName
);
159 pNewItem
->hIcon
= fi
.hIcon
;
165 CNewMenu::LoadAllItems()
168 WCHAR wszName
[MAX_PATH
];
169 SHELLNEW_ITEM
*pNewItem
;
170 SHELLNEW_ITEM
*pCurItem
= NULL
;
172 /* If there are any unload them */
175 /* Enumerate all extesions */
176 while (RegEnumKeyW(HKEY_CLASSES_ROOT
, dwIndex
++, wszName
, _countof(wszName
)) == ERROR_SUCCESS
)
178 if (wszName
[0] != L
'.')
181 pNewItem
= LoadItem(wszName
);
184 if (wcsicmp(pNewItem
->pwszExt
, L
".lnk") == 0)
187 m_pLinkItem
= pNewItem
;
191 /* Add at the end of list */
194 pCurItem
->pNext
= pNewItem
;
198 pCurItem
= m_pItems
= pNewItem
;
205 m_pLinkItem
= static_cast<SHELLNEW_ITEM
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SHELLNEW_ITEM
)));
208 m_pLinkItem
->Type
= SHELLNEW_TYPE_NULLFILE
;
209 m_pLinkItem
->pwszDesc
= _wcsdup(L
"Link");
210 m_pLinkItem
->pwszExt
= _wcsdup(L
".lnk");
214 if (m_pItems
== NULL
)
221 CNewMenu::InsertShellNewItems(HMENU hMenu
, UINT idCmdFirst
, UINT Pos
)
225 UINT idCmd
= idCmdFirst
;
227 if (m_pItems
== NULL
)
233 ZeroMemory(&mii
, sizeof(mii
));
234 mii
.cbSize
= sizeof(mii
);
236 /* Insert new folder action */
237 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWFOLDER
, wszBuf
, _countof(wszBuf
)))
239 mii
.fMask
= MIIM_ID
| MIIM_BITMAP
| MIIM_STRING
;
240 mii
.dwTypeData
= wszBuf
;
241 mii
.cch
= wcslen(mii
.dwTypeData
);
243 mii
.hbmpItem
= HBMMENU_CALLBACK
;
244 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
247 /* Insert new shortcut action */
248 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWLINK
, wszBuf
, _countof(wszBuf
)))
250 mii
.dwTypeData
= wszBuf
;
251 mii
.cch
= wcslen(mii
.dwTypeData
);
253 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
256 /* Insert seperator for custom new action */
257 mii
.fMask
= MIIM_TYPE
| MIIM_ID
;
258 mii
.fType
= MFT_SEPARATOR
;
260 InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
);
262 /* Insert rest of items */
263 mii
.fMask
= MIIM_ID
| MIIM_BITMAP
| MIIM_STRING
;
266 SHELLNEW_ITEM
*pCurItem
= m_pItems
;
269 TRACE("szDesc %s\n", debugstr_w(pCurItem
->pwszDesc
));
270 mii
.dwTypeData
= pCurItem
->pwszDesc
;
271 mii
.cch
= wcslen(mii
.dwTypeData
);
273 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
275 pCurItem
= pCurItem
->pNext
;
278 return idCmd
- idCmdFirst
;
281 CNewMenu::SHELLNEW_ITEM
*CNewMenu::FindItemFromIdOffset(UINT IdOffset
)
284 return NULL
; /* Folder */
287 return m_pLinkItem
; /* shortcut */
289 /* Find shell new item */
290 SHELLNEW_ITEM
*pItem
= m_pItems
;
291 for (UINT i
= 2; pItem
; ++i
)
296 pItem
= pItem
->pNext
;
302 HRESULT
CNewMenu::SelectNewItem(LPCMINVOKECOMMANDINFO lpici
, LONG wEventId
, UINT uFlags
, LPWSTR pszName
)
304 CComPtr
<IShellBrowser
> lpSB
;
305 CComPtr
<IShellView
> lpSV
;
308 PITEMID_CHILD pidlNewItem
;
310 /* Notify the view object about the new item */
311 SHChangeNotify(wEventId
, uFlags
, (LPCVOID
) pszName
, NULL
);
313 /* FIXME: I think that this can be implemented using callbacks to the shell folder */
315 /* Note: CWM_GETISHELLBROWSER returns shell browser without adding reference */
316 lpSB
= (LPSHELLBROWSER
)SendMessageA(lpici
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
320 hr
= lpSB
->QueryActiveShellView(&lpSV
);
324 /* Attempt to get the pidl of the new item */
325 hr
= SHILCreateFromPathW(pszName
, &pidl
, NULL
);
326 if (FAILED_UNEXPECTEDLY(hr
))
329 pidlNewItem
= ILFindLastID(pidl
);
331 hr
= lpSV
->SelectItem(pidlNewItem
, SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
|
332 SVSI_FOCUSED
| SVSI_SELECT
);
339 HRESULT
CNewMenu::CreateNewFolder(LPCMINVOKECOMMANDINFO lpici
)
341 WCHAR wszPath
[MAX_PATH
];
342 WCHAR wszName
[MAX_PATH
];
343 WCHAR wszNewFolder
[25];
346 /* Get folder path */
347 hr
= SHGetPathFromIDListW(m_pidlFolder
, wszPath
);
348 if (FAILED_UNEXPECTEDLY(hr
))
351 if (!LoadStringW(shell32_hInstance
, IDS_NEWFOLDER
, wszNewFolder
, _countof(wszNewFolder
)))
354 /* Create the name of the new directory */
355 if (!PathYetAnotherMakeUniqueName(wszName
, wszPath
, NULL
, wszNewFolder
))
358 /* Create the new directory and show the appropriate dialog in case of error */
359 if (SHCreateDirectory (lpici
->hwnd
, wszName
) != ERROR_SUCCESS
)
362 /* Show and select the new item in the def view */
363 SelectNewItem(lpici
, SHCNE_MKDIR
, SHCNF_PATHW
, wszName
);
368 HRESULT
CNewMenu::CreateNewItem(SHELLNEW_ITEM
*pItem
, LPCMINVOKECOMMANDINFO lpcmi
)
370 WCHAR wszBuf
[MAX_PATH
];
371 WCHAR wszPath
[MAX_PATH
];
374 /* Get folder path */
375 hr
= SHGetPathFromIDListW(m_pidlFolder
, wszPath
);
376 if (FAILED_UNEXPECTEDLY(hr
))
381 case SHELLNEW_TYPE_COMMAND
:
384 WCHAR wszTemp
[MAX_PATH
];
386 PROCESS_INFORMATION pi
;
388 if (!ExpandEnvironmentStringsW((LPWSTR
)pItem
->pData
, wszBuf
, MAX_PATH
))
390 TRACE("ExpandEnvironmentStrings failed\n");
394 /* Expand command parameter, FIXME: there can be more modifiers */
395 Ptr
= wcsstr(wszBuf
, L
"%1");
399 StringCbPrintfW(wszTemp
, sizeof(wszTemp
), wszBuf
, wszPath
);
406 ZeroMemory(&si
, sizeof(si
));
408 if (CreateProcessW(NULL
, pwszCmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
410 CloseHandle(pi
.hProcess
);
411 CloseHandle(pi
.hThread
);
413 ERR("Failed to create process\n");
416 case SHELLNEW_TYPE_DATA
:
417 case SHELLNEW_TYPE_FILENAME
:
418 case SHELLNEW_TYPE_NULLFILE
:
420 BOOL bSuccess
= TRUE
;
421 LPWSTR pwszFilename
= NULL
;
422 size_t cchFilenameMax
= 0;
424 /* Build new file name */
425 LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, wszBuf
, _countof(wszBuf
));
426 StringCchCatExW(wszPath
, _countof(wszPath
), L
"\\", &pwszFilename
, &cchFilenameMax
, 0);
427 StringCchPrintfW(pwszFilename
, cchFilenameMax
, L
"%s %s%s", wszBuf
, pItem
->pwszDesc
, pItem
->pwszExt
);
429 /* Find unique name */
430 for (UINT i
= 2; PathFileExistsW(wszPath
); ++i
)
432 StringCchPrintfW(pwszFilename
, cchFilenameMax
, L
"%s %s (%u)%s", wszBuf
, pItem
->pwszDesc
, i
, pItem
->pwszExt
);
433 TRACE("New Filename %ls\n", pwszFilename
);
436 /* Create new file */
437 HANDLE hFile
= CreateFileW(wszPath
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, NULL
);
438 if (hFile
!= INVALID_HANDLE_VALUE
)
440 if (pItem
->Type
== SHELLNEW_TYPE_DATA
)
442 /* Write a content */
444 WriteFile(hFile
, pItem
->pData
, pItem
->cbData
, &cbWritten
, NULL
);
452 if (pItem
->Type
== SHELLNEW_TYPE_FILENAME
)
455 if (!CopyFileW((LPWSTR
)pItem
->pData
, wszPath
, FALSE
))
456 ERR("Copy file failed: %ls\n", (LPWSTR
)pItem
->pData
);
459 /* Show message if we failed */
462 TRACE("Notifying fs %s\n", debugstr_w(wszPath
));
463 SelectNewItem(lpcmi
, SHCNE_CREATE
, SHCNF_PATHW
, wszPath
);
467 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Cannot create file: %s", pwszFilename
);
468 MessageBoxW(NULL
, wszBuf
, L
"Cannot create file", MB_OK
|MB_ICONERROR
); // FIXME
472 case SHELLNEW_TYPE_INVALID
:
473 ERR("Invalid type\n");
480 HRESULT STDMETHODCALLTYPE
CNewMenu::SetSite(IUnknown
*pUnkSite
)
486 HRESULT STDMETHODCALLTYPE
CNewMenu::GetSite(REFIID riid
, void **ppvSite
)
488 return m_pSite
->QueryInterface(riid
, ppvSite
);
493 CNewMenu::QueryContextMenu(HMENU hMenu
,
503 m_idCmdFirst
= idCmdFirst
;
505 TRACE("%p %p %u %u %u %u\n", this,
506 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
508 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, wszNew
, _countof(wszNew
)))
511 m_hSubMenu
= CreateMenu();
515 cItems
= InsertShellNewItems(m_hSubMenu
, idCmdFirst
, 0);
517 memset(&mii
, 0, sizeof(mii
));
518 mii
.cbSize
= sizeof(mii
);
519 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
| MIIM_SUBMENU
;
520 mii
.fType
= MFT_STRING
;
522 mii
.dwTypeData
= wszNew
;
523 mii
.cch
= wcslen(mii
.dwTypeData
);
524 mii
.fState
= MFS_ENABLED
;
525 mii
.hSubMenu
= m_hSubMenu
;
527 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
530 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, cItems
);
535 CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
539 if (LOWORD(lpici
->lpVerb
) == 0)
540 hr
= CreateNewFolder(lpici
);
543 SHELLNEW_ITEM
*pItem
= FindItemFromIdOffset(LOWORD(lpici
->lpVerb
));
545 hr
= CreateNewItem(pItem
, lpici
);
548 TRACE("CNewMenu::InvokeCommand %x\n", hr
);
554 CNewMenu::GetCommandString(UINT_PTR idCmd
,
560 FIXME("%p %lu %u %p %p %u\n", this,
561 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
568 CNewMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
575 CNewMenu::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*plResult
)
581 MEASUREITEMSTRUCT
* lpmis
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
582 if (!lpmis
|| lpmis
->CtlType
!= ODT_MENU
)
585 if (lpmis
->itemWidth
< (UINT
)GetSystemMetrics(SM_CXMENUCHECK
))
586 lpmis
->itemWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
587 if (lpmis
->itemHeight
< 16)
588 lpmis
->itemHeight
= 16;
596 DRAWITEMSTRUCT
* lpdis
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
597 if (!lpdis
|| lpdis
->CtlType
!= ODT_MENU
)
600 DWORD id
= LOWORD(lpdis
->itemID
) - m_idCmdFirst
;
603 hIcon
= m_hiconFolder
;
608 SHELLNEW_ITEM
*pItem
= FindItemFromIdOffset(id
);
610 hIcon
= pItem
->hIcon
;
616 DrawIconEx(lpdis
->hDC
,
618 lpdis
->rcItem
.top
+ (lpdis
->rcItem
.bottom
- lpdis
->rcItem
.top
- 16) / 2,
633 CNewMenu::Initialize(LPCITEMIDLIST pidlFolder
,
634 IDataObject
*pdtobj
, HKEY hkeyProgID
)
636 m_pidlFolder
= ILClone(pidlFolder
);
638 /* Load folder and shortcut icons */
639 m_hiconFolder
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_FOLDER
), IMAGE_ICON
, 16, 16, LR_SHARED
);
640 m_hiconLink
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT
), IMAGE_ICON
, 16, 16, LR_SHARED
);