截图后程序卡住了

Posted

技术标签:

【中文标题】截图后程序卡住了【英文标题】:Program gets stuck after taking screenshot 【发布时间】:2020-01-26 22:29:52 【问题描述】:
#include "screenshot.h"
#include "changewallpaper.h"

using namespace std;

int main()

    screenshot();
    changewallpaper();

我的截图();

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>

using namespace Gdiplus;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)

   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      
   

   free(pImageCodecInfo);
   return -1;  // Failure


void screenshot()

    // get the device context of the screen
    HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);

    int width = GetDeviceCaps(hScreenDC, HORZRES);
    int height = GetDeviceCaps(hScreenDC, VERTRES);

    POINT a,b;

    a.x=0;
    a.y=0;

    b.x=width;
    b.y=height;

    // copy screen to bitmap
    HDC     hScreen = GetDC(NULL);
    HDC     hDC     = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x-a.x), abs(b.y-a.y));
    HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
    BOOL    bRet    = BitBlt(hDC, 0, 0, abs(b.x-a.x), abs(b.y-a.y), hScreen, a.x, a.y, SRCCOPY);

    //Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    Gdiplus::Bitmap bitmap(hBitmap, NULL);

    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    bitmap.Save(L"D:\\Pictures\\screen.png", &pngClsid, NULL);

    // clean up
    SelectObject(hDC, old_obj);
    DeleteDC(hDC);
    ReleaseDC(NULL, hScreen);
    DeleteObject(hBitmap);

    //delete image;
    GdiplusShutdown(gdiplusToken);

我的问题是 changewallpaper();从不跑。如果我放置 changewallpaper();在屏幕截图()之前;在我的主要一切工作,但如果我有它像上面那样。我需要我的程序在更改壁纸之前截取屏幕截图,所以我不能只是切换它们。有谁知道可能是什么问题?我一无所知。

【问题讨论】:

当你说它没有按当前顺序工作时,你是什么意思——它是挂起的吗?您是否尝试过使用调试器单步执行它以查看它在screenshot() 中的进展情况?如果屏幕截图成功返回,则问题可能出在其他函数中(现在不是问题的一部分)。 请出示changewallpaper的代码 GDI+ 使用 WIC,这是一个通过 COM 公开的接口。据推测,GDI+ 在调用线程上初始化 COM。如果它选择单线程单元,则需要运行消息循环。 @IInspectable,假设您尝试了我的修复并且它仍然挂起,您能告诉我它挂在哪里吗?我想尝试并从中学习。在确定 gdi 对象范围后的调试器中,我可以看到所有三个 GDI 线程都干净地退出。 【参考方案1】:

作为@xsoftie,你挂在image::~image()

根据GdiplusShutdown :

您必须删除所有 GDI+ 对象(或让它们退出 范围)在调用 GdiplusShutdown 之前。

Gdiplus::Bitmap bitmap(hBitmap, NULL);

bitmap 存储在堆栈中,系统会在其生命周期结束时( 之后)自动释放它们。 scoftie 的方法是一种可行的解决方案。

当然你也可以使用new在堆上请求GDI+对象指针,就像sample:

Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(hBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap->Save(L"D:\\Pictures\\screen.png", &pngClsid, NULL);

//...
delete bitmap;
GdiplusShutdown(gdiplusToken);

或者,将GdiplusStartupGdiplusShutdown放在主函数中:

int main()

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    screenshot();
    changewallpaper();
    GdiplusShutdown(gdiplusToken);

此外,当您不再需要hScreenDC 时,请调用DeleteDC 函数。

【讨论】:

【参考方案2】:

一些变化。范围界定对于使位图超出范围并自行清理是必要的。第二个是发布 hScreenDC,尽管我将把它作为练习留给你看是否有必要。你挂在 image::~image 函数中。让我知道它是否为您解决了问题。

  
    Gdiplus::Bitmap bitmap(hBitmap, NULL);

    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    bitmap.Save(L"c:\\temp\\screen.png", &pngClsid, NULL);


// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
DeleteDC(hScreenDC);

【讨论】:

以上是关于截图后程序卡住了的主要内容,如果未能解决你的问题,请参考以下文章

第一个 epoch 完成后模型训练卡住了……第二个 epoch 甚至不会开始,也不会抛出任何错误,它只是保持空闲

获得 json 响应后,Swift 4 UI 卡住了

AdLds / Adam 每 50 秒卡住一次

centos ssh 卡住

当我在小于 10 的 ios 版本中隐藏/显示导航栏时,应用程序在进入后台后卡住

boost::async 读写在一次读取和两次写入后卡住