[SHELL32] Implement 'New Link' (Retrial of #1510), CORE-15511 (#1518)

Correctly create cache about ".lnk" and handling in the member functions.

Co-authored-by: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
This commit is contained in:
Katayama Hirofumi MZ 2019-04-23 10:48:34 +09:00 committed by Hermès Bélusca-Maïto
parent f2512254e0
commit 8e60c2efee
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 164 additions and 152 deletions

View file

@ -4,6 +4,7 @@
* Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org) * Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org)
* Copyright 2009 Andrew Hill * Copyright 2009 Andrew Hill
* Copyright 2012 Rafal Harabien * Copyright 2012 Rafal Harabien
* Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -60,18 +61,14 @@ void CNewMenu::UnloadAllItems()
{ {
SHELLNEW_ITEM *pCurItem; SHELLNEW_ITEM *pCurItem;
/* Unload normal items */ /* Unload the handler items, including the link item */
while (m_pItems) while (m_pItems)
{ {
pCurItem = m_pItems; pCurItem = m_pItems;
m_pItems = m_pItems->pNext; m_pItems = m_pItems->pNext;
UnloadItem(pCurItem); UnloadItem(pCurItem);
} }
m_pItems = NULL;
/* Unload link item */
if (m_pLinkItem)
UnloadItem(m_pLinkItem);
m_pLinkItem = NULL; m_pLinkItem = NULL;
} }
@ -183,21 +180,21 @@ CNewMenu::CacheItems()
if (pNewItem) if (pNewItem)
{ {
dwSize += wcslen(wszName) + 1; dwSize += wcslen(wszName) + 1;
if (wcsicmp(pNewItem->pwszExt, L".lnk") == 0) if (!m_pLinkItem && wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
{ {
/* Link handler */ /* The unique link handler */
m_pLinkItem = pNewItem; m_pLinkItem = pNewItem;
} }
/* Add at the end of the list */
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else else
{ {
/* Add at the end of list */ pCurItem = m_pItems = pNewItem;
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else
pCurItem = m_pItems = pNewItem;
} }
} }
} }
@ -208,13 +205,10 @@ CNewMenu::CacheItems()
if (!lpValues) if (!lpValues)
return FALSE; return FALSE;
lpValue = lpValues; for (pCurItem = m_pItems, lpValue = lpValues; pCurItem; pCurItem = pCurItem->pNext)
pCurItem = m_pItems;
while (pCurItem)
{ {
memcpy(lpValue, pCurItem->pwszExt, (wcslen(pCurItem->pwszExt) + 1) * sizeof(WCHAR)); memcpy(lpValue, pCurItem->pwszExt, (wcslen(pCurItem->pwszExt) + 1) * sizeof(WCHAR));
lpValue += wcslen(pCurItem->pwszExt) + 1; lpValue += wcslen(pCurItem->pwszExt) + 1;
pCurItem = pCurItem->pNext;
} }
if (RegCreateKeyEx(HKEY_CURRENT_USER, ShellNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS) if (RegCreateKeyEx(HKEY_CURRENT_USER, ShellNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
@ -269,21 +263,21 @@ CNewMenu::LoadCachedItems()
pNewItem = LoadItem(wszName); pNewItem = LoadItem(wszName);
if (pNewItem) if (pNewItem)
{ {
if (wcsicmp(pNewItem->pwszExt, L".lnk") == 0) if (!m_pLinkItem && wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
{ {
/* Link handler */ /* The unique link handler */
m_pLinkItem = pNewItem; m_pLinkItem = pNewItem;
} }
/* Add at the end of the list */
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else else
{ {
/* Add at the end of list */ pCurItem = m_pItems = pNewItem;
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else
pCurItem = m_pItems = pNewItem;
} }
} }
} }
@ -299,27 +293,13 @@ CNewMenu::LoadAllItems()
{ {
/* If there are any unload them */ /* If there are any unload them */
UnloadAllItems(); UnloadAllItems();
if (!LoadCachedItems()) if (!LoadCachedItems())
{ {
CacheItems(); CacheItems();
} }
if (!m_pLinkItem)
{
m_pLinkItem = static_cast<SHELLNEW_ITEM *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SHELLNEW_ITEM)));
if (m_pLinkItem)
{
m_pLinkItem->Type = SHELLNEW_TYPE_NULLFILE;
m_pLinkItem->pwszDesc = _wcsdup(L"Link");
m_pLinkItem->pwszExt = _wcsdup(L".lnk");
}
}
if (m_pItems == NULL) return (m_pItems != NULL);
return FALSE;
else
return TRUE;
} }
UINT UINT
@ -349,14 +329,17 @@ CNewMenu::InsertShellNewItems(HMENU hMenu, UINT idCmdFirst, UINT Pos)
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii)) if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
++idCmd; ++idCmd;
/* Insert new shortcut action */ /* Insert the new shortcut action */
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWLINK, wszBuf, _countof(wszBuf))) if (m_pLinkItem)
wszBuf[0] = 0; {
mii.dwTypeData = wszBuf; if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWLINK, wszBuf, _countof(wszBuf)))
mii.cch = wcslen(mii.dwTypeData); wszBuf[0] = 0;
mii.wID = idCmd; mii.dwTypeData = wszBuf;
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii)) mii.cch = wcslen(mii.dwTypeData);
++idCmd; mii.wID = idCmd;
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
++idCmd;
}
/* Insert seperator for custom new action */ /* Insert seperator for custom new action */
mii.fMask = MIIM_TYPE | MIIM_ID; mii.fMask = MIIM_TYPE | MIIM_ID;
@ -371,6 +354,10 @@ CNewMenu::InsertShellNewItems(HMENU hMenu, UINT idCmdFirst, UINT Pos)
SHELLNEW_ITEM *pCurItem = m_pItems; SHELLNEW_ITEM *pCurItem = m_pItems;
while (pCurItem) while (pCurItem)
{ {
/* Skip shortcut item */
if (pCurItem == m_pLinkItem)
continue;
TRACE("szDesc %s\n", debugstr_w(pCurItem->pwszDesc)); TRACE("szDesc %s\n", debugstr_w(pCurItem->pwszDesc));
mii.dwTypeData = pCurItem->pwszDesc; mii.dwTypeData = pCurItem->pwszDesc;
mii.cch = wcslen(mii.dwTypeData); mii.cch = wcslen(mii.dwTypeData);
@ -404,13 +391,18 @@ CNewMenu::SHELLNEW_ITEM *CNewMenu::FindItemFromIdOffset(UINT IdOffset)
return pItem; return pItem;
} }
HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName) HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName, BOOL bRename)
{ {
CComPtr<IShellBrowser> lpSB; CComPtr<IShellBrowser> lpSB;
CComPtr<IShellView> lpSV; CComPtr<IShellView> lpSV;
HRESULT hr = E_FAIL; HRESULT hr = E_FAIL;
LPITEMIDLIST pidl; LPITEMIDLIST pidl;
PITEMID_CHILD pidlNewItem; PITEMID_CHILD pidlNewItem;
DWORD dwSelectFlags;
dwSelectFlags = SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
if (bRename)
dwSelectFlags |= SVSI_EDIT;
/* Notify the view object about the new item */ /* Notify the view object about the new item */
SHChangeNotify(wEventId, uFlags, (LPCVOID) pszName, NULL); SHChangeNotify(wEventId, uFlags, (LPCVOID) pszName, NULL);
@ -430,8 +422,7 @@ HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName)
pidlNewItem = ILFindLastID(pidl); pidlNewItem = ILFindLastID(pidl);
hr = lpSV->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | hr = lpSV->SelectItem(pidlNewItem, dwSelectFlags);
SVSI_FOCUSED | SVSI_SELECT);
SHFree(pidl); SHFree(pidl);
@ -463,121 +454,139 @@ HRESULT CNewMenu::CreateNewFolder(LPCMINVOKECOMMANDINFO lpici)
return E_FAIL; return E_FAIL;
/* Show and select the new item in the def view */ /* Show and select the new item in the def view */
SelectNewItem(SHCNE_MKDIR, SHCNF_PATHW, wszName); SelectNewItem(SHCNE_MKDIR, SHCNF_PATHW, wszName, TRUE);
return S_OK;
}
HRESULT CNewMenu::NewItemByCommand(SHELLNEW_ITEM *pItem, LPCWSTR wszPath)
{
WCHAR wszBuf[MAX_PATH];
LPWSTR Ptr, pwszCmd;
WCHAR wszTemp[MAX_PATH];
STARTUPINFOW si;
PROCESS_INFORMATION pi;
if (!ExpandEnvironmentStringsW((LPWSTR)pItem->pData, wszBuf, _countof(wszBuf)))
{
TRACE("ExpandEnvironmentStrings failed\n");
return E_FAIL;
}
/* Expand command parameter, FIXME: there can be more modifiers */
Ptr = wcsstr(wszBuf, L"%1");
if (Ptr)
{
Ptr[1] = L's';
StringCbPrintfW(wszTemp, sizeof(wszTemp), wszBuf, wszPath);
pwszCmd = wszTemp;
}
else
{
pwszCmd = wszBuf;
}
/* Create process */
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (CreateProcessW(NULL, pwszCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return S_OK;
}
else
{
ERR("Failed to create process\n");
return E_FAIL;
}
}
HRESULT CNewMenu::NewItemByNonCommand(SHELLNEW_ITEM *pItem, LPWSTR wszName,
DWORD cchNameMax, LPCWSTR wszPath)
{
WCHAR wszBuf[MAX_PATH];
WCHAR wszNewFile[MAX_PATH];
BOOL bSuccess = TRUE;
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszBuf, _countof(wszBuf)))
return E_FAIL;
StringCchPrintfW(wszNewFile, _countof(wszNewFile), L"%s %s%s", wszBuf, pItem->pwszDesc, pItem->pwszExt);
/* Create the name of the new file */
if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFile))
return E_FAIL;
/* Create new file */
HANDLE hFile = CreateFileW(wszName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
if (pItem->Type == SHELLNEW_TYPE_DATA)
{
/* Write a content */
DWORD cbWritten;
WriteFile(hFile, pItem->pData, pItem->cbData, &cbWritten, NULL);
}
/* Close file now */
CloseHandle(hFile);
}
else
{
bSuccess = FALSE;
}
if (pItem->Type == SHELLNEW_TYPE_FILENAME)
{
/* Copy file */
if (!CopyFileW((LPWSTR)pItem->pData, wszName, FALSE))
ERR("Copy file failed: %ls\n", (LPWSTR)pItem->pData);
}
/* Show message if we failed */
if (bSuccess)
{
TRACE("Notifying fs %s\n", debugstr_w(wszName));
SelectNewItem(SHCNE_CREATE, SHCNF_PATHW, wszName, pItem != m_pLinkItem);
}
else
{
StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Cannot create file: %s", wszName);
MessageBoxW(NULL, wszBuf, L"Cannot create file", MB_OK|MB_ICONERROR); // FIXME load localized error msg
}
return S_OK; return S_OK;
} }
HRESULT CNewMenu::CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi) HRESULT CNewMenu::CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi)
{ {
WCHAR wszBuf[MAX_PATH];
WCHAR wszPath[MAX_PATH];
HRESULT hr; HRESULT hr;
WCHAR wszPath[MAX_PATH], wszName[MAX_PATH];
/* Get folder path */ /* Get folder path */
hr = SHGetPathFromIDListW(m_pidlFolder, wszPath); hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
if (FAILED_UNEXPECTEDLY(hr)) if (FAILED_UNEXPECTEDLY(hr))
return hr; return hr;
if (pItem == m_pLinkItem)
{
NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
NewItemByCommand(pItem, wszName);
return S_OK;
}
switch (pItem->Type) switch (pItem->Type)
{ {
case SHELLNEW_TYPE_COMMAND: case SHELLNEW_TYPE_COMMAND:
{ NewItemByCommand(pItem, wszPath);
LPWSTR Ptr, pwszCmd;
WCHAR wszTemp[MAX_PATH];
STARTUPINFOW si;
PROCESS_INFORMATION pi;
if (!ExpandEnvironmentStringsW((LPWSTR)pItem->pData, wszBuf, _countof(wszBuf)))
{
TRACE("ExpandEnvironmentStrings failed\n");
break;
}
/* Expand command parameter, FIXME: there can be more modifiers */
Ptr = wcsstr(wszBuf, L"%1");
if (Ptr)
{
Ptr[1] = L's';
StringCbPrintfW(wszTemp, sizeof(wszTemp), wszBuf, wszPath);
pwszCmd = wszTemp;
}
else
{
pwszCmd = wszBuf;
}
/* Create process */
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (CreateProcessW(NULL, pwszCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
ERR("Failed to create process\n");
}
break; break;
}
case SHELLNEW_TYPE_DATA: case SHELLNEW_TYPE_DATA:
case SHELLNEW_TYPE_FILENAME: case SHELLNEW_TYPE_FILENAME:
case SHELLNEW_TYPE_NULLFILE: case SHELLNEW_TYPE_NULLFILE:
{ NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
BOOL bSuccess = TRUE;
WCHAR wszName[MAX_PATH];
WCHAR wszNewFile[MAX_PATH];
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszBuf, _countof(wszBuf)))
return E_FAIL;
StringCchPrintfW(wszNewFile, _countof(wszNewFile), L"%s %s%s", wszBuf, pItem->pwszDesc, pItem->pwszExt);
/* Create the name of the new file */
if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFile))
return E_FAIL;
/* Create new file */
HANDLE hFile = CreateFileW(wszName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
if (pItem->Type == SHELLNEW_TYPE_DATA)
{
/* Write a content */
DWORD cbWritten;
WriteFile(hFile, pItem->pData, pItem->cbData, &cbWritten, NULL);
}
/* Close file now */
CloseHandle(hFile);
}
else
{
bSuccess = FALSE;
}
if (pItem->Type == SHELLNEW_TYPE_FILENAME)
{
/* Copy file */
if (!CopyFileW((LPWSTR)pItem->pData, wszName, FALSE))
ERR("Copy file failed: %ls\n", (LPWSTR)pItem->pData);
}
/* Show message if we failed */
if (bSuccess)
{
TRACE("Notifying fs %s\n", debugstr_w(wszName));
SelectNewItem(SHCNE_CREATE, SHCNF_PATHW, wszName);
}
else
{
StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Cannot create file: %s", wszName);
MessageBoxW(NULL, wszBuf, L"Cannot create file", MB_OK|MB_ICONERROR); // FIXME load localized error msg
}
break; break;
}
case SHELLNEW_TYPE_INVALID: case SHELLNEW_TYPE_INVALID:
ERR("Invalid type\n"); ERR("Invalid type\n");

View file

@ -56,7 +56,7 @@ private:
LPITEMIDLIST m_pidlFolder; LPITEMIDLIST m_pidlFolder;
LPWSTR m_wszPath; LPWSTR m_wszPath;
SHELLNEW_ITEM *m_pItems; SHELLNEW_ITEM *m_pItems;
SHELLNEW_ITEM *m_pLinkItem; SHELLNEW_ITEM *m_pLinkItem; // Points to the link handler item in the m_pItems list.
CComPtr<IUnknown> m_pSite; CComPtr<IUnknown> m_pSite;
HMENU m_hSubMenu; HMENU m_hSubMenu;
HICON m_hiconFolder, m_hiconLink; HICON m_hiconFolder, m_hiconLink;
@ -71,7 +71,10 @@ private:
SHELLNEW_ITEM *FindItemFromIdOffset(UINT IdOffset); SHELLNEW_ITEM *FindItemFromIdOffset(UINT IdOffset);
HRESULT CreateNewFolder(LPCMINVOKECOMMANDINFO lpici); HRESULT CreateNewFolder(LPCMINVOKECOMMANDINFO lpici);
HRESULT CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi); HRESULT CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi);
HRESULT SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName); HRESULT SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName, BOOL bRename);
HRESULT NewItemByCommand(SHELLNEW_ITEM *pItem, LPCWSTR wszPath);
HRESULT NewItemByNonCommand(SHELLNEW_ITEM *pItem, LPWSTR wszName,
DWORD cchNameMax, LPCWSTR wszPath);
public: public:
CNewMenu(); CNewMenu();