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
);
41 DeleteObject(m_hbmFolder
);
43 DeleteObject(m_hbmLink
);
46 void CNewMenu::UnloadItem(SHELLNEW_ITEM
*pItem
)
48 /* Note: free allows NULL as argument */
50 free(pItem
->pwszDesc
);
54 DeleteObject(pItem
->hBitmap
);
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 static HBITMAP
IconToBitmap(HICON hIcon
)
85 hdc
= CreateCompatibleDC(hdcScr
);
86 SetRect(&rc
, 0, 0, GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
));
87 hbm
= CreateCompatibleBitmap(hdcScr
, rc
.right
, rc
.bottom
);
88 ReleaseDC(NULL
, hdcScr
);
90 hbmOld
= (HBITMAP
)SelectObject(hdc
, hbm
);
91 FillRect(hdc
, &rc
, (HBRUSH
)(COLOR_MENU
+ 1));
92 if (!DrawIconEx(hdc
, 0, 0, hIcon
, rc
.right
, rc
.bottom
, 0, NULL
, DI_NORMAL
))
93 ERR("DrawIcon failed: %x\n", GetLastError());
94 SelectObject(hdc
, hbmOld
);
101 CNewMenu::SHELLNEW_ITEM
*CNewMenu::LoadItem(LPCWSTR pwszExt
)
104 WCHAR wszBuf
[MAX_PATH
];
108 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s\\ShellNew", pwszExt
);
110 TRACE("LoadItem Keyname %s Name %s\n", debugstr_w(pwszExt
), debugstr_w(wszBuf
));
112 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
114 TRACE("Failed to open key\n");
118 /* Find first valid value */
126 {L
"FileName", SHELLNEW_TYPE_FILENAME
, TRUE
, TRUE
},
127 {L
"Command", SHELLNEW_TYPE_COMMAND
, TRUE
, TRUE
},
128 {L
"Data", SHELLNEW_TYPE_DATA
, TRUE
, FALSE
},
129 {L
"NullFile", SHELLNEW_TYPE_NULLFILE
, FALSE
},
134 for (i
= 0; Types
[i
].pszName
; ++i
)
136 /* Note: We are using ANSI function because strings can be treated as data */
138 DWORD dwFlags
= Types
[i
].bStr
? RRF_RT_REG_SZ
: RRF_RT_ANY
;
140 if (RegGetValueW(hKey
, NULL
, Types
[i
].pszName
, dwFlags
, NULL
, NULL
, &cbData
) == ERROR_SUCCESS
)
142 if (Types
[i
].bNeedData
&& cbData
> 0)
144 pData
= (BYTE
*)malloc(cbData
);
145 RegGetValueW(hKey
, NULL
, Types
[i
].pszName
, dwFlags
, &dwType
, pData
, &cbData
);
146 if (!Types
[i
].bStr
&& (dwType
== REG_SZ
|| dwType
== REG_EXPAND_SZ
))
148 PBYTE pData2
= (PBYTE
)malloc(cbData
);
149 cbData
= WideCharToMultiByte(CP_ACP
, 0, (LPWSTR
)pData
, -1, (LPSTR
)pData2
, cbData
, NULL
, NULL
);
159 /* Was any key found? */
160 if (!Types
[i
].pszName
)
164 if (!SHGetFileInfoW(pwszExt
, FILE_ATTRIBUTE_NORMAL
, &fi
, sizeof(fi
), SHGFI_USEFILEATTRIBUTES
|SHGFI_TYPENAME
|SHGFI_ICON
|SHGFI_SMALLICON
))
167 /* Create new item */
168 SHELLNEW_ITEM
*pNewItem
= static_cast<SHELLNEW_ITEM
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SHELLNEW_ITEM
)));
175 TRACE("new item %ls\n", fi
.szTypeName
);
176 pNewItem
->Type
= Types
[i
].Type
;
177 pNewItem
->pData
= pData
;
178 pNewItem
->cbData
= pData
? cbData
: 0;
179 pNewItem
->pwszExt
= _wcsdup(pwszExt
);
180 pNewItem
->pwszDesc
= _wcsdup(fi
.szTypeName
);
183 pNewItem
->hBitmap
= IconToBitmap(fi
.hIcon
);
184 DestroyIcon(fi
.hIcon
);
191 CNewMenu::LoadAllItems()
194 WCHAR wszName
[MAX_PATH
];
195 SHELLNEW_ITEM
*pNewItem
;
196 SHELLNEW_ITEM
*pCurItem
= NULL
;
198 /* If there are any unload them */
201 /* Enumerate all extesions */
202 while (RegEnumKeyW(HKEY_CLASSES_ROOT
, dwIndex
++, wszName
, _countof(wszName
)) == ERROR_SUCCESS
)
204 if (wszName
[0] != L
'.')
207 pNewItem
= LoadItem(wszName
);
210 if (wcsicmp(pNewItem
->pwszExt
, L
".lnk") == 0)
213 m_pLinkItem
= pNewItem
;
217 /* Add at the end of list */
220 pCurItem
->pNext
= pNewItem
;
224 pCurItem
= m_pItems
= pNewItem
;
231 m_pLinkItem
= static_cast<SHELLNEW_ITEM
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SHELLNEW_ITEM
)));
234 m_pLinkItem
->Type
= SHELLNEW_TYPE_NULLFILE
;
235 m_pLinkItem
->pwszDesc
= _wcsdup(L
"Link");
236 m_pLinkItem
->pwszExt
= _wcsdup(L
".lnk");
240 if (m_pItems
== NULL
)
247 CNewMenu::InsertShellNewItems(HMENU hMenu
, UINT idCmdFirst
, UINT Pos
)
251 UINT idCmd
= idCmdFirst
;
253 if (m_pItems
== NULL
)
259 ZeroMemory(&mii
, sizeof(mii
));
260 mii
.cbSize
= sizeof(mii
);
262 /* Insert new folder action */
263 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWFOLDER
, wszBuf
, _countof(wszBuf
)))
265 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
266 mii
.fType
= MFT_STRING
;
267 mii
.dwTypeData
= wszBuf
;
268 mii
.cch
= wcslen(mii
.dwTypeData
);
272 mii
.fMask
|= MIIM_CHECKMARKS
;
273 mii
.hbmpChecked
= mii
.hbmpUnchecked
= m_hbmFolder
;
275 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
278 /* Insert new shortcut action */
279 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWLINK
, wszBuf
, _countof(wszBuf
)))
281 mii
.dwTypeData
= wszBuf
;
282 mii
.cch
= wcslen(mii
.dwTypeData
);
286 mii
.fMask
|= MIIM_CHECKMARKS
;
287 mii
.hbmpChecked
= mii
.hbmpUnchecked
= m_hbmLink
;
289 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
292 /* Insert seperator for custom new action */
293 mii
.fMask
= MIIM_TYPE
| MIIM_ID
;
294 mii
.fType
= MFT_SEPARATOR
;
296 InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
);
298 /* Insert rest of items */
299 mii
.fType
= MFT_STRING
;
300 mii
.fState
= MFS_ENABLED
;
302 SHELLNEW_ITEM
*pCurItem
= m_pItems
;
305 TRACE("szDesc %s\n", debugstr_w(pCurItem
->pwszDesc
));
306 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
307 mii
.dwTypeData
= pCurItem
->pwszDesc
;
308 mii
.cch
= wcslen(mii
.dwTypeData
);
310 if (pCurItem
->hBitmap
)
312 mii
.fMask
|= MIIM_CHECKMARKS
;
313 mii
.hbmpChecked
= mii
.hbmpUnchecked
= pCurItem
->hBitmap
;
315 if (InsertMenuItemW(hMenu
, Pos
++, TRUE
, &mii
))
317 pCurItem
= pCurItem
->pNext
;
320 return idCmd
- idCmdFirst
;
323 CNewMenu::SHELLNEW_ITEM
*CNewMenu::FindItemFromIdOffset(UINT IdOffset
)
326 return NULL
; /* Folder */
329 return m_pLinkItem
; /* shortcut */
331 /* Find shell new item */
332 SHELLNEW_ITEM
*pItem
= m_pItems
;
333 for (UINT i
= 2; pItem
; ++i
)
338 pItem
= pItem
->pNext
;
344 HRESULT
CNewMenu::CreateNewFolder(IShellView
*psv
)
346 WCHAR wszName
[MAX_PATH
];
347 CComPtr
<ISFHelper
> psfhlp
;
348 CComPtr
<IFolderView
> pFolderView
;
349 CComPtr
<IShellFolder
> pParentFolder
;
352 //if (m_pSite == NULL)
355 /* Get current folder */
356 hr
= IUnknown_QueryService(psv
, SID_IFolderView
, IID_PPV_ARG(IFolderView
, &pFolderView
));
360 hr
= pFolderView
->GetFolder(IID_PPV_ARG(IShellFolder
, &pParentFolder
));
364 hr
= pParentFolder
->QueryInterface(IID_PPV_ARG(ISFHelper
, &psfhlp
));
370 /* Get unique name and create a folder */
371 hr
= psfhlp
->GetUniqueName(wszName
, _countof(wszName
));
374 hr
= psfhlp
->AddFolder(0, wszName
, &pidl
);
378 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Cannot create folder: %s", wszName
);
379 MessageBoxW(NULL
, wszBuf
, L
"Cannot create folder", MB_OK
|MB_ICONERROR
);
385 psv
->SelectItem(pidl
, SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
|
386 SVSI_FOCUSED
| SVSI_SELECT
);
392 HRESULT
CNewMenu::CreateNewItem(SHELLNEW_ITEM
*pItem
, LPCMINVOKECOMMANDINFO lpcmi
, IShellView
*psv
)
396 WCHAR wszBuf
[MAX_PATH
];
397 WCHAR wszPath
[MAX_PATH
];
398 CComPtr
<IFolderView
> pFolderView
;
399 CComPtr
<IShellFolder
> pParentFolder
;
400 CComPtr
<IPersistFolder2
> psf
;
403 /* Get current folder */
404 hr
= IUnknown_QueryService(psv
, SID_IFolderView
, IID_PPV_ARG(IFolderView
, &pFolderView
));
408 hr
= pFolderView
->GetFolder(IID_PPV_ARG(IShellFolder
, &pParentFolder
));
412 if (pParentFolder
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &psf
)) != S_OK
)
414 ERR("Failed to get interface IID_IPersistFolder2\n");
418 if (psf
->GetCurFolder(&pidl
) != S_OK
)
420 ERR("IPersistFolder2_GetCurFolder failed\n");
424 /* Get folder path */
425 if (pParentFolder
== NULL
|| pParentFolder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &strTemp
) != S_OK
)
427 ERR("IShellFolder_GetDisplayNameOf failed\n");
430 StrRetToBufW(&strTemp
, pidl
, wszPath
, _countof(wszPath
));
434 case SHELLNEW_TYPE_COMMAND
:
437 WCHAR wszTemp
[MAX_PATH
];
439 PROCESS_INFORMATION pi
;
441 if (!ExpandEnvironmentStringsW((LPWSTR
)pItem
->pData
, wszBuf
, MAX_PATH
))
443 TRACE("ExpandEnvironmentStrings failed\n");
447 /* Expand command parameter, FIXME: there can be more modifiers */
448 Ptr
= wcsstr(wszBuf
, L
"%1");
452 StringCbPrintfW(wszTemp
, sizeof(wszTemp
), wszBuf
, wszPath
);
459 ZeroMemory(&si
, sizeof(si
));
461 if (CreateProcessW(NULL
, pwszCmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
463 CloseHandle(pi
.hProcess
);
464 CloseHandle(pi
.hThread
);
466 ERR("Failed to create process\n");
469 case SHELLNEW_TYPE_DATA
:
470 case SHELLNEW_TYPE_FILENAME
:
471 case SHELLNEW_TYPE_NULLFILE
:
473 BOOL bSuccess
= TRUE
;
474 LPWSTR pwszFilename
= NULL
;
475 size_t cchFilenameMax
= 0;
477 /* Build new file name */
478 LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, wszBuf
, _countof(wszBuf
));
479 StringCchCatExW(wszPath
, _countof(wszPath
), L
"\\", &pwszFilename
, &cchFilenameMax
, 0);
480 StringCchPrintfW(pwszFilename
, cchFilenameMax
, L
"%s %s%s", wszBuf
, pItem
->pwszDesc
, pItem
->pwszExt
);
482 /* Find unique name */
483 for (UINT i
= 2; PathFileExistsW(wszPath
); ++i
)
485 StringCchPrintfW(pwszFilename
, cchFilenameMax
, L
"%s %s (%u)%s", wszBuf
, pItem
->pwszDesc
, i
, pItem
->pwszExt
);
486 TRACE("New Filename %ls\n", pwszFilename
);
489 /* Create new file */
490 HANDLE hFile
= CreateFileW(wszPath
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, NULL
);
491 if (hFile
!= INVALID_HANDLE_VALUE
)
493 if (pItem
->Type
== SHELLNEW_TYPE_DATA
)
495 /* Write a content */
497 WriteFile(hFile
, pItem
->pData
, pItem
->cbData
, &cbWritten
, NULL
);
505 if (pItem
->Type
== SHELLNEW_TYPE_FILENAME
)
508 if (!CopyFileW((LPWSTR
)pItem
->pData
, wszPath
, FALSE
))
509 ERR("Copy file failed: %ls\n", (LPWSTR
)pItem
->pData
);
512 /* Show message if we failed */
515 TRACE("Notifying fs %s\n", debugstr_w(wszPath
));
516 SHChangeNotify(SHCNE_CREATE
, SHCNF_PATHW
, (LPCVOID
)wszPath
, NULL
);
520 hr
= _ILCreateFromPathW(wszPath
, &pidl
);
523 psv
->SelectItem(pidl
, SVSI_DESELECTOTHERS
|SVSI_EDIT
|SVSI_ENSUREVISIBLE
|SVSI_FOCUSED
|SVSI_SELECT
);
529 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Cannot create file: %s", pwszFilename
);
530 MessageBoxW(NULL
, wszBuf
, L
"Cannot create file", MB_OK
|MB_ICONERROR
); // FIXME
534 case SHELLNEW_TYPE_INVALID
:
535 ERR("Invalid type\n");
542 HRESULT STDMETHODCALLTYPE
CNewMenu::SetSite(IUnknown
*pUnkSite
)
548 HRESULT STDMETHODCALLTYPE
CNewMenu::GetSite(REFIID riid
, void **ppvSite
)
550 return m_pSite
->QueryInterface(riid
, ppvSite
);
555 CNewMenu::QueryContextMenu(HMENU hMenu
,
565 TRACE("%p %p %u %u %u %u\n", this,
566 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
568 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, wszNew
, _countof(wszNew
)))
571 m_hSubMenu
= CreateMenu();
575 cItems
= InsertShellNewItems(m_hSubMenu
, idCmdFirst
, 0);
577 memset(&mii
, 0, sizeof(mii
));
578 mii
.cbSize
= sizeof(mii
);
579 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
| MIIM_SUBMENU
;
580 mii
.fType
= MFT_STRING
;
582 mii
.dwTypeData
= wszNew
;
583 mii
.cch
= wcslen(mii
.dwTypeData
);
584 mii
.fState
= MFS_ENABLED
;
585 mii
.hSubMenu
= m_hSubMenu
;
587 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
590 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, cItems
);
595 CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
597 CComPtr
<IShellBrowser
> lpSB
;
598 CComPtr
<IShellView
> lpSV
;
601 /* Note: CWM_GETISHELLBROWSER returns shell browser without adding reference */
602 lpSB
= (LPSHELLBROWSER
)SendMessageA(lpici
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
604 lpSB
->QueryActiveShellView(&lpSV
);
606 if (LOWORD(lpici
->lpVerb
) == 0)
607 hr
= CreateNewFolder(lpSV
);
610 SHELLNEW_ITEM
*pItem
= FindItemFromIdOffset(LOWORD(lpici
->lpVerb
));
612 hr
= CreateNewItem(pItem
, lpici
, lpSV
);
615 TRACE("CNewMenu::InvokeCommand %x\n", hr
);
621 CNewMenu::GetCommandString(UINT_PTR idCmd
,
627 FIXME("%p %lu %u %p %p %u\n", this,
628 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
635 CNewMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
641 CNewMenu::Initialize(LPCITEMIDLIST pidlFolder
,
642 IDataObject
*pdtobj
, HKEY hkeyProgID
)
644 /* Load folder and shortcut icons */
645 HICON hIcon
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_FOLDER
), IMAGE_ICON
, 0, 0, LR_SHARED
);
646 m_hbmFolder
= hIcon
? IconToBitmap(hIcon
) : NULL
;
647 hIcon
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT
), IMAGE_ICON
, 0, 0, LR_SHARED
);
648 m_hbmLink
= hIcon
? IconToBitmap(hIcon
) : NULL
;