【问题标题】:GDI printer Device Context in a Worker Thread randomly fails工作线程中的 GDI 打印机设备上下文随机失败
【发布时间】:2018-12-09 20:50:29
【问题描述】:

我设法创建了一个基于 GDI 的位图文件打印机例程。它工作稳定,没有任何泄漏。当然,唯一的缺点是它在后台处理程序传输期间冻结了 UI。解决方案 attampt 将打印例程移动到工作线程中。这是代码(删除了错误检查):

void __fastcall PRINT_THREAD::Execute()
{
  while(!Terminated)
  {
   Synchronize(&TalkToOwningThread);
   if(PrintFilePath!="") PrintImage(PrintFilePath);
   Sleep(10);
  }
}


void __fastcall PRINT_THREAD::PrintImage(WideString PrintFilePath)
{
  HDC     hDC;
  DOCINFO di;
  int     w,h;
  bool    success=true;
  TCHAR   szString[32]  = TEXT("Printed from a thread");
  WideString PrinterName="FinePrint";

  TBitmap  *bmp = new TBitmap();
  TPicture *pic = new TPicture();

  hDC=CreateDC(TEXT("WINSPOOL"),PrinterName.c_bstr(),NULL,NULL);

  w=GetDeviceCaps(hDC, HORZRES);
  h=GetDeviceCaps(hDC, VERTRES);

  SecureZeroMemory(&di,sizeof(DOCINFO));
  di.cbSize = sizeof(DOCINFO);
  di.lpszDocName = TEXT("Print Job");

  StartDoc(hDC,&di);
  StartPage(hDC);

  try
  {
    pic->LoadFromFile(PrintFilePath);

    bmp->Width=w;  // set the bitmap dimensions to the printer dimensions
    bmp->Height=h;

    // fill the bitmap with 1:1 print content
    bmp->Canvas->StretchDraw(TRect(0,0,w-1,h-1),pic->Graphic); 
  }
  catch(...){success=false;}

  if(success)
  {
      BitBlt(hDC,0,0,w,h, bmp->Canvas->Handle,0,0, SRCCOPY);
      TextOut(hDC,0,100,szString,lstrlen(szString));
  }

  EndDoc(hDC);
  if(hDC) DeleteDC(hDC);

  delete pic;
  delete bmp;

}

结果:

  • 每次打印调用都会通过打印机在页面上生成 - 好的
  • 10 页中只有大约 2 页包含位图 - 不好
  • 所有页面都包含测试行 - 好的
  • 每次调用都会多添加一个 GDI 资源(任务管理器)- 不行

我已经尝试将 CreateDC/DeleteDC 函数移回主线程并将 hDC 转移到工作线程 - 结果相同。

有什么办法让这只动物跑起来吗?

环境: C++Builder 10.1 柏林,Windows10,16GB

谢谢。

【问题讨论】:

  • EndPage(hDC); 放在EndDoc(hDC); 之前,但这可能不是泄漏的原因。
  • 哪个 C++Builder? GDI 是基于 winapi 的,不应从主消息处理线程之外执行任何可视化组件调用。否则,即使在不相关的 winapi 调用上,winapi 也会在应用程序中的任何地方出现随机错误/故障/错误/崩溃。因此缺少渲染的东西,泄漏等......不确定在 Win10 中是否仍然如此,但在所有以前的版本中这是一个问题,所以我怀疑他们是否修复了它......
  • @Spektre 普通 GDI 在工作线程中运行良好。 VCL 的 UI 框架在主 UI 线程之外是不安全的,因为它如何使用不受并发访问保护的全局资源,它如何使用线程绑定的 HWND 等,而不是因为 GDI。但是,我要指出的是,当在工作线程中使用TBitmap 时,您必须在使用它时(un)lockCanvas 否则主UI 线程可以撕掉TBitmap 背后的GDI 资源, 由于 Graphics 单元缓存和清理可能由多个对象共享的 GDI 资源的方式。
  • @RemyLebeau 很高兴知道。
  • 感谢大家的宝贵cmets!

标签: c++ multithreading c++builder gdi


【解决方案1】:

按照 Remy 的建议,我添加了锁定/解锁对:

TBitmap  *bmp = new TBitmap();
TPicture *pic = new TPicture();

bmp->Canvas->Lock();

----
----

EndPage(hDC);
EndDoc(hDC);

bmp->Canvas->Unlock();

if(hDC) DeleteDC(hDC);
delete pic;
delete bmp;

最后,动物像魅力一样奔跑 - 不再缺少图形,也不再有 GDI 泄漏。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-31
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多