gpt4 book ai didi

c++ - 从 dll 使用 gdi+ 时进程不退出

转载 作者:行者123 更新时间:2023-11-28 04:28:29 28 4
gpt4 key购买 nike

我想将 GDI+ 与本身不提供 GDI+ 的 Pascal 脚本一起使用,但我不知道为什么当使用 dll(共享)时,即使窗口被销毁,进程也不会退出,我的意思是说我尽管没有任何窗口可供查看,但仍然可以看到从任务管理器运行的进程。该进程保持空闲状态,即没有任何资源使用

在我的 dll 中,对于每个新的 hwnd,我正在 Hook 我自己的 wndproc 并且在 WM_Paint 消息上我正在绘制到目前为止要求绘制的指定对象

我正在导出用于绘制和编译 32 位的 DrawRectangle 符号
我的 dll 是

#include <Windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include <objidl.h>
#pragma comment(lib, "Gdiplus.lib")

#include <functional>
#include <map>
#include <memory>
#include <vector>

#define DLL_EXPORT(RETURN_TYPE) \
extern "C" __declspec(dllexport) RETURN_TYPE __stdcall

void msg(const char *str) { MessageBoxA(nullptr, str, "Message", 0); }
void msg(const wchar_t *str) { MessageBoxW(nullptr, str, L"Message", 0); }

class _GdiManager {
public:
_GdiManager() {
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
}
~_GdiManager() { GdiplusShutdown(gdiplusToken); }

private:
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
} GdiManager;

class DrawableObject {
public:
virtual void draw(Gdiplus::Graphics &Graphics) = 0;
virtual ~DrawableObject() = default;
};

namespace DrawableObjects {
class Rectangle : public DrawableObject {
public:
Rectangle(ARGB Color, int X, int Y, int Width, int Height)
: m_X{X}, m_Y{Y}, m_Width{Width}, m_Height{Height}, m_Brush{Color} {}
void draw(Gdiplus::Graphics &graphics) override {
graphics.FillRectangle(&m_Brush, m_X, m_Y, m_Width, m_Height);
}

private:
int m_X, m_Y, m_Width, m_Height;
Gdiplus::SolidBrush m_Brush;
};

} // namespace DrawableObjects

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

class Painter {
public:
Painter(HWND hWnd) : m_WindowHandle{hWnd}, m_Graphics{hWnd} {
m_OriginalWindowProc = (WNDPROC)GetWindowLongW(m_WindowHandle, GWL_WNDPROC);
SetWindowLongW(m_WindowHandle, GWL_WNDPROC, (LONG)MasterWindowProc);
}

LRESULT CallOriginalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
return CallWindowProcW(m_OriginalWindowProc, hwnd, uMsg, wParam, lParam);
}

LRESULT Paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_PAINT) {
for (auto &o : m_Objects)
o->draw(m_Graphics);
} else if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
}
return 0;
}

std::vector<std::unique_ptr<DrawableObject>> &Objects() { return m_Objects; }

private:
HWND m_WindowHandle;
Gdiplus::Graphics m_Graphics;
WNDPROC m_OriginalWindowProc;
std::vector<std::unique_ptr<DrawableObject>> m_Objects;
};

std::map<HWND, std::unique_ptr<Painter>> windowPaint;

LRESULT MasterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto &p = windowPaint[hwnd];
auto r = p->CallOriginalWndProc(hwnd, uMsg, wParam, lParam);
p->Paint(hwnd, uMsg, wParam, lParam);
return r;
}

auto &insertPainter(HWND hwnd) {
auto &my_painter = windowPaint[hwnd];
if (!my_painter)
my_painter = std::make_unique<Painter>(hwnd);
return my_painter;
}

DLL_EXPORT(int)
DrawRectangle(HWND hwnd, ARGB LineColor, int startX, int startY, int width,
int height) {
auto &my_painter = insertPainter(hwnd);
my_painter->Objects().push_back(std::make_unique<DrawableObjects::Rectangle>(
LineColor, startX, startY, width, height));
return 0;
}

宿主程序:

//#include "gdi.cpp"
#include <ObjIdl.h>
#include <Windows.h>
#include <cassert>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) {
HWND hWnd;
MSG msg;
WNDCLASS wndClass;

wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("GettingStarted");

RegisterClass(&wndClass);

hWnd = CreateWindow(TEXT("GettingStarted"), // window class name
TEXT("Getting Started"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters

ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);

auto dll = LoadLibraryW(L"isGDI.dll");
assert(dll);
auto DrawRectangle = (int(__stdcall *)(
HWND, DWORD, int, int, int, int))GetProcAddress(dll, "DrawRectangle");
assert(DrawRectangle);
DrawRectangle(hWnd, 0xffff0000, 0, 0, 100, 100);

while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeLibrary(dll);

return msg.wParam;
} // WinMain

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {

return DefWindowProc(hWnd, message, wParam, lParam);
} // WndProc

此外,如果我直接通过源代码调用 DrawRectangle(不使用 DLL),程序将按预期运行

最佳答案

我认为您在 dll 中使用了全局对象 GdiManager。这意味着它的析构函数从 DLL_PROCESS_DETACH 调用,所以在 LoaderLock 临界区内。在析构函数中调用 GdiplusShutdown。当 GdiplusShutdownLoaderLock 和 GDI+ 中调用时使用后台线程(suppressBackgroundThread = FALSE - 这是你的情况)这总是导致死锁:GdiplusShutdown 向后台线程退出发出信号(设置 Globals::ThreadQuitEvent),然后等待 后台线程退出。线程退出时尝试进入 LoaderLock 并卡在这里 - 因为它由调用 GdiplusShutdown 的线程持有。所以主线程卡在等待后台线程和辅助线程卡在进入 LoaderLock 临界区。

我们可以尝试使用suppressBackgroundThread = TRUE,但在这种情况下需要调用NotificationUnhook .如果在已经 UB 的 DLL_PROCESS_DETACH 上执行此操作(基于实现),这可能看起来正常、挂起或失败(在这个调用中,例如 DestroyWindow,这也是 dll 的错误条目,如果在不同线程上调用进程分离也会出错(比较 dll 附加) - 因此将在另一个线程上的 NotificationHook 内创建窗口)

这里正确的解决方案是从 dll 导出 2 个附加函数,比如 StartStop,以及从第一次调用 GdiplusStartup 和第二次 GdiplusShutdown。在 dll 加载之后调用 Start 并在卸载之前调用 Stop

关于c++ - 从 dll 使用 gdi+ 时进程不退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53674354/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com