1 /* 2 * PROJECT: ReactOS Font Shell Extension 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: CFontExt implementation 5 * COPYRIGHT: Copyright 2019-2021 Mark Jansen <mark.jansen@reactos.org> 6 * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 */ 8 9 #include "precomp.h" 10 #include "undocgdi.h" // for GetFontResourceInfoW 11 12 WINE_DEFAULT_DEBUG_CHANNEL(fontext); 13 14 #ifndef SHCIDS_ALLFIELDS 15 #define SHCIDS_ALLFIELDS 0x80000000L 16 #endif 17 18 struct FolderViewColumns 19 { 20 int iResource; 21 DWORD dwDefaultState; 22 int cxChar; 23 int fmt; 24 }; 25 26 enum font_columns 27 { 28 FONTEXT_COL_NAME, 29 FONTEXT_COL_FILENAME, 30 FONTEXT_COL_SIZE, 31 FONTEXT_COL_MODIFIED, 32 FONTEXT_COL_ATTR, 33 }; 34 35 static FolderViewColumns g_ColumnDefs[] = 36 { 37 { IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT }, 38 { IDS_COL_FILENAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 20, LVCFMT_LEFT }, 39 { IDS_COL_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_RIGHT }, 40 { IDS_COL_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, 15, LVCFMT_LEFT }, 41 { IDS_COL_ATTR, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 12, LVCFMT_RIGHT }, 42 }; 43 44 45 // Helper functions to translate a guid to a readable name 46 bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size) 47 { 48 WCHAR LocalBuf[100]; 49 DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR); 50 51 if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf), L"Interface\\%s", InterfaceString))) 52 return false; 53 54 return RegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType, buf, &dwDataSize) == ERROR_SUCCESS; 55 } 56 57 WCHAR* g2s(REFCLSID iid) 58 { 59 static WCHAR buf[2][300]; 60 static int idx = 0; 61 62 idx ^= 1; 63 64 LPOLESTR tmp; 65 HRESULT hr = ProgIDFromCLSID(iid, &tmp); 66 if (SUCCEEDED(hr)) 67 { 68 wcscpy(buf[idx], tmp); 69 CoTaskMemFree(tmp); 70 return buf[idx]; 71 } 72 StringFromGUID2(iid, buf[idx], _countof(buf[idx])); 73 if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx]))) 74 { 75 return buf[idx]; 76 } 77 StringFromGUID2(iid, buf[idx], _countof(buf[idx])); 78 79 return buf[idx]; 80 } 81 82 83 CFontExt::CFontExt() 84 { 85 InterlockedIncrement(&g_ModuleRefCnt); 86 } 87 88 CFontExt::~CFontExt() 89 { 90 InterlockedDecrement(&g_ModuleRefCnt); 91 } 92 93 // *** IShellFolder2 methods *** 94 STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid) 95 { 96 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 97 return E_NOTIMPL; 98 } 99 100 STDMETHODIMP CFontExt::EnumSearches(IEnumExtraSearch **ppenum) 101 { 102 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 103 return E_NOTIMPL; 104 } 105 106 STDMETHODIMP CFontExt::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 107 { 108 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 109 return E_NOTIMPL; 110 } 111 112 STDMETHODIMP CFontExt::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags) 113 { 114 if (!pcsFlags || iColumn >= _countof(g_ColumnDefs)) 115 return E_INVALIDARG; 116 117 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState; 118 return S_OK; 119 } 120 121 STDMETHODIMP CFontExt::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) 122 { 123 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 124 return E_NOTIMPL; 125 } 126 127 STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd) 128 { 129 if (iColumn >= _countof(g_ColumnDefs)) 130 return E_FAIL; 131 132 psd->cxChar = g_ColumnDefs[iColumn].cxChar; 133 psd->fmt = g_ColumnDefs[iColumn].fmt; 134 135 // No item requested, so return the column name 136 if (pidl == NULL) 137 { 138 return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource); 139 } 140 141 // Validate that this pidl is the last one 142 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl); 143 if (curpidl->mkid.cb != 0) 144 { 145 ERR("ERROR, unhandled PIDL!\n"); 146 return E_FAIL; 147 } 148 149 // Name, ReactOS specific? 150 if (iColumn == FONTEXT_COL_NAME) 151 return GetDisplayNameOf(pidl, 0, &psd->str); 152 153 const FontPidlEntry* fontEntry = _FontFromIL(pidl); 154 if (!fontEntry) 155 { 156 ERR("ERROR, not a font PIDL!\n"); 157 return E_FAIL; 158 } 159 160 // If we got here, we are in details view! 161 auto info = g_FontCache->Find(fontEntry); 162 if (info == nullptr) 163 { 164 ERR("Unable to query info about %S\n", fontEntry->Name); 165 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 166 } 167 168 int ret; 169 CStringA AttrLetters; 170 DWORD dwAttributes; 171 SYSTEMTIME time; 172 switch (iColumn) 173 { 174 case FONTEXT_COL_FILENAME: 175 return SHSetStrRet(&psd->str, PathFindFileNameW(info->File())); 176 case FONTEXT_COL_SIZE: 177 psd->str.uType = STRRET_CSTR; 178 StrFormatKBSizeA(info->FileSize().QuadPart, psd->str.cStr, MAX_PATH); 179 return S_OK; 180 case FONTEXT_COL_MODIFIED: 181 psd->str.uType = STRRET_CSTR; 182 FileTimeToSystemTime(&info->FileWriteTime(), &time); 183 ret = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, psd->str.cStr, MAX_PATH); 184 if (ret < 1) 185 { 186 ERR("GetDateFormatA failed\n"); 187 return E_FAIL; 188 } 189 psd->str.cStr[ret-1] = ' '; 190 GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret); 191 return S_OK; 192 case FONTEXT_COL_ATTR: 193 AttrLetters.LoadString(IDS_COL_ATTR_LETTERS); 194 if (AttrLetters.GetLength() != 5) 195 { 196 ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n"); 197 return E_FAIL; 198 } 199 psd->str.uType = STRRET_CSTR; 200 dwAttributes = info->FileAttributes(); 201 ret = 0; 202 if (dwAttributes & FILE_ATTRIBUTE_READONLY) 203 psd->str.cStr[ret++] = AttrLetters[0]; 204 if (dwAttributes & FILE_ATTRIBUTE_HIDDEN) 205 psd->str.cStr[ret++] = AttrLetters[1]; 206 if (dwAttributes & FILE_ATTRIBUTE_SYSTEM) 207 psd->str.cStr[ret++] = AttrLetters[2]; 208 if (dwAttributes & FILE_ATTRIBUTE_ARCHIVE) 209 psd->str.cStr[ret++] = AttrLetters[3]; 210 if (dwAttributes & FILE_ATTRIBUTE_COMPRESSED) 211 psd->str.cStr[ret++] = AttrLetters[4]; 212 psd->str.cStr[ret] = '\0'; 213 return S_OK; 214 default: 215 break; 216 } 217 218 UNIMPLEMENTED; 219 return E_NOTIMPL; 220 } 221 222 STDMETHODIMP CFontExt::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) 223 { 224 //ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 225 return E_NOTIMPL; 226 } 227 228 // *** IShellFolder2 methods *** 229 STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) 230 { 231 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 232 return E_NOTIMPL; 233 } 234 235 STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) 236 { 237 return _CEnumFonts_CreateInstance(this, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); 238 } 239 240 STDMETHODIMP CFontExt::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 241 { 242 ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid)); 243 return E_NOTIMPL; 244 } 245 246 STDMETHODIMP CFontExt::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 247 { 248 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 249 return E_NOTIMPL; 250 } 251 252 STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) 253 { 254 const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1); 255 const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2); 256 257 if (!fontEntry1 || !fontEntry2) 258 return E_INVALIDARG; 259 260 int result; 261 DWORD sortMode = lParam & 0xFFFF0000; 262 DWORD column = lParam & 0x0000FFFF; 263 if (sortMode == SHCIDS_ALLFIELDS) 264 { 265 UNIMPLEMENTED; 266 result = (int)fontEntry1->Index - (int)fontEntry2->Index; 267 } 268 else 269 { 270 auto info1 = g_FontCache->Find(fontEntry1); 271 auto info2 = g_FontCache->Find(fontEntry2); 272 273 if (!info1 || !info2) 274 { 275 ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name, fontEntry2->Name); 276 return E_INVALIDARG; 277 } 278 279 switch (column) 280 { 281 case 0xffff: 282 /* ROS bug? */ 283 case FONTEXT_COL_NAME: 284 // These items are already ordered by name 285 result = (int)fontEntry1->Index - (int)fontEntry2->Index; 286 break; 287 case FONTEXT_COL_FILENAME: 288 result = _wcsicmp(PathFindFileNameW(info1->File()), PathFindFileNameW(info2->File())); 289 break; 290 case FONTEXT_COL_SIZE: 291 result = (int)info1->FileSize().HighPart - info2->FileSize().HighPart; 292 if (result == 0) 293 result = (int)info1->FileSize().LowPart - info2->FileSize().LowPart; 294 break; 295 case FONTEXT_COL_MODIFIED: 296 result = CompareFileTime(&info1->FileWriteTime(), &info2->FileWriteTime()); 297 break; 298 case FONTEXT_COL_ATTR: 299 // FIXME: how to compare attributes? 300 result = (int)info1->FileAttributes() - info2->FileAttributes(); 301 break; 302 default: 303 ERR("Unimplemented column %u\n", column); 304 return E_INVALIDARG; 305 } 306 } 307 308 return MAKE_COMPARE_HRESULT(result); 309 } 310 311 STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) 312 { 313 HRESULT hr = E_NOINTERFACE; 314 315 *ppvOut = NULL; 316 317 if (IsEqualIID(riid, IID_IDropTarget)) 318 { 319 ERR("IDropTarget not implemented\n"); 320 *ppvOut = static_cast<IDropTarget *>(this); 321 AddRef(); 322 hr = S_OK; 323 } 324 else if (IsEqualIID(riid, IID_IContextMenu)) 325 { 326 ERR("IContextMenu not implemented\n"); 327 hr = E_NOTIMPL; 328 } 329 else if (IsEqualIID(riid, IID_IShellView)) 330 { 331 // Just create a default shell folder view, and register ourself as folder 332 SFV_CREATE sfv = { sizeof(SFV_CREATE) }; 333 sfv.pshf = this; 334 hr = SHCreateShellFolderView(&sfv, (IShellView**)ppvOut); 335 } 336 337 return hr; 338 } 339 340 STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) 341 { 342 if (!rgfInOut || !cidl || !apidl) 343 return E_INVALIDARG; 344 345 DWORD rgf = 0; 346 while (cidl > 0 && *apidl) 347 { 348 const FontPidlEntry* fontEntry = _FontFromIL(*apidl); 349 if (fontEntry) 350 { 351 // We don't support delete yet 352 rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM); 353 } 354 else 355 { 356 rgf = 0; 357 break; 358 } 359 360 apidl++; 361 cidl--; 362 } 363 364 *rgfInOut = rgf; 365 return S_OK; 366 } 367 368 369 STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) 370 { 371 if (riid == IID_IContextMenu || 372 riid == IID_IContextMenu2 || 373 riid == IID_IContextMenu3) 374 { 375 return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut); 376 } 377 else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW) 378 { 379 if (cidl == 1) 380 { 381 const FontPidlEntry* fontEntry = _FontFromIL(*apidl); 382 if (fontEntry) 383 { 384 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL; 385 CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry)); 386 // Just create a default icon extractor based on the filename 387 // We might want to create a preview with the font to get really fancy one day. 388 return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut); 389 } 390 } 391 else 392 { 393 ERR("IID_IExtractIcon with cidl != 1 UNIMPLEMENTED\n"); 394 } 395 } 396 else if (riid == IID_IDataObject) 397 { 398 if (cidl >= 1) 399 { 400 return _CDataObject_CreateInstance(m_Folder, cidl, apidl, riid, ppvOut); 401 } 402 else 403 { 404 ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n"); 405 } 406 } 407 408 //ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid)); 409 return E_NOTIMPL; 410 } 411 412 STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) 413 { 414 if (!pidl) 415 return E_NOTIMPL; 416 417 // Validate that this pidl is the last one 418 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl); 419 if (curpidl->mkid.cb != 0) 420 { 421 ERR("ERROR, unhandled PIDL!\n"); 422 return E_FAIL; 423 } 424 425 const FontPidlEntry* fontEntry = _FontFromIL(pidl); 426 if (!fontEntry) 427 return E_FAIL; 428 429 if (dwFlags & SHGDN_FORPARSING) 430 { 431 CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry), true); 432 if (!File.IsEmpty()) 433 { 434 return SHSetStrRet(strRet, File); 435 } 436 } 437 438 return SHSetStrRet(strRet, fontEntry->Name); 439 } 440 441 STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) 442 { 443 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); 444 return E_NOTIMPL; 445 } 446 447 // *** IPersistFolder2 methods *** 448 STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl) 449 { 450 if (ppidl && m_Folder) 451 { 452 *ppidl = ILClone(m_Folder); 453 return S_OK; 454 } 455 456 return E_POINTER; 457 } 458 459 460 // *** IPersistFolder methods *** 461 STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl) 462 { 463 WCHAR PidlPath[MAX_PATH + 1] = {0}, FontsDir[MAX_PATH + 1]; 464 if (!SHGetPathFromIDListW(pidl, PidlPath)) 465 { 466 ERR("Unable to extract path from pidl\n"); 467 return E_FAIL; 468 } 469 470 HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir); 471 if (FAILED_UNEXPECTEDLY(hr)) 472 { 473 ERR("Unable to get fonts path (0x%x)\n", hr); 474 return hr; 475 } 476 477 if (_wcsicmp(PidlPath, FontsDir)) 478 { 479 ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath); 480 return E_FAIL; 481 } 482 483 m_Folder.Attach(ILClone(pidl)); 484 StringCchCatW(FontsDir, _countof(FontsDir), L"\\"); 485 g_FontCache->SetFontDir(FontsDir); 486 487 return S_OK; 488 } 489 490 491 // *** IPersist methods *** 492 STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId) 493 { 494 *lpClassId = CLSID_CFontExt; 495 return S_OK; 496 } 497 498 // *** IDropTarget methods *** 499 STDMETHODIMP CFontExt::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 500 { 501 *pdwEffect = DROPEFFECT_NONE; 502 503 CDataObjectHIDA cida(pDataObj); 504 if (FAILED_UNEXPECTEDLY(cida.hr())) 505 return cida.hr(); 506 507 *pdwEffect = DROPEFFECT_COPY; 508 return S_OK; 509 } 510 511 STDMETHODIMP CFontExt::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 512 { 513 return S_OK; 514 } 515 516 STDMETHODIMP CFontExt::DragLeave() 517 { 518 return S_OK; 519 } 520 521 STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 522 { 523 *pdwEffect = DROPEFFECT_NONE; 524 525 CDataObjectHIDA cida(pDataObj); 526 if (!cida) 527 return E_UNEXPECTED; 528 529 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida); 530 if (!pidlParent) 531 { 532 ERR("pidlParent is NULL\n"); 533 return E_FAIL; 534 } 535 536 BOOL bOK = TRUE; 537 CAtlArray<CStringW> FontPaths; 538 for (UINT n = 0; n < cida->cidl; ++n) 539 { 540 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(cida, n); 541 if (!pidlRelative) 542 continue; 543 544 PIDLIST_ABSOLUTE pidl = ILCombine(pidlParent, pidlRelative); 545 if (!pidl) 546 { 547 ERR("ILCombine failed\n"); 548 bOK = FALSE; 549 break; 550 } 551 552 WCHAR szPath[MAX_PATH]; 553 BOOL ret = SHGetPathFromIDListW(pidl, szPath); 554 ILFree(pidl); 555 556 if (!ret) 557 { 558 ERR("SHGetPathFromIDListW failed\n"); 559 bOK = FALSE; 560 break; 561 } 562 563 if (PathIsDirectoryW(szPath)) 564 { 565 ERR("PathIsDirectory\n"); 566 bOK = FALSE; 567 break; 568 } 569 570 LPCWSTR pchDotExt = PathFindExtensionW(szPath); 571 if (!IsFontDotExt(pchDotExt)) 572 { 573 ERR("'%S' is not supported\n", pchDotExt); 574 bOK = FALSE; 575 break; 576 } 577 578 FontPaths.Add(szPath); 579 } 580 581 if (!bOK) 582 return E_FAIL; 583 584 CRegKey keyFonts; 585 if (keyFonts.Open(FONT_HIVE, FONT_KEY, KEY_WRITE) != ERROR_SUCCESS) 586 { 587 ERR("keyFonts.Open failed\n"); 588 return E_FAIL; 589 } 590 591 for (size_t iItem = 0; iItem < FontPaths.GetCount(); ++iItem) 592 { 593 HRESULT hr = DoInstallFontFile(FontPaths[iItem], g_FontCache->FontPath(), keyFonts.m_hKey); 594 if (FAILED_UNEXPECTEDLY(hr)) 595 { 596 bOK = FALSE; 597 break; 598 } 599 } 600 601 // Invalidate our cache 602 g_FontCache->Read(); 603 604 // Notify the system that a font was added 605 SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); 606 607 // Notify the shell that the folder contents are changed 608 SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATHW, g_FontCache->FontPath().GetString(), NULL); 609 610 // TODO: Show message 611 612 return bOK ? S_OK : E_FAIL; 613 } 614 615 HRESULT CFontExt::DoInstallFontFile(LPCWSTR pszFontPath, LPCWSTR pszFontsDir, HKEY hkeyFonts) 616 { 617 WCHAR szDestFile[MAX_PATH]; 618 619 // Add this font to the font list, so we can query the name 620 if (!AddFontResourceW(pszFontPath)) 621 { 622 ERR("AddFontResourceW('%S') failed\n", pszFontPath); 623 DeleteFileW(szDestFile); 624 return E_FAIL; 625 } 626 627 CStringW strFontName; 628 HRESULT hr = DoGetFontTitle(pszFontPath, strFontName); 629 630 // We got the name, remove it again 631 RemoveFontResourceW(pszFontPath); 632 633 if (!SUCCEEDED(hr)) 634 { 635 ERR("DoGetFontTitle failed (err=0x%x)!\n", hr); 636 return hr; 637 } 638 639 StringCchCopyW(szDestFile, sizeof(szDestFile), pszFontsDir); 640 641 LPCWSTR pszFileTitle = PathFindFileName(pszFontPath); 642 PathAppendW(szDestFile, pszFileTitle); 643 if (!CopyFileW(pszFontPath, szDestFile, FALSE)) 644 { 645 ERR("CopyFileW('%S', '%S') failed\n", pszFontPath, szDestFile); 646 return E_FAIL; 647 } 648 649 DWORD cbData = (wcslen(pszFileTitle) + 1) * sizeof(WCHAR); 650 LONG nError = RegSetValueExW(hkeyFonts, strFontName, 0, REG_SZ, 651 (const BYTE *)pszFileTitle, cbData); 652 if (nError) 653 { 654 ERR("RegSetValueExW failed with %ld\n", nError); 655 DeleteFileW(szDestFile); 656 return E_FAIL; 657 } 658 659 AddFontResourceW(szDestFile); 660 661 return S_OK; 662 } 663 664 HRESULT 665 CFontExt::DoGetFontTitle(IN LPCWSTR pszFontPath, OUT CStringW& strFontName) 666 { 667 DWORD cbInfo = 0; 668 BOOL ret = GetFontResourceInfoW(pszFontPath, &cbInfo, NULL, 1); 669 if (!ret || !cbInfo) 670 { 671 ERR("GetFontResourceInfoW failed (err: %u)\n", GetLastError()); 672 return E_FAIL; 673 } 674 675 LPWSTR pszBuffer = strFontName.GetBuffer(cbInfo / sizeof(WCHAR)); 676 ret = GetFontResourceInfoW(pszFontPath, &cbInfo, pszBuffer, 1); 677 DWORD dwErr = GetLastError();; 678 strFontName.ReleaseBuffer(); 679 if (ret) 680 { 681 TRACE("pszFontName: %S\n", (LPCWSTR)strFontName); 682 return S_OK; 683 } 684 685 ERR("GetFontResourceInfoW failed (err: %u)\n", dwErr); 686 return E_FAIL; 687 } 688