含特殊字符路径下Win32 MapAndLoad函数失效问题咨询
解决MapAndLoad()处理含非ASCII字符路径的问题
当路径包含ł这类非ASCII字符时,MapAndLoad()函数会失效——因为这个Win32 API仅支持ASCII格式的路径参数,无法正确解析包含非ASCII字符的路径。结合你提供的代码和目录遍历输出(非ASCII字符被解析为负数字节,本质是UTF-8编码与ANSI编码不匹配导致的乱码),可以通过以下两种方案解决:
方案一:借助临时文件中转
既然MapAndLoad()只能处理ASCII路径,我们可以先把目标文件复制到纯ASCII路径的临时文件中,再对临时文件调用MapAndLoad(),处理完成后删除临时文件。
示例代码:
#include <windows.h> #include <shlwapi.h> #include <fmt/core.h> #pragma comment(lib, "shlwapi.lib") bool ProcessDllWithNonAsciiPath(const std::wstring& wpath) { // 获取临时目录 WCHAR tempPath[MAX_PATH]; if (!GetTempPathW(MAX_PATH, tempPath)) { fmt::println("GetTempPathW failed"); return false; } // 生成纯ASCII的临时文件名 WCHAR tempFile[MAX_PATH]; if (!GetTempFileNameW(tempPath, L"PE_", 0, tempFile)) { fmt::println("GetTempFileNameW failed"); return false; } // 复制原文件到临时文件 if (!CopyFileW(wpath.c_str(), tempFile, FALSE)) { fmt::println("CopyFileW failed"); DeleteFileW(tempFile); return false; } // 将临时文件路径转换为ASCII格式 char tempFileA[MAX_PATH]; if (!WideCharToMultiByte(CP_ACP, 0, tempFile, -1, tempFileA, MAX_PATH, nullptr, nullptr)) { fmt::println("WideCharToMultiByte failed"); DeleteFileW(tempFile); return false; } // 调用MapAndLoad处理临时文件 LOADED_IMAGE LoadedImage = {0}; if (!MapAndLoad(tempFileA, nullptr, &LoadedImage, TRUE, TRUE)) { fmt::println("MapAndLoad failed"); DeleteFileW(tempFile); return false; } // 复用原有PE解析逻辑 DWORD cDirSize = 0; _IMAGE_EXPORT_DIRECTORY* ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*) ImageDirectoryEntryToData(LoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize); if (ImageExportDirectory == nullptr) { fmt::println("ImageExportDirectory == nullptr"); UnMapAndLoad(&LoadedImage); DeleteFileW(tempFile); return false; } DWORD* dNameRVAs = (DWORD*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, ImageExportDirectory->AddressOfNames, nullptr); for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++) { char* sName = (char*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, dNameRVAs[i], nullptr); fmt::println("{}", sName); } // 清理资源 UnMapAndLoad(&LoadedImage); DeleteFileW(tempFile); return true; } // 调用示例 int main() { std::wstring wpath = L"C:\\Users\\user\\łał\\lib\\native.dll"; ProcessDllWithNonAsciiPath(wpath); return 0; }
方案二:手动实现MapAndLoad核心逻辑
MapAndLoad()的本质是将PE文件映射到内存并初始化LOADED_IMAGE结构。我们可以直接用宽字符API完成文件打开和内存映射,跳过MapAndLoad()对ASCII路径的限制,后续PE解析逻辑完全复用原有代码。
示例代码:
#include <windows.h> #include <fmt/core.h> bool ProcessDllWithNonAsciiPath(const std::wstring& wpath) { LOADED_IMAGE LoadedImage = {0}; // 用宽字符API打开目标文件 HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) { fmt::println("CreateFileW failed"); return false; } // 创建文件映射 HANDLE hMap = CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); if (hMap == nullptr) { fmt::println("CreateFileMappingW failed"); CloseHandle(hFile); return false; } // 将文件映射到内存 LPVOID mappedAddr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if (mappedAddr == nullptr) { fmt::println("MapViewOfFile failed"); CloseHandle(hMap); CloseHandle(hFile); return false; } // 初始化LOADED_IMAGE结构,模拟MapAndLoad的行为 LoadedImage.FileHeader = (PIMAGE_DOS_HEADER)mappedAddr; if (LoadedImage.FileHeader->e_magic != IMAGE_DOS_SIGNATURE) { fmt::println("Invalid DOS signature"); UnmapViewOfFile(mappedAddr); CloseHandle(hMap); CloseHandle(hFile); return false; } PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)mappedAddr + LoadedImage.FileHeader->e_lfanew); if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) { fmt::println("Invalid NT signature"); UnmapViewOfFile(mappedAddr); CloseHandle(hMap); CloseHandle(hFile); return false; } LoadedImage.MappedAddress = ntHeaders; LoadedImage.hFile = hFile; LoadedImage.hFileMapping = hMap; // 复用原有PE解析逻辑 DWORD cDirSize = 0; _IMAGE_EXPORT_DIRECTORY* ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*) ImageDirectoryEntryToData(LoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize); if (ImageExportDirectory == nullptr) { fmt::println("ImageExportDirectory == nullptr"); UnmapViewOfFile(mappedAddr); CloseHandle(hMap); CloseHandle(hFile); return false; } DWORD* dNameRVAs = (DWORD*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, ImageExportDirectory->AddressOfNames, nullptr); for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++) { char* sName = (char*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, dNameRVAs[i], nullptr); fmt::println("{}", sName); } // 手动清理资源(不能用UnMapAndLoad) UnmapViewOfFile(mappedAddr); CloseHandle(hMap); CloseHandle(hFile); return true; } // 调用示例 int main() { std::wstring wpath = L"C:\\Users\\user\\łał\\lib\\native.dll"; ProcessDllWithNonAsciiPath(wpath); return 0; }
补充:目录遍历的正确姿势
你之前用FindFirstFileA()遍历目录出现乱码,是因为std::string存储的UTF-8路径被转成系统ANSI编码,导致非ASCII字符解析错误。正确做法是使用宽字符版本API:
WIN32_FIND_DATAW findData; HANDLE hFind = FindFirstFileW((wpath + L"\\*").c_str(), &findData); if (hFind != INVALID_HANDLE_VALUE) { do { if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { fmt::println(L"dir: {}", findData.cFileName); fmt::println("--------------"); } } while (FindNextFileW(hFind, &findData)); FindClose(hFind); }
内容的提问来源于stack exchange,提问作者Kaspek
相关产品推荐
相关产品推荐

