Reactos

[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>.

authored by

Katayama Hirofumi MZ and committed by
GitHub
6af07a31 5213cf71

+666 -275
+1 -1
dll/shellext/fontext/CDataObject.cpp
··· 73 73 } 74 74 else 75 75 { 76 - ERR("No file found for %S\n", fontEntry->Name); 76 + ERR("No file found for %S\n", fontEntry->Name()); 77 77 } 78 78 } 79 79 }
+28 -15
dll/shellext/fontext/CEnumFonts.cpp
··· 42 42 43 43 while (celt) 44 44 { 45 - celt--; 45 + if (m_Index >= g_FontCache->Size()) 46 + { 47 + hr = S_FALSE; 48 + break; 49 + } 46 50 47 - if (m_Index < g_FontCache->Size()) 51 + if (!g_FontCache->IsMarkDeleted(m_Index)) 48 52 { 49 - CStringW Name = g_FontCache->Name(m_Index); 50 - LPITEMIDLIST item = _ILCreate(Name); 51 - if (!item) 53 + CStringW Name = g_FontCache->Name(m_Index), FileName = g_FontCache->File(m_Index); 54 + if (Name.IsEmpty() || FileName.IsEmpty()) 52 55 { 53 - hr = Fetched ? S_FALSE : E_OUTOFMEMORY; 54 - break; 56 + ERR("Why is Name or FileName empty?\n"); 57 + } 58 + else 59 + { 60 + // Create a PIDL 61 + PITEMID_CHILD item = _ILCreate(Name, FileName); 62 + if (!item) 63 + { 64 + hr = Fetched ? S_FALSE : E_OUTOFMEMORY; 65 + break; 66 + } 67 + rgelt[Fetched++] = item; 68 + --celt; 55 69 } 56 - rgelt[Fetched] = item; 57 - m_Index++; 58 - Fetched++; 59 70 } 60 - else 61 - { 62 - hr = S_FALSE; 63 - } 71 + m_Index++; 64 72 } 65 73 66 74 if (pceltFetched) ··· 70 78 71 79 STDMETHODIMP Skip(ULONG celt) override 72 80 { 73 - m_Index += celt; 81 + for (ULONG i = 0; i < celt && m_Index < g_FontCache->Size(); ++i) 82 + { 83 + while (m_Index < g_FontCache->Size() && g_FontCache->IsMarkDeleted(m_Index)) 84 + ++m_Index; 85 + ++m_Index; 86 + } 74 87 return S_OK; 75 88 } 76 89
+2 -3
dll/shellext/fontext/CFontBackgroundMenu.cpp
··· 67 67 if (FAILED_UNEXPECTEDLY(hr) || !CheckDataObject(pDataObj)) 68 68 { 69 69 // Show error message 70 - CStringW text, title; 71 - title.LoadStringW(IDS_REACTOS_FONTS_FOLDER); 72 - text.LoadStringW(IDS_INSTALL_FAILED); 70 + CStringW text(MAKEINTRESOURCEW(IDS_INSTALL_FAILED)); 71 + CStringW title(MAKEINTRESOURCEW(IDS_REACTOS_FONTS_FOLDER)); 73 72 MessageBoxW(m_hwnd, text, title, MB_ICONERROR); 74 73 return E_FAIL; 75 74 }
+101 -42
dll/shellext/fontext/CFontCache.cpp
··· 11 11 12 12 CFontCache* g_FontCache = NULL; 13 13 14 - CFontInfo::CFontInfo(LPCWSTR name) 14 + CFontInfo::CFontInfo(PCWSTR name, PCWSTR value) 15 15 : m_Name(name) 16 + , m_File(value) 16 17 , m_FileRead(false) 17 18 , m_AttrsRead(false) 18 19 , m_FileWriteTime({}) ··· 35 36 { 36 37 if (!m_FileRead) 37 38 { 38 - if (Valid()) 39 + if (Valid() && m_File.IsEmpty()) 39 40 { 40 41 // Read the filename stored in the registry. 41 42 // This can be either a filename or a full path ··· 43 44 if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) 44 45 { 45 46 CStringW Value; 46 - DWORD dwAllocated = 128; 47 + DWORD dwAllocated = MAX_PATH; 47 48 LSTATUS Status; 48 49 do 49 50 { ··· 125 126 126 127 size_t CFontCache::Size() 127 128 { 128 - if (m_Fonts.GetCount() == 0u) 129 + if (m_Fonts.GetSize() == 0) 129 130 Read(); 130 131 131 - return m_Fonts.GetCount(); 132 + return m_Fonts.GetSize(); 132 133 } 133 134 134 135 CStringW CFontCache::Name(size_t Index) 135 136 { 136 - if (m_Fonts.GetCount() == 0u) 137 + if (m_Fonts.GetSize() == 0) 137 138 Read(); 138 139 139 - if (Index >= m_Fonts.GetCount()) 140 - return CStringW(); 140 + if ((INT)Index >= m_Fonts.GetSize()) 141 + return L""; 141 142 142 143 return m_Fonts[Index].Name(); 143 144 } 144 145 146 + CStringW CFontCache::File(size_t Index) 147 + { 148 + if (m_Fonts.GetSize() == 0) 149 + Read(); 150 + 151 + if ((INT)Index >= m_Fonts.GetSize()) 152 + return L""; 153 + 154 + return m_Fonts[Index].File(); 155 + } 156 + 145 157 CFontInfo* CFontCache::Find(const FontPidlEntry* fontEntry) 146 158 { 147 - for (UINT n = 0; n < Size(); ++n) 159 + if (m_Fonts.GetSize() == 0) 160 + Read(); 161 + 162 + for (INT i = 0; i < m_Fonts.GetSize(); ++i) 148 163 { 149 - if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0) 164 + if (m_Fonts[i].Name().CompareNoCase(fontEntry->Name()) == 0) 150 165 { 151 - return &m_Fonts[n]; 166 + return &m_Fonts[i]; 152 167 } 153 168 } 154 169 return nullptr; 155 170 } 156 171 172 + BOOL CFontCache::IsMarkDeleted(size_t Index) const 173 + { 174 + if ((INT)Index >= m_Fonts.GetSize()) 175 + return FALSE; 176 + return m_Fonts[Index].IsMarkDeleted(); 177 + } 178 + 179 + // The item must exist until its visibility is removed, because the change 180 + // notification UI must work for existing items. 181 + void CFontCache::MarkDeleted(const FontPidlEntry* fontEntry) 182 + { 183 + for (INT i = 0; i < m_Fonts.GetSize(); ++i) 184 + { 185 + if (m_Fonts[i].Name().CompareNoCase(fontEntry->Name()) == 0) 186 + { 187 + m_Fonts[i].MarkDeleted(); 188 + break; 189 + } 190 + } 191 + } 157 192 158 193 CStringW CFontCache::Filename(CFontInfo* info, bool alwaysFullPath) 159 194 { ··· 175 210 return File; 176 211 } 177 212 178 - void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName) 213 + void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName, PCWSTR Value) 179 214 { 215 + CFontInfo newInfo(KeyName, Value); 216 + 217 + CStringW strFontFile = g_FontCache->GetFontFilePath(newInfo.File()); 218 + if (!PathFileExistsW(strFontFile)) 219 + return; 220 + 180 221 POSITION it = fonts.GetHeadPosition(); 181 222 while (it != NULL) 182 223 { ··· 184 225 const CFontInfo& info = fonts.GetNext(it); 185 226 if (info.Name().CompareNoCase(KeyName) >= 0) 186 227 { 187 - fonts.InsertBefore(lastit, CFontInfo(KeyName)); 228 + fonts.InsertBefore(lastit, newInfo); 188 229 return; 189 230 } 190 231 } 191 - fonts.AddTail(CFontInfo(KeyName)); 232 + 233 + fonts.AddTail(newInfo); 234 + } 235 + 236 + CStringW CFontCache::GetFontFilePath(const PCWSTR Path) const 237 + { 238 + if (PathIsRelativeW(Path)) 239 + return m_FontFolderPath + Path; 240 + return Path; 192 241 } 193 242 194 243 void CFontCache::Read() 195 244 { 196 - CAtlList<CFontInfo> fonts; 197 245 CRegKey key; 246 + LSTATUS error = key.Open(FONT_HIVE, FONT_KEY, KEY_READ); 247 + if (error != ERROR_SUCCESS) 248 + { 249 + ERR("Can't open registry: %ld\n", error); 250 + return; 251 + } 252 + 253 + CComHeapPtr<WCHAR> Name, Value; 254 + DWORD cchName = MAX_PATH, cchValue = MAX_PATH; 255 + if (!Name.Allocate(cchName) || !Value.Allocate(cchValue)) 256 + { 257 + ERR("Out of memory\n"); 258 + return; 259 + } 198 260 199 261 // Enumerate all registered font names 200 - if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) 262 + CAtlList<CFontInfo> fonts; 263 + for (DWORD iItem = 0;; ++iItem) 201 264 { 202 - LSTATUS Status; 203 - DWORD dwAllocated = 128; 204 - DWORD ilIndex = 0; 205 - CStringW KeyName; 206 - do 265 + DWORD cchName2 = cchName, cbValue = cchValue * sizeof(WCHAR); 266 + error = RegEnumValueW(key, iItem, Name, &cchName2, NULL, NULL, 267 + (PBYTE)(PWSTR)Value, &cbValue); 268 + if (error == ERROR_MORE_DATA) 207 269 { 208 - DWORD dwSize = dwAllocated; 209 - PWSTR Buffer = KeyName.GetBuffer(dwSize); 210 - Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL); 211 - KeyName.ReleaseBuffer(dwSize); 212 - if (Status == ERROR_SUCCESS) 270 + cchName += 128; 271 + cchValue += 128; 272 + if (!Name.Reallocate(cchName) || !Value.Reallocate(cchValue)) 213 273 { 214 - // Insert will create an ordered list 215 - Insert(fonts, KeyName); 216 - ilIndex++; 217 - continue; 218 - } 219 - if (Status == ERROR_NO_MORE_ITEMS) 274 + ERR("Out of memory\n"); 220 275 break; 221 - else if (Status == ERROR_MORE_DATA) 222 - { 223 - dwAllocated += 128; 224 276 } 225 - } while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS); 277 + --iItem; 278 + continue; 279 + } 280 + 281 + if (error != ERROR_SUCCESS) 282 + break; 283 + 284 + if (Name[0] && Value[0]) 285 + { 286 + // Insert will create an ordered list (sorted) 287 + Insert(fonts, (PCWSTR)Name, (PCWSTR)Value); 288 + } 226 289 } 227 290 228 291 // Move the fonts from a list to an array (for easy indexing) 229 - m_Fonts.SetCount(fonts.GetCount()); 230 - size_t Index = 0; 292 + m_Fonts.RemoveAll(); 231 293 POSITION it = fonts.GetHeadPosition(); 232 294 while (it != NULL) 233 - { 234 - m_Fonts[Index] = fonts.GetNext(it); 235 - Index++; 236 - } 295 + m_Fonts.Add(fonts.GetNext(it)); 237 296 }
+11 -4
dll/shellext/fontext/CFontCache.hpp
··· 12 12 private: 13 13 CStringW m_Name; 14 14 CStringW m_File; 15 + BOOL m_bMarkDeleted = FALSE; 15 16 bool m_FileRead; 16 - 17 17 bool m_AttrsRead; 18 18 LARGE_INTEGER m_FileSize; 19 19 FILETIME m_FileWriteTime; ··· 22 22 void ReadAttrs(); 23 23 24 24 public: 25 - CFontInfo(LPCWSTR name = L""); 25 + CFontInfo(PCWSTR name = L"", PCWSTR value = L""); 26 26 27 27 const CStringW& Name() const; // Font display name stored in the registry 28 28 const bool Valid() const; 29 29 30 + BOOL IsMarkDeleted() const { return m_bMarkDeleted; } 31 + void MarkDeleted() { m_bMarkDeleted = TRUE; } 32 + 30 33 const CStringW& File(); // Full path or file, depending on how it's stored in the registry 31 34 const LARGE_INTEGER& FileSize(); 32 35 const FILETIME& FileWriteTime(); ··· 36 39 class CFontCache 37 40 { 38 41 private: 39 - CAtlArray<CFontInfo> m_Fonts; 42 + CSimpleArray<CFontInfo> m_Fonts; 40 43 CStringW m_FontFolderPath; 41 44 42 45 protected: 43 46 CFontCache(); 44 47 45 - void Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName); 48 + void Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName, PCWSTR Value); 46 49 47 50 public: 48 51 void Read(); 49 52 50 53 void SetFontDir(const LPCWSTR Path); 51 54 const CStringW& FontPath() const { return m_FontFolderPath; } 55 + CStringW GetFontFilePath(const PCWSTR Path) const; 52 56 53 57 size_t Size(); 54 58 CStringW Name(size_t Index); // Font display name stored in the registry 59 + CStringW File(size_t Index); 55 60 56 61 CFontInfo* Find(const FontPidlEntry* fontEntry); 62 + BOOL IsMarkDeleted(size_t Index) const; 63 + void MarkDeleted(const FontPidlEntry* fontEntry); 57 64 CStringW Filename(CFontInfo* info, bool alwaysFullPath = false); 58 65 59 66 friend class CFontExtModule;
+202 -99
dll/shellext/fontext/CFontExt.cpp
··· 77 77 return buf[idx]; 78 78 } 79 79 80 + static HRESULT FONTEXT_GetAttributeString(DWORD dwAttributes, LPWSTR pszOut, UINT cchMax) 81 + { 82 + CStringW AttrLetters; 83 + AttrLetters.LoadString(IDS_COL_ATTR_LETTERS); 84 + 85 + if (AttrLetters.GetLength() != 5) 86 + { 87 + ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n"); 88 + return E_FAIL; 89 + } 90 + 91 + UINT ich = 0; 92 + if ((dwAttributes & FILE_ATTRIBUTE_READONLY) && ich < cchMax) 93 + pszOut[ich++] = AttrLetters[0]; 94 + if ((dwAttributes & FILE_ATTRIBUTE_HIDDEN) && ich < cchMax) 95 + pszOut[ich++] = AttrLetters[1]; 96 + if ((dwAttributes & FILE_ATTRIBUTE_SYSTEM) && ich < cchMax) 97 + pszOut[ich++] = AttrLetters[2]; 98 + if ((dwAttributes & FILE_ATTRIBUTE_ARCHIVE) && ich < cchMax) 99 + pszOut[ich++] = AttrLetters[3]; 100 + if ((dwAttributes & FILE_ATTRIBUTE_COMPRESSED) && ich < cchMax) 101 + pszOut[ich++] = AttrLetters[4]; 102 + if (ich < cchMax) 103 + { 104 + pszOut[ich] = UNICODE_NULL; 105 + return S_OK; 106 + } 107 + ERR("Buffer too short: %u\n", cchMax); 108 + return E_FAIL; 109 + } 110 + 80 111 CFontExt::CFontExt() 81 112 { 82 113 InterlockedIncrement(&g_ModuleRefCnt); ··· 92 123 m_hwndView = hwndView; 93 124 } 94 125 95 - HRESULT CALLBACK 96 - CFontExt::MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) 126 + HRESULT CFontExt::DeleteItems(IDataObject* pDataObj) 97 127 { 98 - TRACE("%u, %p, %p\n", uMsg, wParam, lParam); 99 - switch (uMsg) 100 - { 101 - case DFM_MERGECONTEXTMENU: 102 - return S_OK; // Yes, I want verbs 103 - case DFM_INVOKECOMMAND: 104 - return S_FALSE; // Do it for me please 105 - case DFM_GETDEFSTATICID: 106 - return S_FALSE; // Supposedly "required for Windows 7 to pick a default" 107 - } 108 - return E_NOTIMPL; 128 + HRESULT hr = DoDeleteFontFiles(m_hwndView, pDataObj); 129 + FAILED_UNEXPECTEDLY(hr); 130 + return S_OK; // Override default action 131 + } 132 + 133 + HRESULT CFontExt::PreviewItems(IDataObject* pDataObj) 134 + { 135 + HRESULT hr = DoPreviewFontFiles(m_hwndView, pDataObj); 136 + FAILED_UNEXPECTEDLY(hr); 137 + return S_OK; // Override default action 109 138 } 110 139 111 140 // *** IShellFolder2 methods *** ··· 179 208 auto info = g_FontCache->Find(fontEntry); 180 209 if (info == nullptr) 181 210 { 182 - ERR("Unable to query info about %S\n", fontEntry->Name); 211 + ERR("Unable to query info about %S\n", fontEntry->Name()); 183 212 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 184 213 } 185 214 186 215 int ret; 187 - CStringA AttrLetters; 188 - DWORD dwAttributes; 189 216 SYSTEMTIME time; 190 217 switch (iColumn) 191 218 { ··· 208 235 GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret); 209 236 return S_OK; 210 237 case FONTEXT_COL_ATTR: 211 - AttrLetters.LoadString(IDS_COL_ATTR_LETTERS); 212 - if (AttrLetters.GetLength() != 5) 213 238 { 214 - ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n"); 215 - return E_FAIL; 239 + WCHAR szAttr[8]; 240 + HRESULT hr = FONTEXT_GetAttributeString(info->FileAttributes(), szAttr, _countof(szAttr)); 241 + if (FAILED_UNEXPECTEDLY(hr)) 242 + return hr; 243 + return SHSetStrRet(&psd->str, szAttr); 216 244 } 217 - psd->str.uType = STRRET_CSTR; 218 - dwAttributes = info->FileAttributes(); 219 - ret = 0; 220 - if (dwAttributes & FILE_ATTRIBUTE_READONLY) 221 - psd->str.cStr[ret++] = AttrLetters[0]; 222 - if (dwAttributes & FILE_ATTRIBUTE_HIDDEN) 223 - psd->str.cStr[ret++] = AttrLetters[1]; 224 - if (dwAttributes & FILE_ATTRIBUTE_SYSTEM) 225 - psd->str.cStr[ret++] = AttrLetters[2]; 226 - if (dwAttributes & FILE_ATTRIBUTE_ARCHIVE) 227 - psd->str.cStr[ret++] = AttrLetters[3]; 228 - if (dwAttributes & FILE_ATTRIBUTE_COMPRESSED) 229 - psd->str.cStr[ret++] = AttrLetters[4]; 230 - psd->str.cStr[ret] = '\0'; 231 - return S_OK; 232 245 default: 233 246 break; 234 247 } ··· 246 259 // *** IShellFolder2 methods *** 247 260 STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) 248 261 { 249 - ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 250 - return E_NOTIMPL; 262 + if (!lpszDisplayName || !lpszDisplayName[0] || !ppidl) 263 + return E_INVALIDARG; 264 + 265 + *ppidl = NULL; 266 + if (pchEaten) 267 + *pchEaten = 0; 268 + 269 + if (lpszDisplayName[0] == L':' && lpszDisplayName[1] == L':') 270 + return E_INVALIDARG; 271 + 272 + if (PathIsRelativeW(lpszDisplayName)) // Not full path? 273 + return E_INVALIDARG; 274 + 275 + // Load font cache 276 + if (g_FontCache->Size() == 0) 277 + g_FontCache->Read(); 278 + 279 + for (SIZE_T iFont = 0; iFont < g_FontCache->Size(); ++iFont) 280 + { 281 + CStringW fileName = g_FontCache->File(iFont); 282 + if (fileName.IsEmpty()) 283 + continue; 284 + 285 + CStringW filePath = g_FontCache->GetFontFilePath(fileName); 286 + if (filePath.CompareNoCase(lpszDisplayName) != 0) 287 + continue; 288 + 289 + CStringW fontName = g_FontCache->Name(iFont); 290 + if (fontName.IsEmpty()) 291 + continue; 292 + 293 + // Create a PIDL 294 + *ppidl = _ILCreate(fontName, fileName); 295 + if (!*ppidl) 296 + return E_OUTOFMEMORY; 297 + 298 + if (pchEaten) 299 + *pchEaten = wcslen(lpszDisplayName); 300 + 301 + if (pdwAttributes && *pdwAttributes) 302 + *pdwAttributes &= (SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_CANCOPY | 303 + SFGAO_FILESYSTEM); 304 + 305 + return S_OK; 306 + } 307 + 308 + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // Not found 251 309 } 252 310 253 311 STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) ··· 271 329 { 272 330 const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1); 273 331 const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2); 274 - 275 332 if (!fontEntry1 || !fontEntry2) 276 333 return E_INVALIDARG; 277 334 ··· 280 337 DWORD column = lParam & 0x0000FFFF; 281 338 if (sortMode == SHCIDS_ALLFIELDS) 282 339 { 283 - result = StrCmpIW(fontEntry1->Name, fontEntry2->Name); 340 + result = StrCmpIW(fontEntry1->Name(), fontEntry2->Name()); 284 341 } 285 342 else 286 343 { ··· 289 346 290 347 if (!info1 || !info2) 291 348 { 292 - ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name, fontEntry2->Name); 349 + ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name(), fontEntry2->Name()); 293 350 return E_INVALIDARG; 294 351 } 295 352 ··· 298 355 case 0xffff: 299 356 /* ROS bug? */ 300 357 case FONTEXT_COL_NAME: 301 - result = StrCmpIW(fontEntry1->Name, fontEntry2->Name); 358 + result = StrCmpIW(fontEntry1->Name(), fontEntry2->Name()); 302 359 break; 303 360 case FONTEXT_COL_FILENAME: 304 361 result = StrCmpIW(PathFindFileNameW(info1->File()), PathFindFileNameW(info2->File())); 305 362 break; 306 363 case FONTEXT_COL_SIZE: 307 - result = (int)info1->FileSize().HighPart - info2->FileSize().HighPart; 308 - if (result == 0) 309 - result = (int)info1->FileSize().LowPart - info2->FileSize().LowPart; 364 + { 365 + ULONGLONG size1 = info1->FileSize().QuadPart, size2 = info2->FileSize().QuadPart; 366 + result = (size1 < size2) ? -1 : ((size1 > size2) ? 1 : 0); 367 + } 310 368 break; 311 369 case FONTEXT_COL_MODIFIED: 312 370 result = CompareFileTime(&info1->FileWriteTime(), &info2->FileWriteTime()); 313 371 break; 314 372 case FONTEXT_COL_ATTR: 315 - // FIXME: how to compare attributes? 316 - result = (int)info1->FileAttributes() - info2->FileAttributes(); 373 + { 374 + HRESULT hr; 375 + WCHAR szAttr1[8], szAttr2[8]; 376 + hr = FONTEXT_GetAttributeString(info1->FileAttributes(), szAttr1, _countof(szAttr1)); 377 + if (FAILED_UNEXPECTEDLY(hr)) 378 + return hr; 379 + hr = FONTEXT_GetAttributeString(info2->FileAttributes(), szAttr2, _countof(szAttr2)); 380 + if (FAILED_UNEXPECTEDLY(hr)) 381 + return hr; 382 + result = _wcsicmp(szAttr1, szAttr2); 383 + } 317 384 break; 318 385 default: 319 386 ERR("Unimplemented column %u\n", column); ··· 361 428 if (!rgfInOut || !cidl || !apidl) 362 429 return E_INVALIDARG; 363 430 364 - DWORD rgf = 0; 431 + DWORD rgf = (SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM); 365 432 while (cidl > 0 && *apidl) 366 433 { 367 434 const FontPidlEntry* fontEntry = _FontFromIL(*apidl); 368 - if (fontEntry) 369 - { 370 - // We don't support delete yet 371 - rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM); 372 - } 373 - else 435 + if (!fontEntry) 374 436 { 375 437 rgf = 0; 376 438 break; ··· 380 442 cidl--; 381 443 } 382 444 383 - *rgfInOut = rgf; 445 + *rgfInOut &= rgf; 384 446 return S_OK; 385 447 } 386 448 387 - HRESULT CFontExt::CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut) 449 + HRESULT CALLBACK CFontExt::MenuCallback( 450 + IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, 451 + UINT uMsg, WPARAM wParam, LPARAM lParam) 388 452 { 389 - if (cidl <= 0) 390 - { 391 - ERR("cidl: %u\n", cidl); 392 - return E_NOTIMPL; 393 - } 394 - 395 - const FontPidlEntry* pEntry = _FontFromIL(apidl[0]); 396 - if (!pEntry) 397 - { 398 - ERR("!pEntry\n"); 453 + CFontExt* pThis = static_cast<CFontExt*>(psf); 454 + if (!pThis) 399 455 return E_FAIL; 400 - } 401 - auto info = g_FontCache->Find(pEntry); 402 - if (!info) 403 - { 404 - ERR("!info\n"); 405 - return E_FAIL; 406 - } 407 - LPCWSTR extension = PathFindExtensionW(info->File()); 408 456 409 - CRegKeyHandleArray keys; 410 - 411 - WCHAR wszClass[MAX_PATH]; 412 - DWORD dwSize = sizeof(wszClass); 413 - if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS || 414 - !*wszClass || AddClassKeyToArray(wszClass, keys, keys) != ERROR_SUCCESS) 457 + switch (uMsg) 415 458 { 416 - AddClassKeyToArray(extension, keys, keys); 417 - 418 - if (cidl == 1) 419 - AddClassKeyToArray(L"Unknown", keys, keys); 459 + case DFM_MERGECONTEXTMENU: 460 + { 461 + QCMINFO* pqcminfo = (QCMINFO*)lParam; 462 + // Insert [Preview] menu item 463 + CString strPreview(MAKEINTRESOURCEW(IDS_FONT_PREVIEW)); 464 + ::InsertMenuW(pqcminfo->hmenu, 0, MF_BYPOSITION | MF_STRING, 465 + pqcminfo->idCmdFirst++, strPreview); // Command 0 466 + // Make it default 467 + ::SetMenuDefaultItem(pqcminfo->hmenu, pqcminfo->idCmdFirst - 1, FALSE); 468 + return S_OK; 469 + } 470 + case DFM_GETVERBA: 471 + case DFM_GETVERBW: 472 + { 473 + // Replace default "open" command action 474 + UINT idCmd = LOWORD(wParam), cchMax = HIWORD(wParam); 475 + if (idCmd == 0) 476 + { 477 + if (uMsg == DFM_GETVERBA) 478 + lstrcpynA((PSTR)lParam, "open", cchMax); 479 + else 480 + lstrcpynW((PWSTR)lParam, L"open", cchMax); 481 + return S_OK; 482 + } 483 + break; 484 + } 485 + case DFM_INVOKECOMMANDEX: 486 + return E_NOTIMPL; 487 + case DFM_INVOKECOMMAND: 488 + { 489 + if (wParam == 0) 490 + return pThis->PreviewItems(pdtobj); 491 + if (wParam == DFM_CMD_COPY) 492 + return S_FALSE; 493 + if (wParam == DFM_CMD_DELETE) 494 + return pThis->DeleteItems(pdtobj); 495 + if (wParam == DFM_CMD_PASTE) 496 + return S_FALSE; 497 + if (wParam == DFM_CMD_PROPERTIES) 498 + return S_FALSE; 499 + if (wParam == DFM_CMD_MOVE) 500 + { 501 + ERR("DFM_CMD_MOVE not supported\n"); 502 + return E_NOTIMPL; 503 + } 504 + ERR("wParam: %p\n", wParam); 505 + return E_FAIL; 506 + } 507 + case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default 508 + return S_FALSE; 509 + case DFM_WM_INITMENUPOPUP: 510 + { 511 + HMENU hMenu = (HMENU)wParam; 512 + // Delete default [Open] menu item 513 + ::DeleteMenu(hMenu, FCIDM_SHVIEW_OPEN, MF_BYCOMMAND); 514 + // Disable [Paste link] menu item 515 + ::EnableMenuItem(hMenu, FCIDM_SHVIEW_INSERTLINK, MF_BYCOMMAND | MF_GRAYED); 516 + break; 517 + } 420 518 } 421 - 422 - return CDefFolderMenu_Create2(m_Folder, hwndOwner, cidl, apidl, this, MenuCallBack, keys, keys, (IContextMenu**)ppvOut); 519 + return E_NOTIMPL; 423 520 } 424 521 425 522 STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) ··· 428 525 riid == IID_IContextMenu2 || 429 526 riid == IID_IContextMenu3) 430 527 { 431 - return CreateForegroundMenu(hwndOwner, cidl, apidl, ppvOut); 528 + if (cidl <= 0) 529 + return E_FAIL; 530 + 531 + return CDefFolderMenu_Create2(NULL, hwndOwner, cidl, apidl, this, MenuCallback, 532 + 0, NULL, (IContextMenu**)ppvOut); 432 533 } 433 534 else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW) 434 535 { ··· 438 539 if (fontEntry) 439 540 { 440 541 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL; 441 - CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry)); 442 - // Just create a default icon extractor based on the filename 443 - // We might want to create a preview with the font to get really fancy one day. 444 - return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut); 542 + CStringW strFileName = g_FontCache->GetFontFilePath(fontEntry->FileName()); 543 + return SHCreateFileExtractIconW(strFileName, dwAttributes, riid, ppvOut); 445 544 } 446 545 } 447 546 else ··· 478 577 479 578 STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) 480 579 { 481 - if (!pidl) 482 - return E_NOTIMPL; 580 + if (!pidl || !strRet) 581 + return E_INVALIDARG; 483 582 484 583 // Validate that this pidl is the last one 485 - PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl); 486 - if (curpidl->mkid.cb != 0) 584 + PCUIDLIST_RELATIVE nextPidl = ILGetNext(pidl); 585 + if (nextPidl && nextPidl->mkid.cb != 0) 487 586 { 488 587 ERR("ERROR, unhandled PIDL!\n"); 489 588 return E_FAIL; ··· 495 594 496 595 if (dwFlags & SHGDN_FORPARSING) 497 596 { 498 - CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry), true); 499 - if (!File.IsEmpty()) 597 + CStringW fileName = fontEntry->FileName(); 598 + if (!(dwFlags & SHGDN_INFOLDER)) 500 599 { 501 - return SHSetStrRet(strRet, File); 600 + CStringW fullPath = g_FontCache->GetFontFilePath(fileName); 601 + if (!fullPath.IsEmpty()) 602 + return SHSetStrRet(strRet, fullPath); 502 603 } 604 + return SHSetStrRet(strRet, fileName); 503 605 } 504 606 505 - return SHSetStrRet(strRet, fontEntry->Name); 607 + return SHSetStrRet(strRet, fontEntry->Name()); 506 608 } 507 609 508 610 STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) ··· 546 648 return E_FAIL; 547 649 } 548 650 549 - m_Folder.Attach(ILClone(pidl)); 651 + m_Folder.Attach(ILCreateFromPathW(FontsDir)); 550 652 StringCchCatW(FontsDir, _countof(FontsDir), L"\\"); 551 653 g_FontCache->SetFontDir(FontsDir); 552 654 ··· 609 711 if (g_FontCache) 610 712 g_FontCache->Read(); 611 713 714 + SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"fonts", SMTO_ABORTIFHUNG, 1000, NULL); 612 715 SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, NULL); 613 716 614 717 // Show successful message
+7 -2
dll/shellext/fontext/CFontExt.hpp
··· 19 19 BOOL m_bDragAccepted = FALSE; 20 20 HWND m_hwndView = nullptr; 21 21 22 + static HRESULT CALLBACK MenuCallback( 23 + IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, 24 + UINT uMsg, WPARAM wParam, LPARAM lParam); 25 + HRESULT PreviewItems(IDataObject* pDataObj); 26 + HRESULT DeleteItems(IDataObject* pDataObj); 27 + 22 28 public: 23 29 CFontExt(); 24 30 ~CFontExt(); 25 31 26 32 void SetViewWindow(HWND hwndView); 27 - static HRESULT CALLBACK MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); 28 - HRESULT CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut); 29 33 30 34 // *** IShellFolder2 methods *** 31 35 STDMETHODIMP GetDefaultSearchGUID(GUID *lpguid) override; ··· 93 97 COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder) 94 98 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) 95 99 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) 100 + COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) 96 101 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) 97 102 //COM_INTERFACE_ENTRY_FUNC_BLIND(0, log_stuff) 98 103 END_COM_MAP()
+16 -59
dll/shellext/fontext/CFontFolderViewCB.cpp
··· 7 7 8 8 #include "precomp.h" 9 9 10 - WINE_DEFAULT_DEBUG_CHANNEL(shell_ad); 10 + WINE_DEFAULT_DEBUG_CHANNEL(fontext); 11 11 12 12 void CFontFolderViewCB::Initialize(CFontExt* pFontExt, IShellView *psv, LPCITEMIDLIST pidlParent) 13 13 { ··· 18 18 m_pShellView = psv; 19 19 m_pidlParent.Attach(ILClone(pidlParent)); 20 20 if (!m_pidlParent) 21 - ERR("!m_pidlParent\n"); 21 + ERR("m_pidlParent was null\n"); 22 22 } 23 23 24 - HRESULT CFontFolderViewCB::TranslatePidl(LPITEMIDLIST* ppidlNew, LPCITEMIDLIST pidl) 24 + BOOL CFontFolderViewCB::FilterEvent(PIDLIST_ABSOLUTE* apidls, LONG lEvent) const 25 25 { 26 - ATLASSERT(ppidlNew); 27 - 28 - *ppidlNew = NULL; 29 - 30 - WCHAR szFontFile[MAX_PATH]; 31 - if (!SHGetPathFromIDListW(pidl, szFontFile)) 32 - return E_FAIL; 33 - 34 - CStringW strFontName; 35 - HRESULT hr = DoGetFontTitle(szFontFile, strFontName); 36 - if (FAILED_UNEXPECTEDLY(hr)) 37 - return E_FAIL; 26 + lEvent &= ~SHCNE_INTERRUPT; 38 27 39 - LPITEMIDLIST pidlChild = _ILCreate(strFontName); 40 - if (!pidlChild) 41 - { 42 - ERR("!pidlChild\n"); 43 - return E_OUTOFMEMORY; 44 - } 45 - 46 - *ppidlNew = ILCombine(m_pidlParent, pidlChild); 47 - ILFree(pidlChild); 48 - 49 - return *ppidlNew ? S_OK : E_OUTOFMEMORY; 50 - } 51 - 52 - void CFontFolderViewCB::TranslateTwoPIDLs(PIDLIST_ABSOLUTE* pidls) 53 - { 54 - ATLASSERT(pidls); 55 - 56 - HRESULT hr; 57 - if (pidls[0]) 58 - { 59 - m_pidl0.Free(); 60 - hr = TranslatePidl(&m_pidl0, pidls[0]); 61 - if (!FAILED_UNEXPECTEDLY(hr)) 62 - pidls[0] = m_pidl0; 63 - } 64 - if (pidls[1]) 65 - { 66 - m_pidl1.Free(); 67 - hr = TranslatePidl(&m_pidl1, pidls[1]); 68 - if (!FAILED_UNEXPECTEDLY(hr)) 69 - pidls[1] = m_pidl1; 70 - } 71 - } 72 - 73 - BOOL CFontFolderViewCB::FilterEvent(LONG lEvent) const 74 - { 75 - switch (lEvent & ~SHCNE_INTERRUPT) 28 + switch (lEvent) 76 29 { 77 30 case SHCNE_CREATE: 78 - case SHCNE_DELETE: 79 31 case SHCNE_RENAMEITEM: 80 32 case SHCNE_UPDATEDIR: 81 - return FALSE; // OK 33 + // Refresh font cache and notify the system about the font change 34 + if (g_FontCache) 35 + g_FontCache->Read(); 36 + break; 37 + case SHCNE_DELETE: 38 + break; 82 39 default: 83 40 return TRUE; // We don't want this event 84 41 } 42 + 43 + return FALSE; 85 44 } 86 45 87 46 STDMETHODIMP ··· 91 50 { 92 51 case SFVM_QUERYFSNOTIFY: // Registering change notification 93 52 { 53 + if (!m_pShellView || !m_pFontExt) 54 + return E_FAIL; 94 55 // Now, we can get the view window 95 - ATLASSERT(m_pShellView); 96 - ATLASSERT(m_pFontExt); 97 56 m_pShellView->GetWindow(&m_hwndView); 98 57 m_pFontExt->SetViewWindow(m_hwndView); 99 58 return S_OK; 100 59 } 101 60 case SFVM_FSNOTIFY: // Change notification 102 61 { 103 - if (FilterEvent((LONG)lParam)) 62 + if (FilterEvent((PIDLIST_ABSOLUTE*)wParam, (LONG)lParam)) 104 63 return S_FALSE; // Don't process 105 - 106 - TranslateTwoPIDLs((PIDLIST_ABSOLUTE*)wParam); 107 64 return S_OK; 108 65 } 109 66 }
+4 -8
dll/shellext/fontext/CFontFolderViewCB.h
··· 11 11 : public CComObjectRootEx<CComMultiThreadModelNoCS> 12 12 , public IShellFolderViewCB 13 13 { 14 - CFontExt* m_pFontExt = nullptr; // Not ref-counted! 15 - IShellView* m_pShellView = nullptr; // Not ref-counted! 14 + CFontExt* m_pFontExt = nullptr; 15 + CComPtr<IShellView> m_pShellView; 16 16 HWND m_hwndView = nullptr; 17 17 CComHeapPtr<ITEMIDLIST> m_pidlParent; 18 - CComHeapPtr<ITEMIDLIST> m_pidl0; 19 - CComHeapPtr<ITEMIDLIST> m_pidl1; 20 18 21 - HRESULT TranslatePidl(LPITEMIDLIST* ppidlNew, LPCITEMIDLIST pidl); 22 - void TranslateTwoPIDLs(PIDLIST_ABSOLUTE* pidls); 23 - BOOL FilterEvent(LONG lEvent) const; 19 + BOOL FilterEvent(PIDLIST_ABSOLUTE* apidls, LONG lEvent) const; 24 20 25 21 public: 26 22 CFontFolderViewCB() { } 27 23 void Initialize(CFontExt* pFontExt, IShellView *psv, LPCITEMIDLIST pidlParent); 28 24 29 25 // IShellFolderViewCB 30 - STDMETHOD(MessageSFVCB)(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 26 + STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 31 27 32 28 DECLARE_NO_REGISTRY() 33 29 DECLARE_NOT_AGGREGATABLE(CFontFolderViewCB)
+136 -5
dll/shellext/fontext/fontext.cpp
··· 111 111 if (*cKeys >= 16) 112 112 return ERROR_MORE_DATA; 113 113 114 - HKEY hkey; 115 - LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey); 116 - if (result == ERROR_SUCCESS) 114 + CRegKey key; 115 + LSTATUS error = key.Open(HKEY_CLASSES_ROOT, szClass, KEY_READ | KEY_QUERY_VALUE); 116 + if (error == ERROR_SUCCESS) 117 117 { 118 - array[*cKeys] = hkey; 118 + array[*cKeys] = key.Detach(); 119 119 *cKeys += 1; 120 120 } 121 - return result; 121 + return error; 122 122 } 123 123 124 124 HRESULT ··· 434 434 return S_FALSE; 435 435 436 436 return FAILED_UNEXPECTEDLY(data.hrResult) ? E_FAIL : S_OK; 437 + } 438 + 439 + HRESULT DoPreviewFontFiles(HWND hwnd, IDataObject* pDataObj) 440 + { 441 + CDataObjectHIDA cida(pDataObj); 442 + if (!cida || cida->cidl <= 0) 443 + { 444 + ERR("Invalid IDataObject\n"); 445 + return E_FAIL; 446 + } 447 + 448 + for (UINT iItem = 0; iItem < cida->cidl; ++iItem) 449 + { 450 + PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem); 451 + const FontPidlEntry* fontEntry = _FontFromIL(pidlChild); 452 + if (fontEntry) 453 + RunFontViewer(hwnd, fontEntry); 454 + } 455 + 456 + return S_OK; 457 + } 458 + 459 + HRESULT DeleteFontFiles(HWND hwnd, IDataObject* pDataObj) 460 + { 461 + CDataObjectHIDA cida(pDataObj); 462 + if (!cida || cida->cidl <= 0) 463 + { 464 + ERR("E_FAIL\n"); 465 + return E_FAIL; 466 + } 467 + 468 + PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida); 469 + if (!pidlParent) 470 + { 471 + ERR("pidlParent is NULL\n"); 472 + return E_FAIL; 473 + } 474 + 475 + // Delete files 476 + for (UINT iItem = 0; iItem < cida->cidl; ++iItem) 477 + { 478 + PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem); 479 + const FontPidlEntry* pEntry = _FontFromIL(pidlChild); 480 + if (!pEntry) 481 + { 482 + ERR("Invalid pEntry: %p\n", pEntry); 483 + return E_FAIL; 484 + } 485 + 486 + CStringW szPath = g_FontCache->GetFontFilePath(pEntry->FileName()); 487 + 488 + // WINDOWS BUG: Removing once is not enough 489 + for (INT iTry = 0; iTry < 3; ++iTry) 490 + { 491 + if (!RemoveFontResourceW(szPath) && !RemoveFontResourceExW(szPath, FR_PRIVATE, NULL)) 492 + break; 493 + } 494 + 495 + if (!DeleteFileW(szPath)) 496 + { 497 + ERR("Unable to delete font file: %S: %ld\n", (PCWSTR)szPath, GetLastError()); 498 + return E_FAIL; 499 + } 500 + 501 + CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl(ILCombine(pidlParent, pidlChild)); 502 + if (pidl) 503 + SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, (LPCITEMIDLIST)pidl, NULL); 504 + else 505 + ERR("Out of memory\n"); 506 + } 507 + 508 + // Delete registry values and mark the entry as deleted 509 + CRegKey key; 510 + if (key.Open(FONT_HIVE, FONT_KEY, KEY_WRITE) == ERROR_SUCCESS) 511 + { 512 + for (UINT iItem = 0; iItem < cida->cidl; ++iItem) 513 + { 514 + PCUIDLIST_RELATIVE pidlChild = HIDA_GetPIDLItem(cida, iItem); 515 + const FontPidlEntry* pEntry = _FontFromIL(pidlChild); 516 + if (pEntry) 517 + { 518 + CStringW strFontName = pEntry->Name(); 519 + key.DeleteValue(strFontName); 520 + g_FontCache->MarkDeleted(pEntry); 521 + } 522 + } 523 + key.Close(); 524 + } 525 + 526 + return S_OK; 527 + } 528 + 529 + HRESULT DoDeleteFontFiles(HWND hwnd, IDataObject* pDataObj) 530 + { 531 + CStringW title(MAKEINTRESOURCEW(IDS_REACTOS_FONTS_FOLDER)); 532 + CStringW msg(MAKEINTRESOURCEW(IDS_CONFIRM_DELETE_FONT)); 533 + if (MessageBoxW(hwnd, msg, title, MB_YESNOCANCEL | MB_ICONWARNING) != IDYES) 534 + return S_FALSE; 535 + 536 + HRESULT hr = DeleteFontFiles(hwnd, pDataObj); 537 + if (FAILED_UNEXPECTEDLY(hr)) 538 + { 539 + msg.LoadString(IDS_CANTDELETEFONT); 540 + MessageBoxW(hwnd, msg, title, MB_ICONERROR); 541 + return hr; 542 + } 543 + 544 + SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"fonts", 545 + SMTO_ABORTIFHUNG, 1000, NULL); 546 + SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, NULL); 547 + return S_OK; 548 + } 549 + 550 + void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry) 551 + { 552 + CStringW Path = g_FontCache->GetFontFilePath(fontEntry->FileName()); 553 + if (Path.IsEmpty()) 554 + return; 555 + 556 + // '/d' disables the install button 557 + WCHAR FontPathArg[MAX_PATH + 3]; 558 + StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString()); 559 + PathQuoteSpacesW(FontPathArg + 3); 560 + 561 + SHELLEXECUTEINFOW si = { sizeof(si) }; 562 + si.fMask = SEE_MASK_DOENVSUBST; 563 + si.hwnd = hwnd; 564 + si.lpFile = L"%SystemRoot%\\System32\\fontview.exe"; 565 + si.lpParameters = FontPathArg; 566 + si.nShow = SW_SHOWNORMAL; 567 + ShellExecuteExW(&si); 437 568 } 438 569 439 570 EXTERN_C
+89 -13
dll/shellext/fontext/fontpidl.cpp
··· 3 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 4 * PURPOSE: pidl handling 5 5 * COPYRIGHT: Copyright 2019,2020 Mark Jansen <mark.jansen@reactos.org> 6 + * Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 7 */ 7 8 8 9 #include "precomp.h" 9 10 10 - LPITEMIDLIST _ILCreate(LPCWSTR lpString) 11 + WINE_DEFAULT_DEBUG_CHANNEL(fontext); 12 + 13 + #define FONTPIDL_MAGIC 0x7066 // 'fp' 14 + #define ALIGN_DWORD(size) (((size) + 3) & ~3) 15 + 16 + PITEMID_CHILD _ILCreate(LPCWSTR lpName, LPCWSTR lpFileName) 11 17 { 12 - // Because the FontPidlEntry contains one WCHAR, we do not need to take the null terminator into account 13 - size_t cbData = sizeof(FontPidlEntry) + wcslen(lpString) * sizeof(WCHAR); 18 + ATLASSERT(lpName); 19 + ATLASSERT(lpFileName); 20 + 21 + if (!lpName[0] || !lpFileName[0]) 22 + { 23 + ERR("Invalid parameters: lpName '%S', lpFileName '%S'\n", lpName, lpFileName); 24 + return NULL; 25 + } 26 + 27 + // SECURITY: Check string length 28 + HRESULT hr; 29 + size_t cbName, cbFileName; 30 + hr = StringCbLengthW(lpName, min(MAXWORD - 1, STRSAFE_MAX_CCH) * sizeof(WCHAR), &cbName); 31 + if (FAILED_UNEXPECTEDLY(hr)) 32 + return NULL; 33 + hr = StringCbLengthW(lpFileName, min(MAXWORD - 1, STRSAFE_MAX_CCH) * sizeof(WCHAR), &cbFileName); 34 + if (FAILED_UNEXPECTEDLY(hr)) 35 + return NULL; 36 + cbName += sizeof(UNICODE_NULL); 37 + cbFileName += sizeof(UNICODE_NULL); 38 + 39 + size_t ibName = ALIGN_DWORD(sizeof(FontPidlEntry)); 40 + size_t ibFileName = ALIGN_DWORD(ibName + cbName); 41 + size_t cbData = ibFileName + cbFileName; 42 + if (cbData > MAXWORD - sizeof(WORD)) 43 + { 44 + ATLASSERT(FALSE); 45 + return NULL; 46 + } 47 + 14 48 FontPidlEntry* pidl = (FontPidlEntry*)CoTaskMemAlloc(cbData + sizeof(WORD)); 15 49 if (!pidl) 50 + { 51 + ERR("Out of memory\n"); 16 52 return NULL; 53 + } 17 54 18 55 ZeroMemory(pidl, cbData + sizeof(WORD)); 19 - 20 56 pidl->cb = (WORD)cbData; 21 - pidl->Magic = 'fp'; 22 - wcscpy(pidl->Name, lpString); 23 - // Should be zero already, but make sure it is 24 - *(WORD*)((char*)pidl + cbData) = 0; 57 + pidl->Magic = FONTPIDL_MAGIC; 58 + pidl->ibName = (WORD)ibName; 59 + pidl->ibFileName = (WORD)ibFileName; 25 60 26 - return (LPITEMIDLIST)pidl; 61 + // SECURITY: Copy strings 62 + hr = StringCbCopyW(pidl->Name(), cbName, lpName); 63 + if (FAILED_UNEXPECTEDLY(hr)) 64 + { 65 + CoTaskMemFree(pidl); 66 + return NULL; 67 + } 68 + hr = StringCbCopyW(pidl->FileName(), cbFileName, lpFileName); 69 + if (FAILED_UNEXPECTEDLY(hr)) 70 + { 71 + CoTaskMemFree(pidl); 72 + return NULL; 73 + } 74 + 75 + *(PWORD)((PBYTE)pidl + cbData) = UNICODE_NULL; 76 + 77 + ATLASSERT(_FontFromIL((PITEMID_CHILD)pidl)); 78 + 79 + return (PITEMID_CHILD)pidl; 27 80 } 28 81 29 - const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl) 82 + const FontPidlEntry* _FontFromIL(PCITEMID_CHILD pidl) 30 83 { 84 + if (!pidl || pidl->mkid.cb < sizeof(FontPidlEntry)) 85 + return NULL; 86 + 31 87 const FontPidlEntry* fontEntry = (const FontPidlEntry*)pidl; 32 - if (fontEntry->Magic == 'fp') 33 - return fontEntry; 34 - return NULL; 88 + if (fontEntry->Magic != FONTPIDL_MAGIC) 89 + return NULL; 90 + 91 + // The function gets an arbitrary PIDL here. Security is important. 92 + // SECURITY: Check ibName and ibFileName 93 + if (fontEntry->ibName < sizeof(FontPidlEntry) || fontEntry->ibFileName < sizeof(FontPidlEntry) || 94 + fontEntry->ibName >= fontEntry->cb || fontEntry->ibFileName >= fontEntry->cb || 95 + fontEntry->ibName % sizeof(WCHAR) != 0 || fontEntry->ibFileName % sizeof(WCHAR) != 0) 96 + { 97 + ERR("Invalid fontEntry %p (ibName %d, ibFileName %d, cb %d)\n", fontEntry, 98 + fontEntry->ibName, fontEntry->ibFileName, fontEntry->cb); 99 + return NULL; 100 + } 101 + 102 + // SECURITY: Check null termination 103 + size_t cbName, cbNameMax = fontEntry->cb - fontEntry->ibName; 104 + if (FAILED_UNEXPECTEDLY(StringCbLengthW(fontEntry->Name(), cbNameMax, &cbName))) 105 + return NULL; 106 + size_t cbFileName, cbFileNameMax = fontEntry->cb - fontEntry->ibFileName; 107 + if (FAILED_UNEXPECTEDLY(StringCbLengthW(fontEntry->FileName(), cbFileNameMax, &cbFileName))) 108 + return NULL; 109 + 110 + return fontEntry; 35 111 }
+9 -3
dll/shellext/fontext/fontpidl.hpp
··· 3 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 4 * PURPOSE: pidl handling 5 5 * COPYRIGHT: Copyright 2019 Mark Jansen <mark.jansen@reactos.org> 6 + * Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 7 */ 7 8 8 9 #pragma once ··· 12 13 { 13 14 WORD cb; 14 15 WORD Magic; 15 - WCHAR Name[1]; 16 + WORD ibName; 17 + WORD ibFileName; 18 + LPWSTR Name() { return (LPWSTR)((PBYTE)this + ibName); } 19 + LPWSTR FileName() { return (LPWSTR)((PBYTE)this + ibFileName); } 20 + LPCWSTR Name() const { return (LPCWSTR)((PBYTE)this + ibName); } 21 + LPCWSTR FileName() const { return (LPCWSTR)((PBYTE)this + ibFileName); } 16 22 }; 17 23 #include <poppack.h> 18 24 19 - LPITEMIDLIST _ILCreate(LPCWSTR lpString); 20 - const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl); 25 + PITEMID_CHILD _ILCreate(LPCWSTR lpName, LPCWSTR lpFileName); 26 + const FontPidlEntry* _FontFromIL(PCITEMID_CHILD pidl);
+4 -2
dll/shellext/fontext/lang/de-DE.rc
··· 20 20 STRINGTABLE 21 21 BEGIN 22 22 IDS_REACTOS_FONTS_FOLDER "ReactOS Schriftartenordner" 23 - IDS_INSTALL_OK "The font files have been installed successfully." 24 - IDS_INSTALL_FAILED "Failed to install the font files." 23 + IDS_INSTALL_OK "The font(s) have been installed successfully." 24 + IDS_INSTALL_FAILED "Failed to install the font(s)." 25 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 25 26 IDS_PROPERTIES "P&roperties" 27 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 26 28 END 27 29 28 30 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/en-US.rc
··· 15 15 STRINGTABLE 16 16 BEGIN 17 17 IDS_REACTOS_FONTS_FOLDER "ReactOS Font Folder" 18 - IDS_INSTALL_OK "The font files have been installed successfully." 19 - IDS_INSTALL_FAILED "Failed to install the font files." 18 + IDS_INSTALL_OK "The font(s) have been installed successfully." 19 + IDS_INSTALL_FAILED "Failed to install the font(s)." 20 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 20 21 IDS_PROPERTIES "P&roperties" 22 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 21 23 END 22 24 23 25 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/it-IT.rc
··· 20 20 STRINGTABLE 21 21 BEGIN 22 22 IDS_REACTOS_FONTS_FOLDER "Cartella dei font di ReactOS" 23 - IDS_INSTALL_OK "The font files have been installed successfully." 24 - IDS_INSTALL_FAILED "Failed to install the font files." 23 + IDS_INSTALL_OK "The font(s) have been installed successfully." 24 + IDS_INSTALL_FAILED "Failed to install the font(s)." 25 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 25 26 IDS_PROPERTIES "P&roperties" 27 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 26 28 END 27 29 28 30 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/pl-PL.rc
··· 15 15 STRINGTABLE 16 16 BEGIN 17 17 IDS_REACTOS_FONTS_FOLDER "Folder czcionek ReactOS" 18 - IDS_INSTALL_OK "The font files have been installed successfully." 19 - IDS_INSTALL_FAILED "Failed to install the font files." 18 + IDS_INSTALL_OK "The font(s) have been installed successfully." 19 + IDS_INSTALL_FAILED "Failed to install the font(s)." 20 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 20 21 IDS_PROPERTIES "P&roperties" 22 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 21 23 END 22 24 23 25 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/ro-RO.rc
··· 21 21 STRINGTABLE 22 22 BEGIN 23 23 IDS_REACTOS_FONTS_FOLDER "Folder-ul de fonturi de ReactOS" 24 - IDS_INSTALL_OK "The font files have been installed successfully." 25 - IDS_INSTALL_FAILED "Failed to install the font files." 24 + IDS_INSTALL_OK "The font(s) have been installed successfully." 25 + IDS_INSTALL_FAILED "Failed to install the font(s)." 26 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 26 27 IDS_PROPERTIES "P&roperties" 28 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 27 29 END 28 30 29 31 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/tr-TR.rc
··· 15 15 STRINGTABLE 16 16 BEGIN 17 17 IDS_REACTOS_FONTS_FOLDER "ReactOS Yazı Tipi Dizini" 18 - IDS_INSTALL_OK "The font files have been installed successfully." 19 - IDS_INSTALL_FAILED "Failed to install the font files." 18 + IDS_INSTALL_OK "The font(s) have been installed successfully." 19 + IDS_INSTALL_FAILED "Failed to install the font(s)." 20 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 20 21 IDS_PROPERTIES "P&roperties" 22 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 21 23 END 22 24 23 25 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/zh-CN.rc
··· 20 20 STRINGTABLE 21 21 BEGIN 22 22 IDS_REACTOS_FONTS_FOLDER "ReactOS 字体文件夹" 23 - IDS_INSTALL_OK "The font files have been installed successfully." 24 - IDS_INSTALL_FAILED "Failed to install the font files." 23 + IDS_INSTALL_OK "The font(s) have been installed successfully." 24 + IDS_INSTALL_FAILED "Failed to install the font(s)." 25 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 25 26 IDS_PROPERTIES "P&roperties" 27 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 26 28 END 27 29 28 30 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/zh-HK.rc
··· 20 20 STRINGTABLE 21 21 BEGIN 22 22 IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾" 23 - IDS_INSTALL_OK "The font files have been installed successfully." 24 - IDS_INSTALL_FAILED "Failed to install the font files." 23 + IDS_INSTALL_OK "The font(s) have been installed successfully." 24 + IDS_INSTALL_FAILED "Failed to install the font(s)." 25 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 25 26 IDS_PROPERTIES "P&roperties" 27 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 26 28 END 27 29 28 30 STRINGTABLE
+4 -2
dll/shellext/fontext/lang/zh-TW.rc
··· 20 20 STRINGTABLE 21 21 BEGIN 22 22 IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾" 23 - IDS_INSTALL_OK "The font files have been installed successfully." 24 - IDS_INSTALL_FAILED "Failed to install the font files." 23 + IDS_INSTALL_OK "The font(s) have been installed successfully." 24 + IDS_INSTALL_FAILED "Failed to install the font(s)." 25 + IDS_CONFIRM_DELETE_FONT "Do you want to delete the font(s)?" 25 26 IDS_PROPERTIES "P&roperties" 27 + IDS_CANTDELETEFONT "Failed to delete the font(s)." 26 28 END 27 29 28 30 STRINGTABLE
+4
dll/shellext/fontext/precomp.h
··· 20 20 #include <atlbase.h> 21 21 #include <atlcom.h> 22 22 #include <atlcoll.h> 23 + #include <atlsimpcoll.h> 23 24 #include <atlstr.h> 24 25 #include <wine/debug.h> 25 26 #include <shellutils.h> ··· 82 83 BOOL CheckDropFontFiles(HDROP hDrop); 83 84 BOOL CheckDataObject(IDataObject *pDataObj); 84 85 HRESULT InstallFontsFromDataObject(HWND hwndView, IDataObject* pDataObj); 86 + HRESULT DoPreviewFontFiles(HWND hwnd, IDataObject* pDataObj); 87 + HRESULT DoDeleteFontFiles(HWND hwnd, IDataObject* pDataObj); 88 + void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry); 85 89 86 90 HRESULT 87 91 APIENTRY
+1 -1
dll/shellext/fontext/res/fontext.rgs
··· 4 4 { 5 5 ForceRemove {BD84B380-8CA2-1069-AB1D-08000948F534} = s 'Fonts' 6 6 { 7 - InprocServer32 = s 'fontext.dll' 7 + InprocServer32 = s '%MODULE%' 8 8 { 9 9 val ThreadingModel = s 'Apartment' 10 10 }
+3 -1
dll/shellext/fontext/resource.h
··· 7 7 #define IDS_REACTOS_FONTS_FOLDER 151 8 8 #define IDS_INSTALL_OK 152 9 9 #define IDS_INSTALL_FAILED 153 10 - #define IDS_PROPERTIES 154 10 + #define IDS_CONFIRM_DELETE_FONT 154 11 + #define IDS_PROPERTIES 155 12 + #define IDS_CANTDELETEFONT 156 11 13 12 14 #define IDS_COL_NAME 301 13 15 #define IDS_COL_FILENAME 304
+14 -1
dll/win32/shell32/CDefaultContextMenu.cpp
··· 1159 1159 if (!m_cidl || !m_pDataObj) 1160 1160 return E_FAIL; 1161 1161 1162 + HRESULT hr = _DoInvokeCommandCallback(lpcmi, bCopy ? DFM_CMD_COPY : DFM_CMD_MOVE); 1163 + if (hr == S_OK) 1164 + return hr; 1165 + 1162 1166 FORMATETC formatetc; 1163 1167 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL); 1164 1168 STGMEDIUM medium = {0}; ··· 1174 1178 if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv)))) 1175 1179 psfv->SetPoints(m_pDataObj); 1176 1180 1177 - HRESULT hr = OleSetClipboard(m_pDataObj); 1181 + hr = OleSetClipboard(m_pDataObj); 1178 1182 if (FAILED_UNEXPECTEDLY(hr)) 1179 1183 return hr; 1180 1184 ··· 1774 1778 } 1775 1779 1776 1780 UINT CmdId = LOWORD(idCommand); 1781 + 1782 + if (uFlags == GCS_VERBA || uFlags == GCS_VERBW) 1783 + { 1784 + UINT uMsg = (uFlags == GCS_VERBA) ? DFM_GETVERBA : DFM_GETVERBW; 1785 + WPARAM wParam = MAKEWPARAM(idCommand, uMaxNameLen); 1786 + HRESULT hr = _DoCallback(uMsg, wParam, lpszName); 1787 + if (hr == S_OK) 1788 + return S_OK; 1789 + } 1777 1790 1778 1791 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1779 1792 {
+2
sdk/include/psdk/shlobj.h
··· 417 417 _In_opt_ PCWSTR pszLongPlate, 418 418 _In_opt_ PCWSTR pszDir); 419 419 420 + HRESULT WINAPI SHMultiFileProperties(_In_ IDataObject *pDataObject, _In_ DWORD dwFlags); 421 + 420 422 /***************************************************************************** 421 423 * IContextMenu interface 422 424 */