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