【问题标题】:WinAPI LoadImage to a button have border but LoadBitmap doesn'tWinAPI LoadImage 到按钮有边框但 LoadBitmap 没有
【发布时间】:2019-02-08 17:38:07
【问题描述】:

我的应用是DLL,我将其注入到(游戏)进程中。

当我使用LoadBitmap() 并使用MAKEINTRESOURCE(IMAGE_RESOURCE_NAME)

像这样:

MyImage = LoadBitmap(hInstance, MAKEINTRESOURCE(IMAGE_RESOURCE_NAME))

SendMessage(MyButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)MyImage);

创建按钮代码:

MyButton = CreateWindow("BUTTON", "My Button", WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_FLAT, 17, 18, 110, 30, hwnd, (HMENU)ButtonId, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);

当我将DLL 注入任何应用程序游戏时,

LoadBitmap() 有效。 我想是因为当我将DLL 注入游戏时,它不会从Resources 加载并且图像不会出现。所以我无法使用来自ResourcesLoadBitmap。不知何故,Resources 不与DLL 数据一起去Game 并且游戏找不到资源所以它找不到图像。

另外,我尝试使用 磁盘文件 中的LoadImage()。这样它就起作用了,图像出现在按钮上。

当我将它注入到 任何应用程序(如记事本)中时,它会显示为:

(这就是我想要的样子)

但是当我将DLL 注入游戏时,按钮会出现在边框和3D 效果中:

经过大量搜索,我认为我正在注入的 游戏 不会将 视觉样式 应用于我的 DLL GUI Window按钮 出现在 Classy 外观中,边框和 3D 效果。甚至BS_FLAT 也不适用于按钮。

这是我正在使用的完整代码:

#include "stdafx.h"
#include "Process.h"
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <tchar.h>
#include "resource.h"

HINSTANCE hInstance;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc = { 0 };

    HWND MainHwnd;

    MSG Msg;

    wc.cbSize = sizeof(wc);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;

    wc.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(30, 30, 30)));
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;

    wc.lpszMenuName = NULL;
    wc.lpszClassName = "My Application";

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    wc.hCursor = LoadCursor(NULL, IDC_ARROW);


    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, std::to_string(GetLastError()).c_str(), "RegisterClassEx!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    MainHwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "Application",
        "My Application",
        WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 280,
        NULL, NULL, hInstance, NULL);

    if (MainHwnd == NULL)
    {
        MessageBox(NULL, std::to_string(GetLastError()).c_str(), "CreateWindow!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(MainHwnd, nCmdShow);
    UpdateWindow(MainHwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;

}

int MyButtonId = 1000;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE: {

        HWND MyButton;
        HBITMAP MyImage;

        MyButton = CreateWindow("BUTTON", "A Button Text", WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_FLAT, 17, 18, 110, 30, hwnd, (HMENU)MyButtonId, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);

        ///////// --->

        // Here I'm using one of these :

        // Using LoadImage()
        MyImage = (HBITMAP)LoadImage(hInstance, "UI\\myimage.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_LOADFROMFILE);

        // Using LoadBitmap() | My_Bitmap is an image resource name
        MyImage = LoadBitmap(hInstance, MAKEINTRESOURCE(My_Bitmap));

        ///////// <---

        SendMessage(MyButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)MyImage);

        break;
    }

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

unsigned long __stdcall Win_Thread(LPVOID Param)
{
    WinMain(NULL, NULL, NULL, 1);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        // Set hInstance to hModule
        hInstance = hModule;

        CreateThread(0, 0, LPTHREAD_START_ROUTINE(Win_Thread), hModule, 0, 0);
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

我想我有两个选择。

1.尝试让游戏从Resources 找到我的图像,并使用Resources 中的LoadBitmap()。所以按钮不会有边框和3d效果。

2.继续使用磁盘文件中的LoadImage(),并尝试隐藏边框和3d 效果,例如为我的DLL GUI 启用视觉样式

不幸的是,我无法做到这些,也不知道该怎么做,我正在搜索整个互联网,但没有找到任何相关信息。

我怎么能做到这一点,有什么想法吗?

【问题讨论】:

标签: c++ winapi win32gui


【解决方案1】:

很遗憾,您对LoadBitmap 的追求是一次狙击。视觉样式是导致不同外观的唯一因素。即使您获得了使用资源的代码,除非您启用视觉样式,否则您仍然会看到错误的外观。

当主应用程序不使用它们时,MSDN 有一个专门用于在插件 DLL 中使用视觉样式的参考:

要点是您需要使用ISOLATION_AWARE_ENABLED 宏并为视觉样式显示您的DLL。

您还需要致电InitCommonControlsEx。上述文档的其他几个部分都提到了这一点。对于平面按钮,传递 ICC_STANDARD_CLASSES 标志(在结构内)。


您的代码中确实存在一些错误,这些错误可能会阻止视觉样式正确激活,即使您执行了显示和隔离。

  1. 您的 DLL 不应具有 WinMain 函数。让一个函数完成从WinMain 和 DLL 线程调用的所有工作,而不是让 DLL 线程调用WinMain。这本身并没有错,只是风格不好,但它导致了下一个错误,这是一个更大的问题:

  2. 您的hInstance 参数隐藏了全局hInstance 变量,导致wc.hInstance 的值错误。如果WinMainDllMain 都设置了全局变量,然后所有其余代码都使用了全局变量,那么你就不会遇到这个问题。但修复它需要在 EXE 而不是 DLL 中运行代码,这意味着从 DLL 线程中删除对WinMain 的调用。

【讨论】:

  • 是的,视觉风格导致了这个问题。我尝试按照您提供的链接为我的 DLL 启用视觉样式,但完全没有运气。以正确的方式面临清单(如何将其添加到 dll)的许多问题。但即使经过 3 个小时的尝试和搜索,我也无法做到这一点。如果我理解你提到的两个错误。我的代码应该是这样的:pastebin.com/phNufk11如果你能帮忙,请给我一些代码示例,谢谢!
  • @AmrSubZero:将主函数重命名为 DllMain 也是一个坏主意,因为 (1) 现在您忽略了 reason_for_call 参数,并且 (2) 现在您正在做所有这些工作在装载机锁内。
  • 我无法理解 Your DLL should not have a WinMain function. Let there be one function that gets called from WinMain 当我不应该有 WinMain 函数时,我怎么能从 WinMain 调用 Function?我无法理解这对夫妇的错误。第二个错误我试图在dllMain(HINSTANCE hModule, ...) 中将全局static HINSTANCE mainInstance 设置为hModule,然后在我的其余代码中使用mainInstance。那是对的吗?如果可能的话,你能给我一个代码示例吗?
  • @AmrSubZero:您对mainInstance 所做的更改很好。对于第一个更改,您只需将WinMain 重命名为非魔术函数名称,例如void DoAllTheThings(void)。并从您调用WinMain() 的线程过程中调用它。然后对于您的 EXE,您将拥有一个新的 WinMain,它执行 mainInstance = hInstance;,然后调用 DoAllTheThings()。 EXE 路径:WinMain() -> DoAllTheThings()。 DLL 路径:DllMain() -> CreateThread() -> Win_Thread -> DoAllTheThings()。设置mainInstance的部分在EXE和DLL中需要不同。
  • 先生,您没看清楚我的意思,我无法控制EXE 这是一个游戏,我只用WinAPI GUI 创建一个DLL 并将其注入游戏@987654363 @ 然后DLL GUI 在注入后出现。我无法控制 exe/游戏。出现问题是因为游戏无法以某种方式将visual styles 应用于我的DLL GUI。所以我需要为我的 DLL GUI 启用视觉样式。我不能那样做!很难理解如何将Manifest 文件添加到我的项目等中。
猜你喜欢
  • 2013-01-08
  • 2013-09-01
  • 2013-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
相关资源
最近更新 更多