Windows程序的manifest是嵌入PE文件资源节中的RT_MANIFEST类型二进制资源,需用FindResource/LoadResource等API读取UTF-16 LE格式XML数据,而非fopen直接读取磁盘文件。

Manifest 文件不是普通文本,不能直接 fopen 读取
Windows 程序的 manifest 是嵌入在 PE 文件资源节中的二进制资源(类型为 RT_MANIFEST),不是磁盘上独立的 .manifest 文件。即使你看到同名文件,系统默认优先加载嵌入 manifest —— 所以用 fopen("app.exe.manifest", "r") 读到的很可能根本没被系统加载过,信息无意义。
正确路径是调用 Windows API 从当前模块或指定模块中提取资源:
- 用
FindResource+LoadResource+LockResource获取原始 XML 字节流(注意:含 UTF-16 BOM,且是宽字符编码) - 资源 ID 通常是
1(对于主程序 manifest)或2(对于 DLL 的 manifest),但更稳妥的是用MAKEINTRESOURCE(IDI_MANIFEST)(需#define WIN32_LEAN_AND_MEAN后包含windows.h) - 返回的 buffer 是
LPVOID,内容为以\0\0结尾的 UTF-16 LE 数据,不能直接当char*printf
用 GetManifestResourceSize + LoadString 处理失败的常见原因
有人尝试用 LoadString 读 manifest —— 这是错的。LoadString 只适用于 RT_STRING 类型资源,对 RT_MANIFEST 会返回 0 并设 GetLastError() 为 ERROR_RESOURCE_TYPE_NOT_FOUND。
真正该用的组合是:
立即学习“C++免费学习笔记(深入)”;
-
FindResource(hModule, MAKEINTRESOURCE(1), RT_MANIFEST)—— 注意第三个参数必须是宏RT_MANIFEST(值为 24),不能写字符串"RT_MANIFEST" -
SizeofResource获取准确字节数(不是字符数),这个值决定了后续内存分配大小 - 若
FindResource返回 NULL,常见原因:hModule传错(比如用了GetModuleHandle(NULL)但程序是 DLL)、manifest 根本未嵌入、或资源被 UPX 等加壳工具剥离
解析 UTF-16 manifest XML 时容易崩的点
拿到的 manifest 数据是裸的 UTF-16 LE 字节流,开头有 BOM(0xFF 0xFE),结尾是双 \0(即两个 0x00 字节)。如果直接用 wprintf(L"%s", (LPCWSTR)buf),可能因中间出现 L'\0' 提前截断 —— 因为 XML 里允许有 null 字符(虽然实际 manifest 不会,但安全起见应按长度打印)。
安全做法:
- 用
WideCharToMultiByte(CP_UTF8, ...)转成 UTF-8 字符串再输出,避免控制台乱码(尤其 Win10+ 默认 cmd 支持 UTF-8) - 或用
std::wstring_view((const wchar_t*)buf, size_in_bytes / sizeof(wchar_t) - 1)剔除末尾双 null 再处理 - 别用
std::wifstream打开 —— 它期望文件,不是内存块;也别用tinyxml2直接 parse 内存指针,要先转成std::string或std::wstring
调试时快速验证 manifest 是否生效的土办法
光读出来不等于系统加载了它。最直接的验证方式是看进程是否启用了 uiAccess="true" 或高 DPI 感知模式 —— 但这些需要运行时检测。更轻量的办法:
- 用
sigcheck -m your.exe(Sysinternals 工具),输出里带Manifest: present行就说明嵌入成功 - 用
ResourceHacker打开 exe,展开Manifest节点,能看见 XML 内容才代表可读 - 代码里调用
IsProcessDPIAware()或GetProcessDpiAwareness(),结果和 manifest 中dpiAware设置一致,才是真生效
嵌入 manifest 后必须重建签名(如果程序有 Authenticode 签名),否则部分安全策略下会被拒绝加载 —— 这个细节几乎没人提,但生产环境出问题时往往卡在这里。



















