获取 Dll 模块的加载字符串资源

本文介绍了如何通过LoadStringW函数从DLL模块加载字符串资源,以及注意事项,特别是关于正确处理缓冲区大小以防止安全问题。作者还提供了C++示例代码演示了该功能的使用。
摘要由CSDN通过智能技术生成

概要

获取 Dll 模块中加载的字符串资源,可以通过 LoadString 实现。这个函数可以用于在不同版本索引系统字符串,对于一些根据名称操作系统菜单的功能,可以使用这种方法动态获取系统模块当前的加载字符串。

LoadStringW 从与指定模块关联的可执行文件加载字符串资源,并将字符串复制到具有终止 null 字符的缓冲区中,或返回指向字符串资源本身的只读指针。

int LoadStringW(
  [in, optional] HINSTANCE hInstance,
  [in]           UINT      uID,
  [out]          LPWSTR    lpBuffer,
  [in]           int       cchBufferMax
);

参数

  • [in, optional] hInstance
  • 类型: HINSTANCE

                其可执行文件包含字符串资源的模块实例的句柄。 若要获取应用程序本身的句柄,请使用 NULL 调用 GetModuleHandle 函数。

  • [in] uID
  • 类型: UINT

                要加载的字符串的标识符。

  • [out] lpBuffer
  • 类型: LPTSTR

                如果 cchBufferMax 为非零) ,则接收字符串的缓冲区 (如果 cchBufferMax 为零) ,则为指向字符串资源本身 (只读指针,必须具有足够的长度,才能将指针保留 8 个字节) 。

  • [in] cchBufferMax
  • 类型: int

                缓冲区的大小(以字符为单位)。 如果字符串的长度超过指定的字符数,则字符串将被截断并以 null 结尾。 如果此参数为 0,则 lpBuffer 会收到指向字符串资源本身的只读指针。

返回值
类型: int

如果函数成功,则返回值为以下值之一:

如果 cchBufferMax 为非零(不包括终止 null 字符),则复制到缓冲区中的字符数 (
如果 cchBufferMax 为零) ,则 lpBuffer 指向的字符串资源中的字符数 ( 字符串资源不保证在模块的资源表中以 null 结尾),可以使用此值来确定字符串资源结束的位置。
如果字符串资源不存在,则为零。
要获得更多的错误信息,请调用 GetLastError。

注解

如果将 0 传递给 cchBufferMax 以返回指向 lpBuffer 参数中字符串资源的只读指针,请使用返回值中的字符数来确定字符串资源的长度。 在模块的资源表中,字符串资源不一定以 null 结尾。 但是,资源表可以包含空字符。 字符串资源存储在 16 个字符串块中,块中的任何空槽都用 null 字符指示。

安全备注

错误使用此函数可能会危及应用程序的安全性。 不正确的使用包括在 nBufferMax 参数中指定错误的大小。 例如,如果 lpBuffer 指向声明为 TCHAR szBuffer[100] 的缓冲区 szBuffer,则 sizeof (szBuffer) 会提供缓冲区的大小(以字节为单位),这可能导致函数的 Unicode 版本的缓冲区溢出。 缓冲区溢出情况是应用程序中许多安全问题的原因。 在这种情况下,使用 sizeof(szBuffer)/sizeof(TCHAR) 或 sizeof(szBuffer)/sizeof(szBuffer[0]) 会提供缓冲区的适当大小。

样例代码

#include <iostream>
#include <windows.h>

int main()
{
    setlocale(NULL, "zh-CN");
    HMODULE hDll = NULL;
    hDll = LoadLibraryW(L"shell32.dll");
    int dwLength = 0;
    UINT uID = 0;
    WCHAR wsBuffer[1024] = { 0 };
    for (int i = 0; i < 80000; i++)
    {
        memset(wsBuffer, 0, 1024 * sizeof(WCHAR));
        uID++;
        dwLength = LoadStringW(hDll, uID, wsBuffer, 1024);
        if (!dwLength) continue;
        printf("ID:[0x%01X], Name:[%ws]\n", uID, wsBuffer);
    }
    return 0;
}

改进代码(参考 [https://www.cnblogs.com/lanuage/p/7725690.html]):

  • 加载 DLL 作为资源: 使用 LoadLibraryExW 函数,并指定 LOAD_LIBRARY_AS_IMAGE_RESOURCELOAD_LIBRARY_AS_DATAFILE 标志,只将其作为资源加载。
  • 枚举字符串资源: 使用 EnumResourceNamesW 函数枚举字符串资源,回调函数 EnumStringTables 处理每个字符串资源。避免了不知道资源数量情况下,需要固定循环次数的弊端。
  • 释放加载的 DLL 模块: 使用 FreeLibrary 释放加载的模块。
#include <iostream>
#include <windows.h>
#include <locale>

// 回调函数:枚举字符串资源名称
BOOL CALLBACK EnumStringTables(HMODULE hModule, LPCWSTR lpType, LPWSTR lpName, LONG_PTR lParam) {
    WCHAR wsBuffer[1024] = { 0 };
    for (int i = 0; i < 16; ++i) {
        int stringID = ((int)lpName - 1) * 16 + i;
        int dwLength = LoadStringW(hModule, stringID, wsBuffer, 1024);
        if (dwLength > 0) {
            wprintf(L"ID:[0x%04X], Name:[%ws]\n", stringID, wsBuffer);
        }
    }
    return TRUE;
}

int main() {
    setlocale(LC_ALL, "zh-CN");

    // 加载 DLL 作为资源
    HMODULE hDll = LoadLibraryExW(L"shell32.dll", NULL, 
        LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);

    if (hDll == NULL) {
        std::wcerr << L"Failed to load library" << std::endl;
        return 1;
    }

    // 枚举字符串资源
    // https://learn.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-enumresourcenamesw
    if (!EnumResourceNamesW(hDll, RT_STRING, EnumStringTables, 0)) {
        std::wcerr << L"Failed to enumerate string resources" << std::endl;
    }

    // 释放加载的 DLL 模块
    FreeLibrary(hDll);
    return 0;
}

编译运行结果如图:


更新于:2024.01.13,2024.06.27.

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涟幽516

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值