我的应用程序是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
的Resources
。不知怎的,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
找到我的图像并使用来自LoadBitmap()
的Resources
。所以按钮不会有边框和3D效果。
2.继续使用磁盘文件中的LoadImage()
,并尝试隐藏边框和3d效果,例如为我的DLL GUI启用视觉样式。
不幸的是我不能做任何这些,也不知道该怎么做,我正在搜索整个互联网,但没有找到任何关于那个。
我怎么能实现这一点,任何想法?
不幸的是,你对LoadBitmap
的追求是一次狙击。视觉样式是导致不同外观的唯一因素。即使您获得使用资源的代码,除非启用视觉样式,否则您仍会出现错误的外观。
MSDN有一个专门用于在插件DLL中使用Visual Styles的参考,当主应用程序不使用它们时:
要点是你需要使用ISOLATION_AWARE_ENABLED
宏并显示你的DLL用于视觉样式。
你还需要打电话给InitCommonControlsEx
。在上述文件的其他几个部分中提到了这一点。对于平面按钮,传递ICC_STANDARD_CLASSES
标志(在结构内)。
您的代码中确实存在一些错误,即使您执行清单和隔离,这些错误也可能会阻止视觉样式正确激活。
WinMain
函数。让一个函数完成从WinMain
和DLL线程调用的所有工作,而不是让DLL线程调用WinMain
。这本身并没有错,只是糟糕的风格,但它导致了下一个错误,这是一个更大的问题:hInstance
参数隐藏了全局hInstance
变量,导致wc.hInstance
的值不正确。如果WinMain
和DllMain
都设置了全局变量,然后所有其余的代码都使用了全局变量,那么你就不会遇到这个问题。但修复它需要在EXE而不是DLL中运行代码,这意味着从DLL线程中删除调用到WinMain
。