You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用TTGetEmbeddedFontInfo程序崩溃,求不使用DirectWrite获取HDC字体文件名

问题分析与修复方案

核心崩溃原因

调用TTGetEmbeddedFontInfo时崩溃,主要是因为未正确初始化TTLOADINFO的pusRefStr缓冲区:

  • 该函数需要先调用一次获取所需的缓冲区大小(usRefStrSize),再分配对应内存后进行二次调用;直接传入pusRefStr = nullptr会导致函数尝试向空指针写入数据,触发访问违规崩溃。

其他潜在问题

  1. 异常信息拼接错误:抛出runtime_error时,直接将ULONG类型的错误码与字符串拼接会导致指针运算错误,需先转换为字符串。
  2. 临时文件残留:原代码未删除生成的临时文件,会留下垃圾文件。
  3. 资源泄漏风险:动态分配的内存未显式释放。

修复后的完整代码

#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;
}

关键修复点说明

  1. 分两次调用TTGetEmbeddedFontInfo:
    • 第一次调用时pusRefStr设为nullptr,函数会返回所需的字符串缓冲区大小到usRefStrSize;
    • 根据获取的大小分配内存后,二次调用函数即可正确读取字体来源信息。
  2. 修复异常信息拼接:使用ostringstream将错误码转换为字符串后再拼接,避免指针运算错误。
  3. 完善资源清理:新增临时文件删除、动态内存释放的代码,避免资源泄漏。

内容的提问来源于stack exchange,提问作者jeremie bergeron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 07:24:51