WinAPI - 从 DLL 加载资源

Posted

技术标签:

【中文标题】WinAPI - 从 DLL 加载资源【英文标题】:WinAPI - Loading Ressources from a DLL 【发布时间】:2018-04-24 05:52:11 【问题描述】:

我正在使用 VisualStudio2017 开发适用于 Windows 7 的应用程序。此应用程序希望有从 DLL 加载的特殊游标。所以首先我创建了一个 DLL 并添加了以下 .rc 文件:

BM_CURSOR_GRAB          CURSOR               "./grab.cur"
BM_CURSOR_GRABBING      CURSOR               "./grabbing.cur"

BM_CURSOR_GRAB 和 BM_CURSOR_GRABBING 在头文件中定义为:

#define BM_CURSOR_GRAB     100
#define BM_CURSOR_GRABBING 101

我编译了 DLL - 可以使用并使用 ResourceEditor.exe 检查它我的资源包括在内:

现在“非工作”部分开始了。我的应用程序想要加载光标,但 FindResource 没有找到它。这是我的代码:

HMODULE dll    = LoadLibrary("BenjaMiniRessources.dll");
HRSRC   hRes   = FindResource(dll, MAKEINTRESOURCE(100), RT_CURSOR);
DWORD   dwSize = SizeofResource(dll,hRes);
HGLOBAL hMem   = LoadResource(dll, hRes);
LPBYTE  pBytes = (LPBYTE)LockResource(hMem);

Cursor = CreateIconFromResource(pBytes, dwSize, false, 0x00030000);

我做错了什么?

【问题讨论】:

“我做错了什么?” - 不检查失败,不调用GetLastError() 您是否尝试过枚举资源。 RT_CURSOR 资源不直接包含在资源部分中,iirc,它们通常作为子资源包含在 RT_GROUP_CURSOR 中。 【参考方案1】:

只需将LoadCursor 与相应的hInstance 一起使用。这适用于 MFC 和我的所有 Windows 应用程序。

【讨论】:

好的!那行得通……但是怎么知道我可以将模块用作实例?!啊……WinAPI……谢谢! @DragonEgg 根据Win32 Data Types 文档:“HMODULE 和 HINSTANCE 在当前版本的 Windows 中是相同的,但在 16 位 Windows 中表示不同的东西。”另请参阅What is the difference between HINSTANCE and HMODULE? 在 Raymond Chen 的博客中。【参考方案2】:

当你包括 BM_CURSOR_GRAB CURSOR "./grab.cur" 到 rc 文件的行,在生成的 PE 中将是 (BM_CURSOR_GRAB, RT_GROUP_CURSOR) 资源。所以类型将是RT_GROUP_CURSOR,但不是RT_CURSOR

那么您需要调用LookupIconIdFromDirectoryEx 函数来获取最适合指定大小的游标名称(id)。

在此之后,您需要再次加载资源 - 已经是 RT_CURSOR,其 id 从 LookupIconIdFromDirectoryEx 返回。

最后在电话CreateIconFromResourceEx中使用它。

但是,更简单地使用 IMAGE_CURSOR 资源类型调用 LoadImage

(HCURSOR)LoadImageW(hmod, MAKEINTRESOURCE(BM_CURSOR_GRAB), IMAGE_CURSOR, 
            0, 0, 0);

例如,用于使用实际资源大小。还是您想使用默认系统光标大小:

(HCURSOR)LoadImageW(hmod, MAKEINTRESOURCE(BM_CURSOR_GRAB), IMAGE_CURSOR, 
                GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR), 0);

(HCURSOR)LoadImageW(hmod, MAKEINTRESOURCE(BM_CURSOR_GRAB), IMAGE_CURSOR, 
                    0, 0, LR_DEFAULTSIZE);

或者干脆

LoadCursorW(hmod, MAKEINTRESOURCE(BM_CURSOR_GRAB));

最后一次呼叫内部呼叫LoadImageW 带有LR_DEFAULTSIZE | LR_SHARED 标志

但是直接访问资源的代码(LoadImageW 内部执行)

ULONG GetResourcePointer(void** ppv, ULONG* pcb, HMODULE hModule, PCWSTR lpName, PCWSTR lpType)

    if (HRSRC hResource = FindResource(hModule, lpName, lpType))
    
        if (HGLOBAL hResData = LoadResource(hModule, hResource))
        
            if (PVOID pv = LockResource(hResData))
            
                if (ULONG cb = SizeofResource(hModule, hResource))
                
                    *ppv = pv, *pcb = cb;

                    return NOERROR;
                
            
        
    

    return GetLastError();


        ULONG err = NOERROR;
        HCURSOR hcur;

        if (HMODULE hmod = (HMODULE)LoadLibraryW(L"*"))
        
            ULONG cb, err;
            PVOID pv;
            if (!(err = GetResourcePointer(&pv, &cb, hmod, MAKEINTRESOURCE(BM_CURSOR_GRAB), RT_GROUP_CURSOR)))
            
                if (int nID = LookupIconIdFromDirectoryEx((PBYTE)pv, FALSE, 0, 0, LR_DEFAULTCOLOR))
                

                    if (!(err = GetResourcePointer(&pv, &cb, hmod, MAKEINTRESOURCE(nID), RT_CURSOR)))
                    
                        if (!(hcur = (HCURSOR)CreateIconFromResourceEx((PBYTE)pv, cb,
                            FALSE, 0x00030000, 0, 0, LR_DEFAULTCOLOR|LR_DEFAULTSIZE)))
                        
                            err = GetLastError();
                        
                    
                
                else
                
                    err = GetLastError();
                
            
        

【讨论】:

这也有效...但它相当...复杂 D:而且...我不知道如何解决这个问题...您的答案比我的问题更接近第一个 - 但我将第一个标记为已解决,因为它更易于使用。 @DragonEgg - 但我一开始就说LoadImageW 是最适合您的解决方案。 LoadCursorWLoadImageW 的特例,如果您同意使用光标的默认大小。我提供的代码 - 仅用于演示【参考方案3】:

各种错误:

    如果LoadLibrary("BenjaMiniRessources.dll")中的返回值为NULL,则系统找不到指定名称BenjaMiniRessources.dll的库; 如果FindResource(dll, MAKEINTRESOURCE(100), RT_CURSOR) 中的返回值为NULL,则系统在BenjaMiniRessources.dll 中找不到RT_CURSOR 类型的资源。 (可能此资源的类型是 RT_GROUP_CURSOR 或 RT_ANICURSOR);

    如果返回值为NULL,则错误在字符串SizeofResource(dll,hRes),中;

    问题出在LoadResource(dll, hRes),如果这个函数返回NULL;

    LockResource(hMem)中的故障,如果该函数返回NULL;

    错误在字符串CreateIconFromResource(pBytes, dwSize, false, 0x00030000)中。

你可以通过调试器下的代码,看看程序的哪一行最先返回错误。

【讨论】:

如果 LoadLibrary 中的返回值小于 32; - 看起来你混淆了 LoadLibraryShellExecute。失败时的LoadLibrary 恰好返回 0,并且返回值必须仅与 0 进行比较(而不是 32)。可能不是this resource is RT_GROUP_CURSOR,而是准确的 对不起,我把 LoadLibrary() 和 LoadModule() 弄混了。

以上是关于WinAPI - 从 DLL 加载资源的主要内容,如果未能解决你的问题,请参考以下文章

损坏的 winapi 可执行清单

无法使用 LoadIcon WINAPI 从资源中加载图标

如何使用 WinApi/user32.dll 或类似的东西远程执行 ListBox 的 ListBox1_DoubleClick 事件?

无法加载多个位图 - WinAPI [重复]

WinAPI 在按钮单击时没有任何反应

WinAPI 的 Groupbox 字体问题