процесс не завершается при использовании gdi + из dll - PullRequest
0 голосов
/ 07 декабря 2018

Я хочу использовать GDI + со сценариями Pascal, которые изначально не предоставляют GDI +, но я не знаю, почему. При использовании dll (shared) процесс не завершится, даже когда окно разрушено, я имею в виду, что явсе еще может видеть процесс, запущенный из диспетчера задач, хотя у него нет никакого окна, чтобы видеть.Процесс остается бездействующим, т.е. без использования ресурсов

В моем 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

также, программабудет работать как положено, если я вызову DrawRectangle напрямую через исходный код (без использования DLL)

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Я вижу, что вы используете глобальный объект GdiManager внутри DLL.это означало, что деструктор вызывается из DLL_PROCESS_DETACH, поэтому внутри LoaderLock критическая секция.в деструкторе вы звоните GdiplusShutdown.когда GdiplusShutdown вызывается внутри LoaderLock и GDI + используют фоновый поток (suppressBackgroundThread = FALSE - это ваш случай), это всегда вызывает взаимоблокировку: GdiplusShutdown сигнал на выход из фонового потока (установить Globals::ThreadQuitEvent), а затем ожидание выхода из фонового потока.Поток при выходе, попробуйте войти в LoaderLock и зависает здесь - потому что он удерживается потоком, который вызывает GdiplusShutdown.поэтому основной поток завис в ожидании фонового потока, а внутренний поток завис в критической секции enter LoaderLock .

мы можем попытаться использовать suppressBackgroundThread = TRUE, но в этом случае необходимо вызвать NotificationUnhook.если сделать это на DLL_PROCESS_DETACH уже UB (в зависимости от реализации), это может выглядеть нормально, зависать или давать сбой (внутри этого вызывается, например, DestroyWindow, что также является ошибкой при входе в dll, а также ошибкой, если будет вызван процесс отсоединения)в другом потоке (сравните dll attach) - таким образом, окно будет создано внутри NotificationHook в другом потоке)

правильное решение здесь будет экспортировать 2 дополнительные функции из dll, скажем Start и Stop, ис первого звонка GdiplusStartup и со второго GdiplusShutdown.вызов Start сразу после загрузки dll и Stop перед выгрузкой

0 голосов
/ 07 декабря 2018

Вы не говорите текущему потоку (приложению) о выходе.Используйте PostQuitMessage в WndProc, связанном с окном:

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;
}
...