我想将GDI +与Pascal脚本一起使用,而Pascal脚本本身并不提供GDI +,但是我不知道为什么使用dll(共享)时,即使窗口被破坏,该过程也不会退出,我的意思是说我尽管没有任何窗口,它仍然可以从任务管理器中看到正在运行的进程。该过程保持空闲状态,即没有任何资源使用
在我的dll中,对于每个新的hwnd,我都钩住了自己的wndproc,并且在WM_Paint消息中,我正在绘制到目前为止需要绘制的指定对象
我正在导出DrawRectangle符号以用于32位的绘图和编译
我的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
此外,如果我直接通过源代码(不使用DLL)调用DrawRectangle,则程序将按预期运行
答案 0 :(得分:1)
GdiManager
。这意味着它是从DLL_PROCESS_DETACH
调用的析构函数,因此位于 LoaderLock 关键部分。在析构函数中,您调用GdiplusShutdown
。当在{em> LoaderLock 内部调用的GdiplusShutdown
和GDI +使用后台线程(suppressBackgroundThread = FALSE
-这是您的情况)时,总是会导致死锁:
GdiplusShutdown
发出信号通知后台线程退出(设置Globals::ThreadQuitEvent
),然后等待等待后台线程退出。线程退出时,请尝试进入 LoaderLock 并挂在这里-因为它由调用GdiplusShutdown
的线程持有。因此,在进入 LoaderLock 关键部分时,主线程挂起,等待背景线程,而bacground线程挂起。
我们可以尝试使用suppressBackgroundThread = TRUE
,但在这种情况下需要致电NotificationUnhook
。如果在已经根据市场情况实现的DLL_PROCESS_DETACH
UB上执行此操作,则它看起来可以正常运行,挂起或失败(例如在此调用DestroyWindow
,这也是dll条目错误,如果进程也错误)分离将在其他线程上调用(比较dll附加)-因此将在另一线程的NotificationHook
内部创建窗口)
Start
和Stop
,并从第一次调用GdiplusStartup
和第二次调用GdiplusShutdown
中导出。 dll加载后立即调用Start
,卸载前调用Stop
答案 1 :(得分:0)
您没有告诉当前线程(应用程序)退出。在与窗口关联的WndProc
中使用PostQuitMessage:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
if (message == WM_DESTROY)
PostQuitMessage(0);
else
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
}