调用TTGetEmbeddedFontInfo程序崩溃,求不使用DirectWrite获取HDC字体文件名
问题分析与修复方案
核心崩溃原因
调用TTGetEmbeddedFontInfo时崩溃,主要是因为未正确初始化TTLOADINFO的pusRefStr缓冲区:
- 该函数需要先调用一次获取所需的缓冲区大小(
usRefStrSize),再分配对应内存后进行二次调用;直接传入pusRefStr = nullptr会导致函数尝试向空指针写入数据,触发访问违规崩溃。
其他潜在问题
- 异常信息拼接错误:抛出
runtime_error时,直接将ULONG类型的错误码与字符串拼接会导致指针运算错误,需先转换为字符串。 - 临时文件残留:原代码未删除生成的临时文件,会留下垃圾文件。
- 资源泄漏风险:动态分配的内存未显式释放。
修复后的完整代码
#define _CRT_SECURE_NO_DEPRECATE #include <iostream> #include <Windows.h> #include <t2embapi.h> #include <sstream> #pragma comment(lib, "t2embed.lib") unsigned long CALLBACK write_proc(void* tFile, const void* buffer, const unsigned long count) { std::wcout << L"write_proc is called" << std::endl; return (unsigned long)fwrite(buffer, 1, count, (FILE*)tFile); } unsigned long CALLBACK read_proc(void* tFile, void* buffer, const unsigned long count) { std::wcout << L"read_proc is called" << std::endl; return (unsigned long)fread(buffer, 1, count, (FILE*)tFile); } int main() { HDC hdc = CreateCompatibleDC(nullptr); if (hdc == nullptr) { throw std::runtime_error("Failed to call CreateCompatibleDC"); } WCHAR tempPath[MAX_PATH]; WCHAR tempFileName[MAX_PATH]; if (GetTempPathW(MAX_PATH, tempPath) == 0) { throw std::runtime_error("Failed to call GetTempPathW"); } if (GetTempFileNameW(tempPath, L"TMP", 0, tempFileName) == 0) { throw std::runtime_error("Failed to call GetTempFileNameW"); } std::wcout << L"tempFileName: " << tempFileName << std::endl; FILE* tfile = _wfopen(tempFileName, L"wb+"); if (tfile == NULL) { throw std::runtime_error("Failed to call fopen"); } LOGFONTW lf{}; lf.lfCharSet = DEFAULT_CHARSET; wcscpy_s(lf.lfFaceName, L"Arial"); lf.lfItalic = false; lf.lfWeight = 400; lf.lfOutPrecision = OUT_TT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = ANTIALIASED_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; HFONT hfont = CreateFontIndirectW(&lf); if (hfont == NULL) { throw std::runtime_error("Failed to call CreateFont"); } SelectObject(hdc, hfont); ULONG ulFlags = TTEMBED_RAW; ULONG ulCharSet = CHARSET_UNICODE; ULONG ulPrivStatus; ULONG ulStatus; if (TTEmbedFontEx(hdc, ulFlags, ulCharSet, &ulPrivStatus, &ulStatus, (WRITEEMBEDPROC)write_proc, tfile, 0, 0, 0, nullptr) != E_NONE) { std::ostringstream oss; oss << "Failed to call TTEmbedFontEx. error code: " << ulStatus; throw std::runtime_error(oss.str()); } // 将文件指针重置到开头 fseek(tfile, 0, SEEK_SET); std::wcout << L"The next call is TTGetEmbeddedFontInfo" << std::endl; TTLOADINFO loadInfo = { 0 }; loadInfo.usStructSize = sizeof(TTLOADINFO); // 第一次调用:获取所需的缓冲区大小 if (TTGetEmbeddedFontInfo(ulFlags, &ulPrivStatus, LICENSE_DEFAULT, &ulStatus, (READEMBEDPROC)read_proc, tfile, &loadInfo) != E_NONE) { std::ostringstream oss; oss << "Failed to get font info size. error code: " << ulStatus; throw std::runtime_error(oss.str()); } // 分配缓冲区内存(usRefStrSize是字节数,需转换为WCHAR数量) WCHAR* pRefStr = new WCHAR[loadInfo.usRefStrSize / sizeof(WCHAR)]; loadInfo.pusRefStr = (WORD*)pRefStr; // 第二次调用:获取实际的字体信息 if (TTGetEmbeddedFontInfo(ulFlags, &ulPrivStatus, LICENSE_DEFAULT, &ulStatus, (READEMBEDPROC)read_proc, tfile, &loadInfo) != E_NONE) { std::ostringstream oss; oss << "Failed to call TTGetEmbeddedFontInfo. error code: " << ulStatus; delete[] pRefStr; throw std::runtime_error(oss.str()); } std::wcout << L"ulPrivStatus: " << ulPrivStatus << std::endl; std::wcout << L"ulStatus: " << ulStatus << std::endl; std::wcout << L"Filename size: " << loadInfo.usRefStrSize << std::endl; if (loadInfo.pusRefStr == nullptr) { std::wcout << L"Filename: is null" << std::endl; } else { std::wcout << L"Filename: " << pRefStr << std::endl; } // 清理资源 delete[] pRefStr; fclose(tfile); DeleteObject(hfont); DeleteDC(hdc); DeleteFileW(tempFileName); // 删除临时文件 return 0; }
关键修复点说明
- 分两次调用
TTGetEmbeddedFontInfo:- 第一次调用时
pusRefStr设为nullptr,函数会返回所需的字符串缓冲区大小到usRefStrSize; - 根据获取的大小分配内存后,二次调用函数即可正确读取字体来源信息。
- 第一次调用时
- 修复异常信息拼接:使用
ostringstream将错误码转换为字符串后再拼接,避免指针运算错误。 - 完善资源清理:新增临时文件删除、动态内存释放的代码,避免资源泄漏。
内容的提问来源于stack exchange,提问作者jeremie bergeron
相关产品推荐
相关产品推荐

