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

含特殊字符路径下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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:00:21