xref: /reactos/dll/shellext/fontext/CFontExt.cpp (revision 8a92b556)
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,2020 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 
15 struct FolderViewColumns
16 {
17     int iResource;
18     DWORD dwDefaultState;
19     int cxChar;
20     int fmt;
21 };
22 
23 static FolderViewColumns g_ColumnDefs[] =
24 {
25     { IDS_COL_NAME,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   25, LVCFMT_LEFT },
26     { IDS_COL_FILENAME,  SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   20, LVCFMT_LEFT },
27     { IDS_COL_SIZE,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   10, LVCFMT_RIGHT },
28     { IDS_COL_MODIFIED,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT,  15, LVCFMT_LEFT },
29     { IDS_COL_ATTR,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   12, LVCFMT_RIGHT },
30 };
31 
32 
33 
34 // Should fix our headers..
35 EXTERN_C HRESULT WINAPI SHCreateFileExtractIconW(LPCWSTR pszPath, DWORD dwFileAttributes, REFIID riid, void **ppv);
36 
37 
38 // Helper functions to translate a guid to a readable name
39 bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size)
40 {
41     WCHAR LocalBuf[100];
42     DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR);
43 
44     if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf), L"Interface\\%s", InterfaceString)))
45         return false;
46 
47     return RegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType, buf, &dwDataSize) == ERROR_SUCCESS;
48 }
49 
50 WCHAR* g2s(REFCLSID iid)
51 {
52     static WCHAR buf[2][300];
53     static int idx = 0;
54 
55     idx ^= 1;
56 
57     LPOLESTR tmp;
58     HRESULT hr = ProgIDFromCLSID(iid, &tmp);
59     if (SUCCEEDED(hr))
60     {
61         wcscpy(buf[idx], tmp);
62         CoTaskMemFree(tmp);
63         return buf[idx];
64     }
65     StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
66     if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx])))
67     {
68         return buf[idx];
69     }
70     StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
71 
72     return buf[idx];
73 }
74 
75 
76 CFontExt::CFontExt()
77 {
78     InterlockedIncrement(&g_ModuleRefCnt);
79 }
80 
81 CFontExt::~CFontExt()
82 {
83     InterlockedDecrement(&g_ModuleRefCnt);
84 }
85 
86 // *** IShellFolder2 methods ***
87 STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid)
88 {
89     ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
90     return E_NOTIMPL;
91 }
92 
93 STDMETHODIMP CFontExt::EnumSearches(IEnumExtraSearch **ppenum)
94 {
95     ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
96     return E_NOTIMPL;
97 }
98 
99 STDMETHODIMP CFontExt::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
100 {
101     ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
102     return E_NOTIMPL;
103 }
104 
105 STDMETHODIMP CFontExt::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
106 {
107     if (!pcsFlags || iColumn >= _countof(g_ColumnDefs))
108         return E_INVALIDARG;
109 
110     *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
111     return S_OK;
112 }
113 
114 STDMETHODIMP CFontExt::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
115 {
116     ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
117     return E_NOTIMPL;
118 }
119 
120 STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
121 {
122     if (iColumn >= _countof(g_ColumnDefs))
123         return E_FAIL;
124 
125     psd->cxChar = g_ColumnDefs[iColumn].cxChar;
126     psd->fmt = g_ColumnDefs[iColumn].fmt;
127 
128     // No item requested, so return the column name
129     if (pidl == NULL)
130     {
131         return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
132     }
133 
134     // Validate that this pidl is the last one
135     PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
136     if (curpidl->mkid.cb != 0)
137     {
138         ERR("ERROR, unhandled PIDL!\n");
139         return E_FAIL;
140     }
141 
142     // Name, ReactOS specific?
143     if (iColumn == 0)
144         return GetDisplayNameOf(pidl, 0, &psd->str);
145 
146     const FontPidlEntry* fontEntry = _FontFromIL(pidl);
147     if (!fontEntry)
148     {
149         ERR("ERROR, not a font PIDL!\n");
150         return E_FAIL;
151     }
152 
153     // If we got here, we are in details view!
154     // Let's see if we got info about this file that we can re-use
155     if (m_LastDetailsFontName != fontEntry->Name)
156     {
157         CStringW File = g_FontCache->Filename(fontEntry, true);
158         HANDLE hFile = FindFirstFileW(File, &m_LastDetailsFileData);
159         if (hFile == INVALID_HANDLE_VALUE)
160         {
161             m_LastDetailsFontName.Empty();
162             ERR("Unable to query info about %S\n", File.GetString());
163             return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
164         }
165         FindClose(hFile);
166         m_LastDetailsFontName = fontEntry->Name;
167     }
168 
169     // Most code borrowed from CFSFolder::GetDetailsOf
170     FILETIME lft;
171     SYSTEMTIME time;
172     int ret;
173     LARGE_INTEGER FileSize;
174     CStringA AttrLetters;
175     switch (iColumn)
176     {
177     case 1: // Filename
178         return SHSetStrRet(&psd->str, m_LastDetailsFileData.cFileName);
179     case 2: // Size
180         psd->str.uType = STRRET_CSTR;
181         FileSize.HighPart = m_LastDetailsFileData.nFileSizeHigh;
182         FileSize.LowPart = m_LastDetailsFileData.nFileSizeLow;
183         StrFormatKBSizeA(FileSize.QuadPart, psd->str.cStr, MAX_PATH);
184         return S_OK;
185     case 3: // Modified
186         FileTimeToLocalFileTime(&m_LastDetailsFileData.ftLastWriteTime, &lft);
187         FileTimeToSystemTime (&lft, &time);
188         psd->str.uType = STRRET_CSTR;
189         ret = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, psd->str.cStr, MAX_PATH);
190         if (ret < 1)
191         {
192             ERR("GetDateFormatA failed\n");
193             return E_FAIL;
194         }
195         psd->str.cStr[ret-1] = ' ';
196         GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret);
197         return S_OK;
198     case 4: // Attributes
199         AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
200         if (AttrLetters.GetLength() != 5)
201         {
202             ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n");
203             return E_FAIL;
204         }
205         psd->str.uType = STRRET_CSTR;
206         ret = 0;
207         if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
208             psd->str.cStr[ret++] = AttrLetters[0];
209         if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
210             psd->str.cStr[ret++] = AttrLetters[1];
211         if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
212             psd->str.cStr[ret++] = AttrLetters[2];
213         if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
214             psd->str.cStr[ret++] = AttrLetters[3];
215         if (m_LastDetailsFileData.dwFileAttributes & 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 = (int)fontEntry1->Index - (int)fontEntry2->Index;
266 
267     return MAKE_COMPARE_HRESULT(result);
268 }
269 
270 STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
271 {
272     HRESULT hr = E_NOINTERFACE;
273 
274     *ppvOut = NULL;
275 
276     if (IsEqualIID(riid, IID_IDropTarget))
277     {
278         ERR("IDropTarget not implemented\n");
279         *ppvOut = static_cast<IDropTarget *>(this);
280         AddRef();
281         hr = S_OK;
282     }
283     else if (IsEqualIID(riid, IID_IContextMenu))
284     {
285         ERR("IContextMenu not implemented\n");
286         hr = E_NOTIMPL;
287     }
288     else if (IsEqualIID(riid, IID_IShellView))
289     {
290         // Just create a default shell folder view, and register ourself as folder
291         SFV_CREATE sfv = { sizeof(SFV_CREATE) };
292         sfv.pshf = this;
293         hr = SHCreateShellFolderView(&sfv, (IShellView**)ppvOut);
294     }
295 
296     return hr;
297 }
298 
299 STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
300 {
301     if (!rgfInOut || !cidl || !apidl)
302         return E_INVALIDARG;
303 
304     DWORD rgf = 0;
305     while (cidl > 0 && *apidl)
306     {
307         const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
308         if (fontEntry)
309         {
310             // We don't support delete yet
311             rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM);
312         }
313         else
314         {
315             rgf = 0;
316             break;
317         }
318 
319         apidl++;
320         cidl--;
321     }
322 
323     *rgfInOut = rgf;
324     return S_OK;
325 }
326 
327 
328 STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
329 {
330     if (riid == IID_IContextMenu ||
331         riid == IID_IContextMenu2 ||
332         riid == IID_IContextMenu3)
333     {
334         return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut);
335     }
336     else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
337     {
338         if (cidl == 1)
339         {
340             const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
341             if (fontEntry)
342             {
343                 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
344                 CStringW File = g_FontCache->Filename(fontEntry);
345                 // Just create a default icon extractor based on the filename
346                 // We might want to create a preview with the font to get really fancy one day.
347                 return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut);
348             }
349         }
350         else
351         {
352             ERR("IID_IExtractIcon with cidl != 1 UNIMPLEMENTED\n");
353         }
354     }
355     else if (riid == IID_IDataObject)
356     {
357         if (cidl >= 1)
358         {
359             return _CDataObject_CreateInstance(m_Folder, cidl, apidl, riid, ppvOut);
360         }
361         else
362         {
363             ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n");
364         }
365     }
366 
367     //ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid));
368     return E_NOTIMPL;
369 }
370 
371 STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
372 {
373     if (!pidl)
374         return E_NOTIMPL;
375 
376     // Validate that this pidl is the last one
377     PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
378     if (curpidl->mkid.cb != 0)
379     {
380         ERR("ERROR, unhandled PIDL!\n");
381         return E_FAIL;
382     }
383 
384     const FontPidlEntry* fontEntry = _FontFromIL(pidl);
385     if (!fontEntry)
386         return E_FAIL;
387 
388     return SHSetStrRet(strRet, fontEntry->Name);
389 }
390 
391 STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
392 {
393     ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
394     return E_NOTIMPL;
395 }
396 
397 // *** IPersistFolder2 methods ***
398 STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl)
399 {
400     if (ppidl && m_Folder)
401     {
402         *ppidl = ILClone(m_Folder);
403         return S_OK;
404     }
405 
406     return E_POINTER;
407 }
408 
409 
410 // *** IPersistFolder methods ***
411 STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl)
412 {
413     WCHAR PidlPath[MAX_PATH + 1] = {0}, FontsDir[MAX_PATH + 1];
414     if (!SHGetPathFromIDListW(pidl, PidlPath))
415     {
416         ERR("Unable to extract path from pidl\n");
417         return E_FAIL;
418     }
419 
420     HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
421     if (FAILED_UNEXPECTEDLY(hr))
422     {
423         ERR("Unable to get fonts path (0x%x)\n", hr);
424         return hr;
425     }
426 
427     if (_wcsicmp(PidlPath, FontsDir))
428     {
429         ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath);
430         return E_FAIL;
431     }
432 
433     m_Folder.Attach(ILClone(pidl));
434     StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
435     g_FontCache->SetFontDir(FontsDir);
436 
437     return S_OK;
438 }
439 
440 
441 // *** IPersist methods ***
442 STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId)
443 {
444     *lpClassId = CLSID_CFontExt;
445     return S_OK;
446 }
447 
448 // *** IDropTarget methods ***
449 STDMETHODIMP CFontExt::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
450 {
451     *pdwEffect = DROPEFFECT_NONE;
452 
453     CComHeapPtr<CIDA> cida;
454     HRESULT hr = _GetCidlFromDataObject(pDataObj, &cida);
455     if (FAILED_UNEXPECTEDLY(hr))
456         return hr;
457 
458     *pdwEffect = DROPEFFECT_COPY;
459     return S_OK;
460 }
461 
462 STDMETHODIMP CFontExt::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
463 {
464     return S_OK;
465 }
466 
467 STDMETHODIMP CFontExt::DragLeave()
468 {
469     return S_OK;
470 }
471 
472 STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
473 {
474     *pdwEffect = DROPEFFECT_NONE;
475 
476     CComHeapPtr<CIDA> cida;
477     HRESULT hr = _GetCidlFromDataObject(pDataObj, &cida);
478     if (FAILED_UNEXPECTEDLY(hr))
479         return hr;
480 
481     PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida);
482     if (!pidlParent)
483     {
484         ERR("pidlParent is NULL\n");
485         return E_FAIL;
486     }
487 
488     BOOL bOK = TRUE;
489     CAtlArray<CStringW> FontPaths;
490     for (UINT n = 0; n < cida->cidl; ++n)
491     {
492         PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(cida, n);
493         if (!pidlRelative)
494             continue;
495 
496         PIDLIST_ABSOLUTE pidl = ILCombine(pidlParent, pidlRelative);
497         if (!pidl)
498         {
499             ERR("ILCombine failed\n");
500             bOK = FALSE;
501             break;
502         }
503 
504         WCHAR szPath[MAX_PATH];
505         BOOL ret = SHGetPathFromIDListW(pidl, szPath);
506         ILFree(pidl);
507 
508         if (!ret)
509         {
510             ERR("SHGetPathFromIDListW failed\n");
511             bOK = FALSE;
512             break;
513         }
514 
515         if (PathIsDirectoryW(szPath))
516         {
517             ERR("PathIsDirectory\n");
518             bOK = FALSE;
519             break;
520         }
521 
522         LPCWSTR pchDotExt = PathFindExtensionW(szPath);
523         if (!IsFontDotExt(pchDotExt))
524         {
525             ERR("'%S' is not supported\n", pchDotExt);
526             bOK = FALSE;
527             break;
528         }
529 
530         FontPaths.Add(szPath);
531     }
532 
533     if (!bOK)
534         return E_FAIL;
535 
536     CRegKey keyFonts;
537     if (keyFonts.Open(FONT_HIVE, FONT_KEY, KEY_WRITE) != ERROR_SUCCESS)
538     {
539         ERR("keyFonts.Open failed\n");
540         return E_FAIL;
541     }
542 
543     for (size_t iItem = 0; iItem < FontPaths.GetCount(); ++iItem)
544     {
545         HRESULT hr = DoInstallFontFile(FontPaths[iItem], g_FontCache->FontPath(), keyFonts.m_hKey);
546         if (FAILED_UNEXPECTEDLY(hr))
547         {
548             bOK = FALSE;
549             break;
550         }
551     }
552 
553     // TODO: update g_FontCache
554 
555     SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
556 
557     // TODO: Show message
558 
559     return bOK ? S_OK : E_FAIL;
560 }
561 
562 HRESULT CFontExt::DoInstallFontFile(LPCWSTR pszFontPath, LPCWSTR pszFontsDir, HKEY hkeyFonts)
563 {
564     WCHAR szDestFile[MAX_PATH];
565     LPCWSTR pszFileTitle = PathFindFileName(pszFontPath);
566 
567     CStringW strFontName;
568     if (!DoGetFontTitle(pszFontPath, strFontName))
569         return E_FAIL;
570 
571     RemoveFontResourceW(pszFileTitle);
572 
573     StringCchCopyW(szDestFile, sizeof(szDestFile), pszFontsDir);
574     PathAppendW(szDestFile, pszFileTitle);
575     if (!CopyFileW(pszFontPath, szDestFile, FALSE))
576     {
577         ERR("CopyFileW('%S', '%S') failed\n", pszFontPath, szDestFile);
578         return E_FAIL;
579     }
580 
581     if (!AddFontResourceW(szDestFile))
582     {
583         ERR("AddFontResourceW('%S') failed\n", pszFileTitle);
584         DeleteFileW(szDestFile);
585         return E_FAIL;
586     }
587 
588     DWORD cbData = (wcslen(pszFileTitle) + 1) * sizeof(WCHAR);
589     LONG nError = RegSetValueExW(hkeyFonts, strFontName, 0, REG_SZ,
590                                  (const BYTE *)pszFileTitle, cbData);
591     if (nError)
592     {
593         ERR("RegSetValueExW failed with %ld\n", nError);
594         RemoveFontResourceW(pszFileTitle);
595         DeleteFileW(szDestFile);
596         return E_FAIL;
597     }
598 
599     return S_OK;
600 }
601 
602 HRESULT
603 CFontExt::DoGetFontTitle(IN LPCWSTR pszFontPath, OUT CStringW& strFontName)
604 {
605     DWORD cbInfo = 0;
606     BOOL ret = GetFontResourceInfoW(pszFontPath, &cbInfo, NULL, 1);
607     if (!ret || !cbInfo)
608     {
609         ERR("GetFontResourceInfoW failed\n");
610         return E_FAIL;
611     }
612 
613     LPWSTR pszBuffer = strFontName.GetBuffer(cbInfo / sizeof(WCHAR));
614     ret = GetFontResourceInfoW(pszFontPath, &cbInfo, pszBuffer, 1);
615     strFontName.ReleaseBuffer();
616     if (ret)
617     {
618         TRACE("pszFontName: %S\n", (LPCWSTR)strFontName);
619         return S_OK;
620     }
621 
622     ERR("GetFontResourceInfoW failed\n");
623     return E_FAIL;
624 }
625