[FONTEXT][SHELL32][SDK] Support delete operation (#8639)

Improve usability of Fonts folder.
JIRA issue: CORE-17311
- Modify PIDL design to contain
  name and filename.
- Implement CFontExt::
  ParseDisplayName to parsing
  name as PIDL.
- Modify CDefaultContextMenu::
  GetCommandString and
  CDefaultContextMenu::
  DoCopyOrCut for DFM_GETVERBA,
  DFM_GETVERBW, DFM_CMD_COPY,
  and DFM_CMD_MOVE.
- Add IDS_CONFIRM_DELETE_FONT,
  IDS_CANTDELETEFONT, and
  IDS_PROPERTIES resource strings.
- Add SHMultiFileProperties
  prototype to <shlobj.h>.
This commit is contained in:
Katayama Hirofumi MZ
2026-02-13 18:37:06 +09:00
committed by GitHub
parent 5213cf717c
commit 6af07a31b0
26 changed files with 669 additions and 278 deletions

View File

@@ -73,7 +73,7 @@ HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEM
}
else
{
ERR("No file found for %S\n", fontEntry->Name);
ERR("No file found for %S\n", fontEntry->Name());
}
}
}

View File

@@ -42,25 +42,33 @@ public:
while (celt)
{
celt--;
if (m_Index < g_FontCache->Size())
{
CStringW Name = g_FontCache->Name(m_Index);
LPITEMIDLIST item = _ILCreate(Name);
if (!item)
{
hr = Fetched ? S_FALSE : E_OUTOFMEMORY;
break;
}
rgelt[Fetched] = item;
m_Index++;
Fetched++;
}
else
if (m_Index >= g_FontCache->Size())
{
hr = S_FALSE;
break;
}
if (!g_FontCache->IsMarkDeleted(m_Index))
{
CStringW Name = g_FontCache->Name(m_Index), FileName = g_FontCache->File(m_Index);
if (Name.IsEmpty() || FileName.IsEmpty())
{
ERR("Why is Name or FileName empty?\n");
}
else
{
// Create a PIDL
PITEMID_CHILD item = _ILCreate(Name, FileName);
if (!item)
{
hr = Fetched ? S_FALSE : E_OUTOFMEMORY;
break;
}
rgelt[Fetched++] = item;
--celt;
}
}
m_Index++;
}
if (pceltFetched)
@@ -70,7 +78,12 @@ public:
STDMETHODIMP Skip(ULONG celt) override
{
m_Index += celt;
for (ULONG i = 0; i < celt && m_Index < g_FontCache->Size(); ++i)
{
while (m_Index < g_FontCache->Size() && g_FontCache->IsMarkDeleted(m_Index))
++m_Index;
++m_Index;
}
return S_OK;
}

View File

@@ -67,9 +67,8 @@ STDMETHODIMP CFontBackgroundMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
if (FAILED_UNEXPECTEDLY(hr) || !CheckDataObject(pDataObj))
{
// Show error message
CStringW text, title;
title.LoadStringW(IDS_REACTOS_FONTS_FOLDER);
text.LoadStringW(IDS_INSTALL_FAILED);
CStringW text(MAKEINTRESOURCEW(IDS_INSTALL_FAILED));
CStringW title(MAKEINTRESOURCEW(IDS_REACTOS_FONTS_FOLDER));
MessageBoxW(m_hwnd, text, title, MB_ICONERROR);
return E_FAIL;
}

View File

@@ -11,8 +11,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(fontext);
CFontCache* g_FontCache = NULL;
CFontInfo::CFontInfo(LPCWSTR name)
CFontInfo::CFontInfo(PCWSTR name, PCWSTR value)
: m_Name(name)
, m_File(value)
, m_FileRead(false)
, m_AttrsRead(false)
, m_FileWriteTime({})
@@ -35,7 +36,7 @@ const CStringW& CFontInfo::File()
{
if (!m_FileRead)
{
if (Valid())
if (Valid() && m_File.IsEmpty())
{
// Read the filename stored in the registry.
// This can be either a filename or a full path
@@ -43,7 +44,7 @@ const CStringW& CFontInfo::File()
if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
{
CStringW Value;
DWORD dwAllocated = 128;
DWORD dwAllocated = MAX_PATH;
LSTATUS Status;
do
{
@@ -125,35 +126,69 @@ void CFontCache::SetFontDir(const LPCWSTR Path)
size_t CFontCache::Size()
{
if (m_Fonts.GetCount() == 0u)
if (m_Fonts.GetSize() == 0)
Read();
return m_Fonts.GetCount();
return m_Fonts.GetSize();
}
CStringW CFontCache::Name(size_t Index)
{
if (m_Fonts.GetCount() == 0u)
if (m_Fonts.GetSize() == 0)
Read();
if (Index >= m_Fonts.GetCount())
return CStringW();
if ((INT)Index >= m_Fonts.GetSize())
return L"";
return m_Fonts[Index].Name();
}
CStringW CFontCache::File(size_t Index)
{
if (m_Fonts.GetSize() == 0)
Read();
if ((INT)Index >= m_Fonts.GetSize())
return L"";
return m_Fonts[Index].File();
}
CFontInfo* CFontCache::Find(const FontPidlEntry* fontEntry)
{
for (UINT n = 0; n < Size(); ++n)
if (m_Fonts.GetSize() == 0)
Read();
for (INT i = 0; i < m_Fonts.GetSize(); ++i)
{
if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0)
if (m_Fonts[i].Name().CompareNoCase(fontEntry->Name()) == 0)
{
return &m_Fonts[n];
return &m_Fonts[i];
}
}
return nullptr;
}
BOOL CFontCache::IsMarkDeleted(size_t Index) const
{
if ((INT)Index >= m_Fonts.GetSize())
return FALSE;
return m_Fonts[Index].IsMarkDeleted();
}
// The item must exist until its visibility is removed, because the change
// notification UI must work for existing items.
void CFontCache::MarkDeleted(const FontPidlEntry* fontEntry)
{
for (INT i = 0; i < m_Fonts.GetSize(); ++i)
{
if (m_Fonts[i].Name().CompareNoCase(fontEntry->Name()) == 0)
{
m_Fonts[i].MarkDeleted();
break;
}
}
}
CStringW CFontCache::Filename(CFontInfo* info, bool alwaysFullPath)
{
@@ -175,8 +210,14 @@ CStringW CFontCache::Filename(CFontInfo* info, bool alwaysFullPath)
return File;
}
void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName)
void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName, PCWSTR Value)
{
CFontInfo newInfo(KeyName, Value);
CStringW strFontFile = g_FontCache->GetFontFilePath(newInfo.File());
if (!PathFileExistsW(strFontFile))
return;
POSITION it = fonts.GetHeadPosition();
while (it != NULL)
{
@@ -184,54 +225,72 @@ void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName)
const CFontInfo& info = fonts.GetNext(it);
if (info.Name().CompareNoCase(KeyName) >= 0)
{
fonts.InsertBefore(lastit, CFontInfo(KeyName));
fonts.InsertBefore(lastit, newInfo);
return;
}
}
fonts.AddTail(CFontInfo(KeyName));
fonts.AddTail(newInfo);
}
CStringW CFontCache::GetFontFilePath(const PCWSTR Path) const
{
if (PathIsRelativeW(Path))
return m_FontFolderPath + Path;
return Path;
}
void CFontCache::Read()
{
CAtlList<CFontInfo> fonts;
CRegKey key;
LSTATUS error = key.Open(FONT_HIVE, FONT_KEY, KEY_READ);
if (error != ERROR_SUCCESS)
{
ERR("Can't open registry: %ld\n", error);
return;
}
CComHeapPtr<WCHAR> Name, Value;
DWORD cchName = MAX_PATH, cchValue = MAX_PATH;
if (!Name.Allocate(cchName) || !Value.Allocate(cchValue))
{
ERR("Out of memory\n");
return;
}
// Enumerate all registered font names
if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
CAtlList<CFontInfo> fonts;
for (DWORD iItem = 0;; ++iItem)
{
LSTATUS Status;
DWORD dwAllocated = 128;
DWORD ilIndex = 0;
CStringW KeyName;
do
DWORD cchName2 = cchName, cbValue = cchValue * sizeof(WCHAR);
error = RegEnumValueW(key, iItem, Name, &cchName2, NULL, NULL,
(PBYTE)(PWSTR)Value, &cbValue);
if (error == ERROR_MORE_DATA)
{
DWORD dwSize = dwAllocated;
PWSTR Buffer = KeyName.GetBuffer(dwSize);
Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL);
KeyName.ReleaseBuffer(dwSize);
if (Status == ERROR_SUCCESS)
cchName += 128;
cchValue += 128;
if (!Name.Reallocate(cchName) || !Value.Reallocate(cchValue))
{
// Insert will create an ordered list
Insert(fonts, KeyName);
ilIndex++;
continue;
}
if (Status == ERROR_NO_MORE_ITEMS)
ERR("Out of memory\n");
break;
else if (Status == ERROR_MORE_DATA)
{
dwAllocated += 128;
}
} while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS);
--iItem;
continue;
}
if (error != ERROR_SUCCESS)
break;
if (Name[0] && Value[0])
{
// Insert will create an ordered list (sorted)
Insert(fonts, (PCWSTR)Name, (PCWSTR)Value);
}
}
// Move the fonts from a list to an array (for easy indexing)
m_Fonts.SetCount(fonts.GetCount());
size_t Index = 0;
m_Fonts.RemoveAll();
POSITION it = fonts.GetHeadPosition();
while (it != NULL)
{
m_Fonts[Index] = fonts.GetNext(it);
Index++;
}
m_Fonts.Add(fonts.GetNext(it));
}

View File

@@ -12,8 +12,8 @@ class CFontInfo
private:
CStringW m_Name;
CStringW m_File;
BOOL m_bMarkDeleted = FALSE;
bool m_FileRead;
bool m_AttrsRead;
LARGE_INTEGER m_FileSize;
FILETIME m_FileWriteTime;
@@ -22,11 +22,14 @@ private:
void ReadAttrs();
public:
CFontInfo(LPCWSTR name = L"");
CFontInfo(PCWSTR name = L"", PCWSTR value = L"");
const CStringW& Name() const; // Font display name stored in the registry
const bool Valid() const;
BOOL IsMarkDeleted() const { return m_bMarkDeleted; }
void MarkDeleted() { m_bMarkDeleted = TRUE; }
const CStringW& File(); // Full path or file, depending on how it's stored in the registry
const LARGE_INTEGER& FileSize();
const FILETIME& FileWriteTime();
@@ -36,24 +39,28 @@ public:
class CFontCache
{
private:
CAtlArray<CFontInfo> m_Fonts;
CSimpleArray<CFontInfo> m_Fonts;
CStringW m_FontFolderPath;
protected:
CFontCache();
void Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName);
void Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName, PCWSTR Value);
public:
void Read();
void SetFontDir(const LPCWSTR Path);
const CStringW& FontPath() const { return m_FontFolderPath; }
CStringW GetFontFilePath(const PCWSTR Path) const;
size_t Size();
CStringW Name(size_t Index); // Font display name stored in the registry
CStringW File(size_t Index);
CFontInfo* Find(const FontPidlEntry* fontEntry);
BOOL IsMarkDeleted(size_t Index) const;
void MarkDeleted(const FontPidlEntry* fontEntry);
CStringW Filename(CFontInfo* info, bool alwaysFullPath = false);
friend class CFontExtModule;

View File

@@ -77,6 +77,37 @@ WCHAR* g2s(REFCLSID iid)
return buf[idx];
}
static HRESULT FONTEXT_GetAttributeString(DWORD dwAttributes, LPWSTR pszOut, UINT cchMax)
{
CStringW AttrLetters;
AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
if (AttrLetters.GetLength() != 5)
{
ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n");
return E_FAIL;
}
UINT ich = 0;
if ((dwAttributes & FILE_ATTRIBUTE_READONLY) && ich < cchMax)
pszOut[ich++] = AttrLetters[0];
if ((dwAttributes & FILE_ATTRIBUTE_HIDDEN) && ich < cchMax)
pszOut[ich++] = AttrLetters[1];
if ((dwAttributes & FILE_ATTRIBUTE_SYSTEM) && ich < cchMax)
pszOut[ich++] = AttrLetters[2];
if ((dwAttributes & FILE_ATTRIBUTE_ARCHIVE) && ich < cchMax)
pszOut[ich++] = AttrLetters[3];
if ((dwAttributes & FILE_ATTRIBUTE_COMPRESSED) && ich < cchMax)
pszOut[ich++] = AttrLetters[4];
if (ich < cchMax)
{
pszOut[ich] = UNICODE_NULL;
return S_OK;
}
ERR("Buffer too short: %u\n", cchMax);
return E_FAIL;
}
CFontExt::CFontExt()
{
InterlockedIncrement(&g_ModuleRefCnt);
@@ -92,20 +123,18 @@ void CFontExt::SetViewWindow(HWND hwndView)
m_hwndView = hwndView;
}
HRESULT CALLBACK
CFontExt::MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
HRESULT CFontExt::DeleteItems(IDataObject* pDataObj)
{
TRACE("%u, %p, %p\n", uMsg, wParam, lParam);
switch (uMsg)
{
case DFM_MERGECONTEXTMENU:
return S_OK; // Yes, I want verbs
case DFM_INVOKECOMMAND:
return S_FALSE; // Do it for me please
case DFM_GETDEFSTATICID:
return S_FALSE; // Supposedly "required for Windows 7 to pick a default"
}
return E_NOTIMPL;
HRESULT hr = DoDeleteFontFiles(m_hwndView, pDataObj);
FAILED_UNEXPECTEDLY(hr);
return S_OK; // Override default action
}
HRESULT CFontExt::PreviewItems(IDataObject* pDataObj)
{
HRESULT hr = DoPreviewFontFiles(m_hwndView, pDataObj);
FAILED_UNEXPECTEDLY(hr);
return S_OK; // Override default action
}
// *** IShellFolder2 methods ***
@@ -179,13 +208,11 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
auto info = g_FontCache->Find(fontEntry);
if (info == nullptr)
{
ERR("Unable to query info about %S\n", fontEntry->Name);
ERR("Unable to query info about %S\n", fontEntry->Name());
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
int ret;
CStringA AttrLetters;
DWORD dwAttributes;
SYSTEMTIME time;
switch (iColumn)
{
@@ -208,27 +235,13 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret);
return S_OK;
case FONTEXT_COL_ATTR:
AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
if (AttrLetters.GetLength() != 5)
{
ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n");
return E_FAIL;
WCHAR szAttr[8];
HRESULT hr = FONTEXT_GetAttributeString(info->FileAttributes(), szAttr, _countof(szAttr));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
return SHSetStrRet(&psd->str, szAttr);
}
psd->str.uType = STRRET_CSTR;
dwAttributes = info->FileAttributes();
ret = 0;
if (dwAttributes & FILE_ATTRIBUTE_READONLY)
psd->str.cStr[ret++] = AttrLetters[0];
if (dwAttributes & FILE_ATTRIBUTE_HIDDEN)
psd->str.cStr[ret++] = AttrLetters[1];
if (dwAttributes & FILE_ATTRIBUTE_SYSTEM)
psd->str.cStr[ret++] = AttrLetters[2];
if (dwAttributes & FILE_ATTRIBUTE_ARCHIVE)
psd->str.cStr[ret++] = AttrLetters[3];
if (dwAttributes & FILE_ATTRIBUTE_COMPRESSED)
psd->str.cStr[ret++] = AttrLetters[4];
psd->str.cStr[ret] = '\0';
return S_OK;
default:
break;
}
@@ -246,8 +259,53 @@ STDMETHODIMP CFontExt::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
// *** IShellFolder2 methods ***
STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
{
ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
return E_NOTIMPL;
if (!lpszDisplayName || !lpszDisplayName[0] || !ppidl)
return E_INVALIDARG;
*ppidl = NULL;
if (pchEaten)
*pchEaten = 0;
if (lpszDisplayName[0] == L':' && lpszDisplayName[1] == L':')
return E_INVALIDARG;
if (PathIsRelativeW(lpszDisplayName)) // Not full path?
return E_INVALIDARG;
// Load font cache
if (g_FontCache->Size() == 0)
g_FontCache->Read();
for (SIZE_T iFont = 0; iFont < g_FontCache->Size(); ++iFont)
{
CStringW fileName = g_FontCache->File(iFont);
if (fileName.IsEmpty())
continue;
CStringW filePath = g_FontCache->GetFontFilePath(fileName);
if (filePath.CompareNoCase(lpszDisplayName) != 0)
continue;
CStringW fontName = g_FontCache->Name(iFont);
if (fontName.IsEmpty())
continue;
// Create a PIDL
*ppidl = _ILCreate(fontName, fileName);
if (!*ppidl)
return E_OUTOFMEMORY;
if (pchEaten)
*pchEaten = wcslen(lpszDisplayName);
if (pdwAttributes && *pdwAttributes)
*pdwAttributes &= (SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_CANCOPY |
SFGAO_FILESYSTEM);
return S_OK;
}
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // Not found
}
STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
@@ -271,7 +329,6 @@ STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUID
{
const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1);
const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2);
if (!fontEntry1 || !fontEntry2)
return E_INVALIDARG;
@@ -280,7 +337,7 @@ STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUID
DWORD column = lParam & 0x0000FFFF;
if (sortMode == SHCIDS_ALLFIELDS)
{
result = StrCmpIW(fontEntry1->Name, fontEntry2->Name);
result = StrCmpIW(fontEntry1->Name(), fontEntry2->Name());
}
else
{
@@ -289,7 +346,7 @@ STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUID
if (!info1 || !info2)
{
ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name, fontEntry2->Name);
ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name(), fontEntry2->Name());
return E_INVALIDARG;
}
@@ -298,22 +355,32 @@ STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUID
case 0xffff:
/* ROS bug? */
case FONTEXT_COL_NAME:
result = StrCmpIW(fontEntry1->Name, fontEntry2->Name);
result = StrCmpIW(fontEntry1->Name(), fontEntry2->Name());
break;
case FONTEXT_COL_FILENAME:
result = StrCmpIW(PathFindFileNameW(info1->File()), PathFindFileNameW(info2->File()));
break;
case FONTEXT_COL_SIZE:
result = (int)info1->FileSize().HighPart - info2->FileSize().HighPart;
if (result == 0)
result = (int)info1->FileSize().LowPart - info2->FileSize().LowPart;
{
ULONGLONG size1 = info1->FileSize().QuadPart, size2 = info2->FileSize().QuadPart;
result = (size1 < size2) ? -1 : ((size1 > size2) ? 1 : 0);
}
break;
case FONTEXT_COL_MODIFIED:
result = CompareFileTime(&info1->FileWriteTime(), &info2->FileWriteTime());
break;
case FONTEXT_COL_ATTR:
// FIXME: how to compare attributes?
result = (int)info1->FileAttributes() - info2->FileAttributes();
{
HRESULT hr;
WCHAR szAttr1[8], szAttr2[8];
hr = FONTEXT_GetAttributeString(info1->FileAttributes(), szAttr1, _countof(szAttr1));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = FONTEXT_GetAttributeString(info2->FileAttributes(), szAttr2, _countof(szAttr2));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
result = _wcsicmp(szAttr1, szAttr2);
}
break;
default:
ERR("Unimplemented column %u\n", column);
@@ -361,16 +428,11 @@ STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, D
if (!rgfInOut || !cidl || !apidl)
return E_INVALIDARG;
DWORD rgf = 0;
DWORD rgf = (SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM);
while (cidl > 0 && *apidl)
{
const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
if (fontEntry)
{
// We don't support delete yet
rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM);
}
else
if (!fontEntry)
{
rgf = 0;
break;
@@ -380,46 +442,81 @@ STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, D
cidl--;
}
*rgfInOut = rgf;
*rgfInOut &= rgf;
return S_OK;
}
HRESULT CFontExt::CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut)
HRESULT CALLBACK CFontExt::MenuCallback(
IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (cidl <= 0)
{
ERR("cidl: %u\n", cidl);
return E_NOTIMPL;
}
const FontPidlEntry* pEntry = _FontFromIL(apidl[0]);
if (!pEntry)
{
ERR("!pEntry\n");
CFontExt* pThis = static_cast<CFontExt*>(psf);
if (!pThis)
return E_FAIL;
}
auto info = g_FontCache->Find(pEntry);
if (!info)
switch (uMsg)
{
ERR("!info\n");
return E_FAIL;
case DFM_MERGECONTEXTMENU:
{
QCMINFO* pqcminfo = (QCMINFO*)lParam;
// Insert [Preview] menu item
CString strPreview(MAKEINTRESOURCEW(IDS_FONT_PREVIEW));
::InsertMenuW(pqcminfo->hmenu, 0, MF_BYPOSITION | MF_STRING,
pqcminfo->idCmdFirst++, strPreview); // Command 0
// Make it default
::SetMenuDefaultItem(pqcminfo->hmenu, pqcminfo->idCmdFirst - 1, FALSE);
return S_OK;
}
case DFM_GETVERBA:
case DFM_GETVERBW:
{
// Replace default "open" command action
UINT idCmd = LOWORD(wParam), cchMax = HIWORD(wParam);
if (idCmd == 0)
{
if (uMsg == DFM_GETVERBA)
lstrcpynA((PSTR)lParam, "open", cchMax);
else
lstrcpynW((PWSTR)lParam, L"open", cchMax);
return S_OK;
}
break;
}
case DFM_INVOKECOMMANDEX:
return E_NOTIMPL;
case DFM_INVOKECOMMAND:
{
if (wParam == 0)
return pThis->PreviewItems(pdtobj);
if (wParam == DFM_CMD_COPY)
return S_FALSE;
if (wParam == DFM_CMD_DELETE)
return pThis->DeleteItems(pdtobj);
if (wParam == DFM_CMD_PASTE)
return S_FALSE;
if (wParam == DFM_CMD_PROPERTIES)
return S_FALSE;
if (wParam == DFM_CMD_MOVE)
{
ERR("DFM_CMD_MOVE not supported\n");
return E_NOTIMPL;
}
ERR("wParam: %p\n", wParam);
return E_FAIL;
}
case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
return S_FALSE;
case DFM_WM_INITMENUPOPUP:
{
HMENU hMenu = (HMENU)wParam;
// Delete default [Open] menu item
::DeleteMenu(hMenu, FCIDM_SHVIEW_OPEN, MF_BYCOMMAND);
// Disable [Paste link] menu item
::EnableMenuItem(hMenu, FCIDM_SHVIEW_INSERTLINK, MF_BYCOMMAND | MF_GRAYED);
break;
}
}
LPCWSTR extension = PathFindExtensionW(info->File());
CRegKeyHandleArray keys;
WCHAR wszClass[MAX_PATH];
DWORD dwSize = sizeof(wszClass);
if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS ||
!*wszClass || AddClassKeyToArray(wszClass, keys, keys) != ERROR_SUCCESS)
{
AddClassKeyToArray(extension, keys, keys);
if (cidl == 1)
AddClassKeyToArray(L"Unknown", keys, keys);
}
return CDefFolderMenu_Create2(m_Folder, hwndOwner, cidl, apidl, this, MenuCallBack, keys, keys, (IContextMenu**)ppvOut);
return E_NOTIMPL;
}
STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
@@ -428,7 +525,11 @@ STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_
riid == IID_IContextMenu2 ||
riid == IID_IContextMenu3)
{
return CreateForegroundMenu(hwndOwner, cidl, apidl, ppvOut);
if (cidl <= 0)
return E_FAIL;
return CDefFolderMenu_Create2(NULL, hwndOwner, cidl, apidl, this, MenuCallback,
0, NULL, (IContextMenu**)ppvOut);
}
else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
{
@@ -438,10 +539,8 @@ STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_
if (fontEntry)
{
DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry));
// Just create a default icon extractor based on the filename
// We might want to create a preview with the font to get really fancy one day.
return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut);
CStringW strFileName = g_FontCache->GetFontFilePath(fontEntry->FileName());
return SHCreateFileExtractIconW(strFileName, dwAttributes, riid, ppvOut);
}
}
else
@@ -478,12 +577,12 @@ STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_
STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
{
if (!pidl)
return E_NOTIMPL;
if (!pidl || !strRet)
return E_INVALIDARG;
// Validate that this pidl is the last one
PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
if (curpidl->mkid.cb != 0)
PCUIDLIST_RELATIVE nextPidl = ILGetNext(pidl);
if (nextPidl && nextPidl->mkid.cb != 0)
{
ERR("ERROR, unhandled PIDL!\n");
return E_FAIL;
@@ -495,14 +594,17 @@ STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPS
if (dwFlags & SHGDN_FORPARSING)
{
CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry), true);
if (!File.IsEmpty())
CStringW fileName = fontEntry->FileName();
if (!(dwFlags & SHGDN_INFOLDER))
{
return SHSetStrRet(strRet, File);
CStringW fullPath = g_FontCache->GetFontFilePath(fileName);
if (!fullPath.IsEmpty())
return SHSetStrRet(strRet, fullPath);
}
return SHSetStrRet(strRet, fileName);
}
return SHSetStrRet(strRet, fontEntry->Name);
return SHSetStrRet(strRet, fontEntry->Name());
}
STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
@@ -546,7 +648,7 @@ STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl)
return E_FAIL;
}
m_Folder.Attach(ILClone(pidl));
m_Folder.Attach(ILCreateFromPathW(FontsDir));
StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
g_FontCache->SetFontDir(FontsDir);
@@ -609,6 +711,7 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
if (g_FontCache)
g_FontCache->Read();
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"fonts", SMTO_ABORTIFHUNG, 1000, NULL);
SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, NULL);
// Show successful message

View File

@@ -19,13 +19,17 @@ class CFontExt :
BOOL m_bDragAccepted = FALSE;
HWND m_hwndView = nullptr;
static HRESULT CALLBACK MenuCallback(
IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT PreviewItems(IDataObject* pDataObj);
HRESULT DeleteItems(IDataObject* pDataObj);
public:
CFontExt();
~CFontExt();
void SetViewWindow(HWND hwndView);
static HRESULT CALLBACK MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut);
// *** IShellFolder2 methods ***
STDMETHODIMP GetDefaultSearchGUID(GUID *lpguid) override;
@@ -93,6 +97,7 @@ public:
COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
//COM_INTERFACE_ENTRY_FUNC_BLIND(0, log_stuff)
END_COM_MAP()

View File

@@ -7,7 +7,7 @@
#include "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell_ad);
WINE_DEFAULT_DEBUG_CHANNEL(fontext);
void CFontFolderViewCB::Initialize(CFontExt* pFontExt, IShellView *psv, LPCITEMIDLIST pidlParent)
{
@@ -18,70 +18,29 @@ void CFontFolderViewCB::Initialize(CFontExt* pFontExt, IShellView *psv, LPCITEMI
m_pShellView = psv;
m_pidlParent.Attach(ILClone(pidlParent));
if (!m_pidlParent)
ERR("!m_pidlParent\n");
ERR("m_pidlParent was null\n");
}
HRESULT CFontFolderViewCB::TranslatePidl(LPITEMIDLIST* ppidlNew, LPCITEMIDLIST pidl)
BOOL CFontFolderViewCB::FilterEvent(PIDLIST_ABSOLUTE* apidls, LONG lEvent) const
{
ATLASSERT(ppidlNew);
lEvent &= ~SHCNE_INTERRUPT;
*ppidlNew = NULL;
WCHAR szFontFile[MAX_PATH];
if (!SHGetPathFromIDListW(pidl, szFontFile))
return E_FAIL;
CStringW strFontName;
HRESULT hr = DoGetFontTitle(szFontFile, strFontName);
if (FAILED_UNEXPECTEDLY(hr))
return E_FAIL;
LPITEMIDLIST pidlChild = _ILCreate(strFontName);
if (!pidlChild)
{
ERR("!pidlChild\n");
return E_OUTOFMEMORY;
}
*ppidlNew = ILCombine(m_pidlParent, pidlChild);
ILFree(pidlChild);
return *ppidlNew ? S_OK : E_OUTOFMEMORY;
}
void CFontFolderViewCB::TranslateTwoPIDLs(PIDLIST_ABSOLUTE* pidls)
{
ATLASSERT(pidls);
HRESULT hr;
if (pidls[0])
{
m_pidl0.Free();
hr = TranslatePidl(&m_pidl0, pidls[0]);
if (!FAILED_UNEXPECTEDLY(hr))
pidls[0] = m_pidl0;
}
if (pidls[1])
{
m_pidl1.Free();
hr = TranslatePidl(&m_pidl1, pidls[1]);
if (!FAILED_UNEXPECTEDLY(hr))
pidls[1] = m_pidl1;
}
}
BOOL CFontFolderViewCB::FilterEvent(LONG lEvent) const
{
switch (lEvent & ~SHCNE_INTERRUPT)
switch (lEvent)
{
case SHCNE_CREATE:
case SHCNE_DELETE:
case SHCNE_RENAMEITEM:
case SHCNE_UPDATEDIR:
return FALSE; // OK
// Refresh font cache and notify the system about the font change
if (g_FontCache)
g_FontCache->Read();
break;
case SHCNE_DELETE:
break;
default:
return TRUE; // We don't want this event
}
return FALSE;
}
STDMETHODIMP
@@ -91,19 +50,17 @@ CFontFolderViewCB::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
case SFVM_QUERYFSNOTIFY: // Registering change notification
{
if (!m_pShellView || !m_pFontExt)
return E_FAIL;
// Now, we can get the view window
ATLASSERT(m_pShellView);
ATLASSERT(m_pFontExt);
m_pShellView->GetWindow(&m_hwndView);
m_pFontExt->SetViewWindow(m_hwndView);
return S_OK;
}
case SFVM_FSNOTIFY: // Change notification
{
if (FilterEvent((LONG)lParam))
if (FilterEvent((PIDLIST_ABSOLUTE*)wParam, (LONG)lParam))
return S_FALSE; // Don't process
TranslateTwoPIDLs((PIDLIST_ABSOLUTE*)wParam);
return S_OK;
}
}

View File

@@ -11,23 +11,19 @@ class CFontFolderViewCB
: public CComObjectRootEx<CComMultiThreadModelNoCS>
, public IShellFolderViewCB
{
CFontExt* m_pFontExt = nullptr; // Not ref-counted!
IShellView* m_pShellView = nullptr; // Not ref-counted!
CFontExt* m_pFontExt = nullptr;
CComPtr<IShellView> m_pShellView;
HWND m_hwndView = nullptr;
CComHeapPtr<ITEMIDLIST> m_pidlParent;
CComHeapPtr<ITEMIDLIST> m_pidl0;
CComHeapPtr<ITEMIDLIST> m_pidl1;
HRESULT TranslatePidl(LPITEMIDLIST* ppidlNew, LPCITEMIDLIST pidl);
void TranslateTwoPIDLs(PIDLIST_ABSOLUTE* pidls);
BOOL FilterEvent(LONG lEvent) const;
BOOL FilterEvent(PIDLIST_ABSOLUTE* apidls, LONG lEvent) const;
public:
CFontFolderViewCB() { }
void Initialize(CFontExt* pFontExt, IShellView *psv, LPCITEMIDLIST pidlParent);
// IShellFolderViewCB
STDMETHOD(MessageSFVCB)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
DECLARE_NO_REGISTRY()
DECLARE_NOT_AGGREGATABLE(CFontFolderViewCB)

View File

@@ -111,14 +111,14 @@ LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys)
if (*cKeys >= 16)
return ERROR_MORE_DATA;
HKEY hkey;
LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
if (result == ERROR_SUCCESS)
CRegKey key;
LSTATUS error = key.Open(HKEY_CLASSES_ROOT, szClass, KEY_READ | KEY_QUERY_VALUE);
if (error == ERROR_SUCCESS)
{
array[*cKeys] = hkey;
array[*cKeys] = key.Detach();
*cKeys += 1;
}
return result;
return error;
}
HRESULT
@@ -436,6 +436,137 @@ HRESULT InstallFontsFromDataObject(HWND hwndView, IDataObject* pDataObj)
return FAILED_UNEXPECTEDLY(data.hrResult) ? E_FAIL : S_OK;
}
HRESULT DoPreviewFontFiles(HWND hwnd, IDataObject* pDataObj)
{
CDataObjectHIDA cida(pDataObj);
if (!cida || cida->cidl <= 0)
{
ERR("Invalid IDataObject\n");
return E_FAIL;
}
for (UINT iItem = 0; iItem < cida->cidl; ++iItem)
{
PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem);
const FontPidlEntry* fontEntry = _FontFromIL(pidlChild);
if (fontEntry)
RunFontViewer(hwnd, fontEntry);
}
return S_OK;
}
HRESULT DeleteFontFiles(HWND hwnd, IDataObject* pDataObj)
{
CDataObjectHIDA cida(pDataObj);
if (!cida || cida->cidl <= 0)
{
ERR("E_FAIL\n");
return E_FAIL;
}
PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida);
if (!pidlParent)
{
ERR("pidlParent is NULL\n");
return E_FAIL;
}
// Delete files
for (UINT iItem = 0; iItem < cida->cidl; ++iItem)
{
PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem);
const FontPidlEntry* pEntry = _FontFromIL(pidlChild);
if (!pEntry)
{
ERR("Invalid pEntry: %p\n", pEntry);
return E_FAIL;
}
CStringW szPath = g_FontCache->GetFontFilePath(pEntry->FileName());
// WINDOWS BUG: Removing once is not enough
for (INT iTry = 0; iTry < 3; ++iTry)
{
if (!RemoveFontResourceW(szPath) && !RemoveFontResourceExW(szPath, FR_PRIVATE, NULL))
break;
}
if (!DeleteFileW(szPath))
{
ERR("Unable to delete font file: %S: %ld\n", (PCWSTR)szPath, GetLastError());
return E_FAIL;
}
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl(ILCombine(pidlParent, pidlChild));
if (pidl)
SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, (LPCITEMIDLIST)pidl, NULL);
else
ERR("Out of memory\n");
}
// Delete registry values and mark the entry as deleted
CRegKey key;
if (key.Open(FONT_HIVE, FONT_KEY, KEY_WRITE) == ERROR_SUCCESS)
{
for (UINT iItem = 0; iItem < cida->cidl; ++iItem)
{
PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem);
const FontPidlEntry* pEntry = _FontFromIL(pidlChild);
if (pEntry)
{
CStringW strFontName = pEntry->Name();
key.DeleteValue(strFontName);
g_FontCache->MarkDeleted(pEntry);
}
}
key.Close();
}
return S_OK;
}
HRESULT DoDeleteFontFiles(HWND hwnd, IDataObject* pDataObj)
{
CStringW title(MAKEINTRESOURCEW(IDS_REACTOS_FONTS_FOLDER));
CStringW msg(MAKEINTRESOURCEW(IDS_CONFIRM_DELETE_FONT));
if (MessageBoxW(hwnd, msg, title, MB_YESNOCANCEL | MB_ICONWARNING) != IDYES)
return S_FALSE;
HRESULT hr = DeleteFontFiles(hwnd, pDataObj);
if (FAILED_UNEXPECTEDLY(hr))
{
msg.LoadString(IDS_CANTDELETEFONT);
MessageBoxW(hwnd, msg, title, MB_ICONERROR);
return hr;
}
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"fonts",
SMTO_ABORTIFHUNG, 1000, NULL);
SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, NULL);
return S_OK;
}
void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry)
{
CStringW Path = g_FontCache->GetFontFilePath(fontEntry->FileName());
if (Path.IsEmpty())
return;
// '/d' disables the install button
WCHAR FontPathArg[MAX_PATH + 3];
StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString());
PathQuoteSpacesW(FontPathArg + 3);
SHELLEXECUTEINFOW si = { sizeof(si) };
si.fMask = SEE_MASK_DOENVSUBST;
si.hwnd = hwnd;
si.lpFile = L"%SystemRoot%\\System32\\fontview.exe";
si.lpParameters = FontPathArg;
si.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&si);
}
EXTERN_C
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{

View File

@@ -3,33 +3,109 @@
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: pidl handling
* COPYRIGHT: Copyright 2019,2020 Mark Jansen <mark.jansen@reactos.org>
* Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#include "precomp.h"
LPITEMIDLIST _ILCreate(LPCWSTR lpString)
WINE_DEFAULT_DEBUG_CHANNEL(fontext);
#define FONTPIDL_MAGIC 0x7066 // 'fp'
#define ALIGN_DWORD(size) (((size) + 3) & ~3)
PITEMID_CHILD _ILCreate(LPCWSTR lpName, LPCWSTR lpFileName)
{
// Because the FontPidlEntry contains one WCHAR, we do not need to take the null terminator into account
size_t cbData = sizeof(FontPidlEntry) + wcslen(lpString) * sizeof(WCHAR);
ATLASSERT(lpName);
ATLASSERT(lpFileName);
if (!lpName[0] || !lpFileName[0])
{
ERR("Invalid parameters: lpName '%S', lpFileName '%S'\n", lpName, lpFileName);
return NULL;
}
// SECURITY: Check string length
HRESULT hr;
size_t cbName, cbFileName;
hr = StringCbLengthW(lpName, min(MAXWORD - 1, STRSAFE_MAX_CCH) * sizeof(WCHAR), &cbName);
if (FAILED_UNEXPECTEDLY(hr))
return NULL;
hr = StringCbLengthW(lpFileName, min(MAXWORD - 1, STRSAFE_MAX_CCH) * sizeof(WCHAR), &cbFileName);
if (FAILED_UNEXPECTEDLY(hr))
return NULL;
cbName += sizeof(UNICODE_NULL);
cbFileName += sizeof(UNICODE_NULL);
size_t ibName = ALIGN_DWORD(sizeof(FontPidlEntry));
size_t ibFileName = ALIGN_DWORD(ibName + cbName);
size_t cbData = ibFileName + cbFileName;
if (cbData > MAXWORD - sizeof(WORD))
{
ATLASSERT(FALSE);
return NULL;
}
FontPidlEntry* pidl = (FontPidlEntry*)CoTaskMemAlloc(cbData + sizeof(WORD));
if (!pidl)
{
ERR("Out of memory\n");
return NULL;
}
ZeroMemory(pidl, cbData + sizeof(WORD));
pidl->cb = (WORD)cbData;
pidl->Magic = 'fp';
wcscpy(pidl->Name, lpString);
// Should be zero already, but make sure it is
*(WORD*)((char*)pidl + cbData) = 0;
pidl->Magic = FONTPIDL_MAGIC;
pidl->ibName = (WORD)ibName;
pidl->ibFileName = (WORD)ibFileName;
return (LPITEMIDLIST)pidl;
// SECURITY: Copy strings
hr = StringCbCopyW(pidl->Name(), cbName, lpName);
if (FAILED_UNEXPECTEDLY(hr))
{
CoTaskMemFree(pidl);
return NULL;
}
hr = StringCbCopyW(pidl->FileName(), cbFileName, lpFileName);
if (FAILED_UNEXPECTEDLY(hr))
{
CoTaskMemFree(pidl);
return NULL;
}
*(PWORD)((PBYTE)pidl + cbData) = UNICODE_NULL;
ATLASSERT(_FontFromIL((PITEMID_CHILD)pidl));
return (PITEMID_CHILD)pidl;
}
const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl)
const FontPidlEntry* _FontFromIL(PCITEMID_CHILD pidl)
{
if (!pidl || pidl->mkid.cb < sizeof(FontPidlEntry))
return NULL;
const FontPidlEntry* fontEntry = (const FontPidlEntry*)pidl;
if (fontEntry->Magic == 'fp')
return fontEntry;
return NULL;
if (fontEntry->Magic != FONTPIDL_MAGIC)
return NULL;
// The function gets an arbitrary PIDL here. Security is important.
// SECURITY: Check ibName and ibFileName
if (fontEntry->ibName < sizeof(FontPidlEntry) || fontEntry->ibFileName < sizeof(FontPidlEntry) ||
fontEntry->ibName >= fontEntry->cb || fontEntry->ibFileName >= fontEntry->cb ||
fontEntry->ibName % sizeof(WCHAR) != 0 || fontEntry->ibFileName % sizeof(WCHAR) != 0)
{
ERR("Invalid fontEntry %p (ibName %d, ibFileName %d, cb %d)\n", fontEntry,
fontEntry->ibName, fontEntry->ibFileName, fontEntry->cb);
return NULL;
}
// SECURITY: Check null termination
size_t cbName, cbNameMax = fontEntry->cb - fontEntry->ibName;
if (FAILED_UNEXPECTEDLY(StringCbLengthW(fontEntry->Name(), cbNameMax, &cbName)))
return NULL;
size_t cbFileName, cbFileNameMax = fontEntry->cb - fontEntry->ibFileName;
if (FAILED_UNEXPECTEDLY(StringCbLengthW(fontEntry->FileName(), cbFileNameMax, &cbFileName)))
return NULL;
return fontEntry;
}

View File

@@ -3,6 +3,7 @@
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: pidl handling
* COPYRIGHT: Copyright 2019 Mark Jansen <mark.jansen@reactos.org>
* Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#pragma once
@@ -12,9 +13,14 @@ struct FontPidlEntry
{
WORD cb;
WORD Magic;
WCHAR Name[1];
WORD ibName;
WORD ibFileName;
LPWSTR Name() { return (LPWSTR)((PBYTE)this + ibName); }
LPWSTR FileName() { return (LPWSTR)((PBYTE)this + ibFileName); }
LPCWSTR Name() const { return (LPCWSTR)((PBYTE)this + ibName); }
LPCWSTR FileName() const { return (LPCWSTR)((PBYTE)this + ibFileName); }
};
#include <poppack.h>
LPITEMIDLIST _ILCreate(LPCWSTR lpString);
const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl);
PITEMID_CHILD _ILCreate(LPCWSTR lpName, LPCWSTR lpFileName);
const FontPidlEntry* _FontFromIL(PCITEMID_CHILD pidl);

View File

@@ -20,9 +20,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS Schriftartenordner"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -15,9 +15,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS Font Folder"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -20,9 +20,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Cartella dei font di ReactOS"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -15,9 +15,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Folder czcionek ReactOS"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -21,9 +21,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Folder-ul de fonturi de ReactOS"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -15,9 +15,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS Yazı Tipi Dizini"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -20,9 +20,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS 字体文件夹"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -20,9 +20,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -20,9 +20,11 @@ END
STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾"
IDS_INSTALL_OK "The font files have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font files."
IDS_INSTALL_OK "The font(s) have been installed successfully."
IDS_INSTALL_FAILED "Failed to install the font(s)."
IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?"
IDS_PROPERTIES "P&roperties"
IDS_CANTDELETEFONT "Failed to delete the font(s)."
END
STRINGTABLE

View File

@@ -20,6 +20,7 @@
#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsimpcoll.h>
#include <atlstr.h>
#include <wine/debug.h>
#include <shellutils.h>
@@ -82,6 +83,9 @@ HRESULT DoGetFontTitle(
BOOL CheckDropFontFiles(HDROP hDrop);
BOOL CheckDataObject(IDataObject *pDataObj);
HRESULT InstallFontsFromDataObject(HWND hwndView, IDataObject* pDataObj);
HRESULT DoPreviewFontFiles(HWND hwnd, IDataObject* pDataObj);
HRESULT DoDeleteFontFiles(HWND hwnd, IDataObject* pDataObj);
void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry);
HRESULT
APIENTRY

View File

@@ -4,7 +4,7 @@ HKCR
{
ForceRemove {BD84B380-8CA2-1069-AB1D-08000948F534} = s 'Fonts'
{
InprocServer32 = s 'fontext.dll'
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}

View File

@@ -7,7 +7,9 @@
#define IDS_REACTOS_FONTS_FOLDER 151
#define IDS_INSTALL_OK 152
#define IDS_INSTALL_FAILED 153
#define IDS_PROPERTIES 154
#define IDS_CONFIRM_DELETE_FONT 154
#define IDS_PROPERTIES 155
#define IDS_CANTDELETEFONT 156
#define IDS_COL_NAME 301
#define IDS_COL_FILENAME 304

View File

@@ -1159,6 +1159,10 @@ HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCo
if (!m_cidl || !m_pDataObj)
return E_FAIL;
HRESULT hr = _DoInvokeCommandCallback(lpcmi, bCopy ? DFM_CMD_COPY : DFM_CMD_MOVE);
if (hr == S_OK)
return hr;
FORMATETC formatetc;
InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
STGMEDIUM medium = {0};
@@ -1174,7 +1178,7 @@ HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCo
if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
psfv->SetPoints(m_pDataObj);
HRESULT hr = OleSetClipboard(m_pDataObj);
hr = OleSetClipboard(m_pDataObj);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
@@ -1775,6 +1779,15 @@ CDefaultContextMenu::GetCommandString(
UINT CmdId = LOWORD(idCommand);
if (uFlags == GCS_VERBA || uFlags == GCS_VERBW)
{
UINT uMsg = (uFlags == GCS_VERBA) ? DFM_GETVERBA : DFM_GETVERBW;
WPARAM wParam = MAKEWPARAM(idCommand, uMaxNameLen);
HRESULT hr = _DoCallback(uMsg, wParam, lpszName);
if (hr == S_OK)
return S_OK;
}
if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
{
idCommand -= m_iIdSHEFirst;

View File

@@ -417,6 +417,8 @@ PathMakeUniqueName(
_In_opt_ PCWSTR pszLongPlate,
_In_opt_ PCWSTR pszDir);
HRESULT WINAPI SHMultiFileProperties(_In_ IDataObject *pDataObject, _In_ DWORD dwFlags);
/*****************************************************************************
* IContextMenu interface
*/