WinAPI LoadImage到按钮有边框,但LoadBitmap没有边框

时间:2018-09-03 16:32:47

标签: c++ winapi win32gui

我的应用是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);

当我将LoadBitmap()注入任何应用程序,但游戏时,

DLL起作用。 我认为是因为当我将DLL注入游戏时,它不会从Resources加载,并且图像也不会出现。因此我无法使用LoadBitmap中的ResourcesResources不会与DLL数据一起进入Game,并且游戏找不到资源,因此找不到图像。

因此,我还是尝试使用磁盘文件中的LoadImage()。并以这种方式工作,并且图像出现在按钮上。

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

(这就是我想要的样子)

enter image description here

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

enter image description here

经过大量搜索,我认为我要注入的游戏对我的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中找到我的图像,并使用LoadBitmap()中的Resources。因此该按钮将不会具有边框和3d效果。

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

不幸的是,我什么也做不了,也不知道该怎么做,我正在整个互联网上搜索,却一无所获。

任何想法,我怎么能实现?

1 个答案:

答案 0 :(得分:1)

不幸的是,您对LoadBitmap的追求是一场狙击。视觉样式是导致外观不同的唯一原因。即使您获得了使用资源的代码,但除非启用视觉样式,否则外观还是错误的。

当主应用程序不使用Visual Styles时,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的调用。