我们的产品中嵌入了im子系统,其中包括类似QQ的截图功能。之前截图功能一直都是好好的,无论是在XP还是win7、win8中。但是这两天,在公司大boss新用的Surface 3 Pro微软平板上出现了异常,开启截图后截图背景被放大了,导致截图背景(即发起截图时的桌面图片)显示不全了,这明显是有问题的,大boss说了,必须要解决。大boss既然都下达指令了,只能将手头的工作先放放,抓紧研究一下这个问题,尽快找到解决方法。

正常情况下,截图背景是当前整个桌面的图片,但是在win10中被放大后只能显示一部分桌面,即桌面图片显示不全了。这是什么情况?想到QQ,于是拿过来对比了一下,QQ最新版本也有类似的问题,QQ好像对win10做了一些兼容性处理,但是处理的还是有些问题。QQ都有问题,我们也就似乎有理由向boss说明了,这可能是新的win10 UI新特性引起的,QQ都没有解决,我们可能也不太好解决。但是测试同事就不同意了,大boss关注的问题必须要给个说法,要尽可能的解决掉,谁说QQ没解决或者目前解决不了的问题,我们就可以不解决!于是乎,就只能静下心来研究一下了。

经测试同事提醒,当前Surface平板上显示比例调整为150%,默认的100%在平台上显示太小了,没法用。显示比例调整页面,由于没能在win10系统中截图,以win7界面为例(本文中的相关截图均以win7为例,可能和win10略有差别或者有所出入,关注者可以自行到win10中查看),如下:

在win10中,右键点击桌面,在弹出的右键菜单中点击“显示比例”就能进入如上的显示比例设置页面了。win10以前的系统,都是通知通过改变dpi值来调整显示比例的(可以通过Process Monitor工具检测设置页面设置时改写的注册表项来看出来),win10中则有所不同,在win10的注册表中没有和win7一样的注册表项,用Process Monitor也没检测到DPI相关的注册表项。win10的显示比例设置和win7还有一处不同,win10设置后立即生效,win7则提示用户注销后才能生效。当显示比例设置为100%时,截图没有问题,但是设置到100%以上后,就有被放大的问题了。到底是Win10的什么新特性引起的呢?在网上搜了很久也没找到相关问题的解决办法,可能是win10才开始商用吧,这方面的问题报的比较少,但是无意间找到了一篇很有价值的文章,给了我很大的启发:关于Windows高DPI的一些简单总结,链接为:http://www.cppblog.com/weiym/archive/2014/02/18/205841.aspx。

其中,下面的这段话非常有用:通过 DWM 虚拟化支持的 高DPI方式,这种方式的高DPI支持是通过DWM的缩放实现的, 具体过程是这样的, 比如我们当前系统的DPI是200%, 我们程序运行时,系统会告诉你当前DPI仍然是96(100%), 所以我们程序会仍然按照100%的方式进行绘画, 但是但是系统给我们的坐标是根据DPI缩小过后的(也就是我们对窗口调用GetWindowRect或是通过GetSystemMetrics(SM_CXSCREEN)得到的大小会比实际大小减半) , 当我们画完之后, DWM再对整个窗口进行200% 放大后画到屏幕上, 这样看起来我们的程序就自动支持高DPI了。通过在我们的截图模块中添加打印日志,GetWindowRect获取的桌面窗口的宽度和高度,GetSystemMetrics获取的屏幕宽度和高度(都是像素值),在显示比例为200%时获取的值,比在显示比例为100%时要缩小一半,和上面的一段话的描述完全一致。

一般截图的实现思路是,发起截图时将此时整个桌面抓取下来,然后启动一个全屏的对话框,然后将抓取的桌面图片作为对话框背景贴到对话框上。应该是获取桌面图片的代码出问题了,只获取到了一部分桌面区域图片。那么该如何处理才能将整个桌面的图片都获取到然后“全景”显示出来呢?先来看背景图的相关处理代码。

获取桌面尺寸的代码:

[cpp] view plaincopy print?
  1. RECT rcDeskTop;
  2. HWND hWndDeskTop = ::GetDesktopWindow();
  3. :;GetWindowRect( hWndDeskTop, &rcDeskTop );
 RECT rcDeskTop;HWND hWndDeskTop = ::GetDesktopWindow();:;GetWindowRect( hWndDeskTop, &rcDeskTop );

获取屏幕宽度和高度的代码:

[cpp] view plaincopy print?
  1. m_xScreen = ::GetSystemMetrics( SM_CXSCREEN );
  2. m_yScreen = ::GetSystemMetrics( SM_CYSCREEN );
 m_xScreen = ::GetSystemMetrics( SM_CXSCREEN );m_yScreen = ::GetSystemMetrics( SM_CYSCREEN );

拷贝桌面的代码:

[cpp] view plaincopy print?
  1. // 拷贝桌面,lpRect 代表选定区域,bSave 标记是否将图片内容保存到剪切板中
  2. HBITMAP CCatchScreenDlg::CopyScreenToBitmap( LPRECT lpRect )
  3. {
  4. // 确保选定区域不为空矩形
  5. if ( IsRectEmpty( lpRect ) )
  6. {
  7. return NULL;
  8. }
  9. CString strLog;
  10. HDC hScrDC = ::CreateDC( _T("DISPLAY"), NULL, NULL, NULL ); // 为屏幕创建设备描述表
  11. if ( hScrDC == NULL )
  12. {
  13. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap] 创建DISPLAY失败, GetLastError: %d"),
  14. GetLastError() );
  15. WriteScreenCatchLog( strLog );
  16. return NULL;
  17. }
  18. HDC hMemDC = ::CreateCompatibleDC( hScrDC ); // 为屏幕设备描述表创建兼容的内存设备描述表
  19. if ( hMemDC == NULL )
  20. {
  21. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的hMemDC失败, GetLastError: %d"),
  22. GetLastError() );
  23. WriteScreenCatchLog( strLog );
  24. ::DeleteDC( hScrDC );
  25. return NULL;
  26. }
  27. int nX = 0;
  28. int nY = 0;
  29. int nX2 = 0;
  30. int nY2 = 0;
  31. int nWidth = 0;
  32. int nHeight = 0;
  33. // 保证left小于right,top小于bottom
  34. LONG lTemp = 0;
  35. if ( lpRect->left > lpRect->right )
  36. {
  37. lTemp = lpRect->left;
  38. lpRect->left = lpRect->right;
  39. lpRect->right = lTemp;
  40. }
  41. if ( lpRect->top > lpRect->bottom )
  42. {
  43. lTemp = lpRect->top;
  44. lpRect->top = lpRect->bottom;
  45. lpRect->bottom = lTemp;
  46. }
  47. // 获得选定区域坐标
  48. nX = lpRect->left;
  49. nY = lpRect->top;
  50. nX2 = lpRect->right;
  51. nY2 = lpRect->bottom;
  52. // 确保选定区域是可见的
  53. if ( nX < 0 )
  54. {
  55. nX = 0;
  56. }
  57. if ( nY < 0 )
  58. {
  59. nY = 0;
  60. }
  61. if ( nX2 > m_xScreen )
  62. {
  63. nX2 = m_xScreen;
  64. }
  65. if ( nY2 > m_yScreen )
  66. {
  67. nY2 = m_yScreen;
  68. }
  69. nWidth = nX2 - nX;
  70. nHeight = nY2 - nY;
  71. HBITMAP hBitmap = ::CreateCompatibleBitmap( hScrDC, nWidth, nHeight ); // 创建一个与屏幕设备描述表兼容的位图
  72. if ( hBitmap == NULL )
  73. {
  74. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的Bitmap失败, GetLastError: %d"),
  75. GetLastError() );
  76. WriteScreenCatchLog( strLog );
  77. ::DeleteDC( hScrDC );
  78. ::DeleteDC( hMemDC );
  79. return NULL;
  80. }
  81. ::SelectObject( hMemDC, hBitmap );  // 把新位图选到内存设备描述表中
  82. BOOL bRet = ::BitBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT );  // CAPTUREBLT - 该参数保证能够截到透明窗口
  83. if ( !bRet )
  84. {
  85. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]将hScrDC拷贝到hMemDC失败, GetLastError: %d"),
  86. GetLastError() );
  87. WriteScreenCatchLog( strLog );
  88. ::DeleteDC( hScrDC );
  89. ::DeleteDC( hMemDC );
  90. ::DeleteObject( hBitmap );
  91. return NULL;
  92. }
  93. if ( hScrDC != NULL )
  94. {
  95. ::DeleteDC( hScrDC );
  96. }
  97. if ( hMemDC != NULL )
  98. {
  99. ::DeleteDC( hMemDC );
  100. }
  101. return hBitmap; // hBitmap资源不能释放,因为函数外部要使用
  102. }
// 拷贝桌面,lpRect 代表选定区域,bSave 标记是否将图片内容保存到剪切板中
HBITMAP CCatchScreenDlg::CopyScreenToBitmap( LPRECT lpRect )
{                           // 确保选定区域不为空矩形if ( IsRectEmpty( lpRect ) ){return NULL;}CString strLog;HDC hScrDC = ::CreateDC( _T("DISPLAY"), NULL, NULL, NULL ); // 为屏幕创建设备描述表if ( hScrDC == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap] 创建DISPLAY失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );return NULL;}HDC hMemDC = ::CreateCompatibleDC( hScrDC ); // 为屏幕设备描述表创建兼容的内存设备描述表if ( hMemDC == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的hMemDC失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );::DeleteDC( hScrDC );return NULL;}int nX = 0;int nY = 0;int nX2 = 0;int nY2 = 0;   int nWidth = 0; int nHeight = 0;// 保证left小于right,top小于bottomLONG lTemp = 0;if ( lpRect->left > lpRect->right ){lTemp = lpRect->left;lpRect->left = lpRect->right;lpRect->right = lTemp;}if ( lpRect->top > lpRect->bottom ){lTemp = lpRect->top;lpRect->top = lpRect->bottom;lpRect->bottom = lTemp;}// 获得选定区域坐标nX = lpRect->left;nY = lpRect->top;nX2 = lpRect->right;nY2 = lpRect->bottom;// 确保选定区域是可见的if ( nX < 0 ){nX = 0;}if ( nY < 0 ){nY = 0;}if ( nX2 > m_xScreen ){nX2 = m_xScreen;}if ( nY2 > m_yScreen ){nY2 = m_yScreen;}nWidth = nX2 - nX;nHeight = nY2 - nY;HBITMAP hBitmap = ::CreateCompatibleBitmap( hScrDC, nWidth, nHeight ); // 创建一个与屏幕设备描述表兼容的位图if ( hBitmap == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的Bitmap失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );::DeleteDC( hScrDC );::DeleteDC( hMemDC );return NULL;}::SelectObject( hMemDC, hBitmap );   // 把新位图选到内存设备描述表中BOOL bRet = ::BitBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT );  // CAPTUREBLT - 该参数保证能够截到透明窗口if ( !bRet ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]将hScrDC拷贝到hMemDC失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );::DeleteDC( hScrDC );::DeleteDC( hMemDC );::DeleteObject( hBitmap );return NULL;}if ( hScrDC != NULL ){::DeleteDC( hScrDC );}if ( hMemDC != NULL ){::DeleteDC( hMemDC );}return hBitmap; // hBitmap资源不能释放,因为函数外部要使用
}

获取桌面图片调用该接口时,传入的就是桌面区域的像素大小。函数内部的具体做法是,先获取桌面DC,然后创建一个与桌面DC兼容的、桌面大小的内存位图(通过位图句柄来记录并引用),然后调用BitBlt将桌面图片拷贝到内存位图上。win10中将显示比例调到大于100%时遇到的问题,应该就是该BitBlt左右的代码引起的。 后来又发现了一个很重要的线索,不管显示比例比如何改变,当前的系统分辨率是不变的,还是之前设置的分辨率:

当显示比例设置为100%时屏幕的宽度和高度就是当前系统设置的分辨率,当显示比例大于100%时,比如为N%,DWM虚拟化会将GetWindowRect等获取的像素尺寸缩小到分辨率的1/(N%),所以结合获取的桌面图片区域的比例情况,是不是可以考虑使用当前系统设置的分辨率宽度和高度来获取整张桌面图片,因为分辨率宽度和高度大于当前桌面的像素宽度和高度,所以在向内存中拷贝图片时,需要选用StretchBlt,将图片进行缩放,缩放到桌面的像素高度和宽度。经过试验,这样做确实是可行的,具体的做法是,先调用EnumDisplaySettings获取当前系统设置的分辨率,然后判断屏幕宽度是否小于分辨率,若小于就要换用StretchBlt进行缩放处理:

[cpp] view plaincopy print?
  1. // 拷贝桌面,lpRect 代表选定区域,bSave 标记是否将图片内容保存到剪切板中
  2. HBITMAP CCatchScreenDlg::CopyScreenToBitmap( LPRECT lpRect )
  3. {
  4. // 确保选定区域不为空矩形
  5. if ( IsRectEmpty( lpRect ) )
  6. {
  7. return NULL;
  8. }
  9. CString strLog;
  10. HWND hWndDeskTop = ::GetDesktopWindow();
  11. //HDC hScrDC = ::CreateDC( _T("DISPLAY"), NULL, NULL, NULL );
  12. HDC hScrDC = ::GetDC( hWndDeskTop ); // 为屏幕创建设备描述表
  13. if ( hScrDC == NULL )
  14. {
  15. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap] 创建DISPLAY失败, GetLastError: %d"),
  16. GetLastError() );
  17. WriteScreenCatchLog( strLog );
  18. return NULL;
  19. }
  20. HDC hMemDC = ::CreateCompatibleDC( hScrDC ); // 为屏幕设备描述表创建兼容的内存设备描述表
  21. if ( hMemDC == NULL )
  22. {
  23. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的hMemDC失败, GetLastError: %d"),
  24. GetLastError() );
  25. WriteScreenCatchLog( strLog );
  26. //::DeleteDC( hScrDC );
  27. ::ReleaseDC( hWndDeskTop, hScrDC );
  28. return NULL;
  29. }
  30. int nX = 0;
  31. int nY = 0;
  32. int nX2 = 0;
  33. int nY2 = 0;
  34. int nWidth = 0;
  35. int nHeight = 0;
  36. // 保证left小于right,top小于bottom
  37. CDirectRect rc = *lpRect;
  38. rc.Normalize();
  39. // 获得选定区域坐标
  40. nX = rc.left;
  41. nY = rc.top;
  42. nX2 = rc.right;
  43. nY2 = rc.bottom;
  44. // 确保选定区域是可见的
  45. if ( nX < 0 )
  46. {
  47. nX = 0;
  48. }
  49. if ( nY < 0 )
  50. {
  51. nY = 0;
  52. }
  53. if ( nX2 > m_xScreen )
  54. {
  55. nX2 = m_xScreen;
  56. }
  57. if ( nY2 > m_yScreen )
  58. {
  59. nY2 = m_yScreen;
  60. }
  61. nWidth = nX2 - nX;
  62. nHeight = nY2 - nY;
  63. HBITMAP hBitmap = ::CreateCompatibleBitmap( hScrDC, nWidth, nHeight ); // 创建一个与屏幕设备描述表兼容的位图
  64. if ( hBitmap == NULL )
  65. {
  66. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的Bitmap失败, GetLastError: %d"),
  67. GetLastError() );
  68. WriteScreenCatchLog( strLog );
  69. //::DeleteDC( hScrDC );
  70. ::ReleaseDC( hWndDeskTop, hScrDC );
  71. ::DeleteDC( hMemDC );
  72. return NULL;
  73. }
  74. ::SelectObject( hMemDC, hBitmap );  // 把新位图选到内存设备描述表中
  75. BOOL bRet = FALSE;
  76. BOOL bProcessed = FALSE;
  77. // 针对win10 DWM虚拟缩放时的处理
  78. if ( IsOSWin10() && m_bWin10DpiScaleEnabled )
  79. {
  80. DEVMODE curDevMode;
  81. memset( &curDevMode, 0, sizeof(curDevMode) );
  82. curDevMode.dmSize = sizeof(DEVMODE);
  83. BOOL bEnumRet = ::EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &curDevMode );
  84. if ( bEnumRet && m_xScreen < curDevMode.dmPelsWidth )
  85. {
  86. bProcessed = TRUE;
  87. ::SetStretchBltMode( hMemDC, STRETCH_HALFTONE );
  88. bRet = ::StretchBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, 0, 0, curDevMode.dmPelsWidth,
  89. curDevMode.dmPelsHeight, SRCCOPY|CAPTUREBLT );
  90. }
  91. }
  92. if ( !bProcessed )
  93. {
  94. bRet = ::BitBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT );  // CAPTUREBLT - 该参数保证能够截到透明窗口
  95. }
  96. if ( !bRet )
  97. {
  98. strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]将hScrDC拷贝到hMemDC失败, GetLastError: %d"),
  99. GetLastError() );
  100. WriteScreenCatchLog( strLog );
  101. //::DeleteDC( hScrDC );
  102. ::ReleaseDC( hWndDeskTop, hScrDC );
  103. ::DeleteDC( hMemDC );
  104. ::DeleteObject( hBitmap );
  105. return NULL;
  106. }
  107. if ( hScrDC != NULL )
  108. {
  109. //::DeleteDC( hScrDC );
  110. ::ReleaseDC( hWndDeskTop, hScrDC );
  111. }
  112. if ( hMemDC != NULL )
  113. {
  114. ::DeleteDC( hMemDC );
  115. }
  116. return hBitmap; // hBitmap资源不能释放,因为函数外部要使用
  117. }
// 拷贝桌面,lpRect 代表选定区域,bSave 标记是否将图片内容保存到剪切板中
HBITMAP CCatchScreenDlg::CopyScreenToBitmap( LPRECT lpRect )
{                           // 确保选定区域不为空矩形if ( IsRectEmpty( lpRect ) ){return NULL;}CString strLog;HWND hWndDeskTop = ::GetDesktopWindow();//HDC hScrDC = ::CreateDC( _T("DISPLAY"), NULL, NULL, NULL );HDC hScrDC = ::GetDC( hWndDeskTop ); // 为屏幕创建设备描述表if ( hScrDC == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap] 创建DISPLAY失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );return NULL;}HDC hMemDC = ::CreateCompatibleDC( hScrDC ); // 为屏幕设备描述表创建兼容的内存设备描述表if ( hMemDC == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的hMemDC失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );//::DeleteDC( hScrDC );::ReleaseDC( hWndDeskTop, hScrDC );return NULL;}int nX = 0;int nY = 0;int nX2 = 0;int nY2 = 0;   int nWidth = 0; int nHeight = 0;// 保证left小于right,top小于bottomCDirectRect rc = *lpRect;rc.Normalize();// 获得选定区域坐标nX = rc.left;nY = rc.top;nX2 = rc.right;nY2 = rc.bottom;// 确保选定区域是可见的if ( nX < 0 ){nX = 0;}if ( nY < 0 ){nY = 0;}if ( nX2 > m_xScreen ){nX2 = m_xScreen;}if ( nY2 > m_yScreen ){nY2 = m_yScreen;}nWidth = nX2 - nX;nHeight = nY2 - nY;HBITMAP hBitmap = ::CreateCompatibleBitmap( hScrDC, nWidth, nHeight ); // 创建一个与屏幕设备描述表兼容的位图if ( hBitmap == NULL ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]创建与hScrDC兼容的Bitmap失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );//::DeleteDC( hScrDC );::ReleaseDC( hWndDeskTop, hScrDC );::DeleteDC( hMemDC );return NULL;}::SelectObject( hMemDC, hBitmap );  // 把新位图选到内存设备描述表中BOOL bRet = FALSE;BOOL bProcessed = FALSE;// 针对win10 DWM虚拟缩放时的处理if ( IsOSWin10() && m_bWin10DpiScaleEnabled ){DEVMODE curDevMode;memset( &curDevMode, 0, sizeof(curDevMode) );curDevMode.dmSize = sizeof(DEVMODE);BOOL bEnumRet = ::EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &curDevMode );if ( bEnumRet && m_xScreen < curDevMode.dmPelsWidth ){bProcessed = TRUE;::SetStretchBltMode( hMemDC, STRETCH_HALFTONE );bRet = ::StretchBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, 0, 0, curDevMode.dmPelsWidth, curDevMode.dmPelsHeight, SRCCOPY|CAPTUREBLT );}}if ( !bProcessed ){bRet = ::BitBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT );  // CAPTUREBLT - 该参数保证能够截到透明窗口}if ( !bRet ){strLog.Format( _T("[CCatchScreenDlg::CopyScreenToBitmap]将hScrDC拷贝到hMemDC失败, GetLastError: %d"), GetLastError() );WriteScreenCatchLog( strLog );//::DeleteDC( hScrDC );::ReleaseDC( hWndDeskTop, hScrDC );::DeleteDC( hMemDC );::DeleteObject( hBitmap );return NULL;}if ( hScrDC != NULL ){//::DeleteDC( hScrDC );::ReleaseDC( hWndDeskTop, hScrDC );}if ( hMemDC != NULL ){::DeleteDC( hMemDC );}return hBitmap; // hBitmap资源不能释放,因为函数外部要使用
}

经测试,可以达到我们预想的效果,桌面的整个区域都显示出来了。但是有个小问题,在调用StrecthBlt时虽然设置了STRETCH_HALFTONE标记,但是还是有一定的失真,显示桌面全貌的图片,相对真实的桌面有点模糊,因为大图片被缩小了。不过相对之前的放大问题,这个应该是可以忍受的。

上面的代码还有一点需要注意,对于获取系统版本,GetVersionEx API函数在win8和win10上基本上已经被逐步废弃了,如果调用的话,获取的将一直是win7的系统版本。要正确获取系统版本,可以使用RtlGetNtVersionNumbers或者NetWkstaGetInfo函数,具体参见:http://blog.csdn.net/chenlycly/article/details/52881385。

另外,针对某个进程我们可以将DWM虚拟缩放禁用掉,这样就不会有缩放引起的问题了。具体做法是,右键单击目标进程exe文件或者其快捷方式,点击属性,在弹出的属性窗口中,在“兼容性”标签页中,将“高DPI设置时禁用显示缩放”勾选上就可以了,如下所示:(图片是从win7中截图的,可能和win10中略有不同)

当勾选上该选项后,该程序启动后,窗口的大小始终固定为100%时的尺寸,不会随着显示比例的设置而变化。通过该方法,可以将截图放大的问题规避掉,但是对于surface平板来说,是行不通的,因为100%时的窗口显示太小,很难看清,所以需要按上面的办法对截图进行改造。

有人可能会问,我们能否通过代码去判断当前进程是否禁用了高DPI设置时的缩放呢?答案是肯定的。按经验来看,应该将是否勾选对应的标记保存到注册表中了,可以使用Process Monitor工具监测一下注册表操作。具体的做法是,打开右键属性,在任务管理器中查看属于哪个进程,然后到Process Monitor中设置过滤条件,进程属于资源管理器explorer.exe,注意由于该进程注册表操作很频繁,所以要抓的准确,容易看的出来一点,可以先将上面的属性打开,然后开启捕获,然后快速勾选,然后点击应用,然后到Process Monitor停止捕获,这样看到的捕获结果会少一点,这样更容易找到到底写到哪个位置的注册表中。经检测,是保存到HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers中了,如下所示:

注意上图是win7中的截图,win10中的禁用标记和win7不一样,因为我当前机器是win7的,之前没有在win10上截图,所以以win7系统来说明,一定要注意。那么从代码角度如何判断目标进程是否禁用了高DPI时的缩放了呢?其实很简单,可以先获取到目标进程的完整路径,然后到注册表HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers位置中,查看是否有对应的如上图的设置选项,如果有,则说明禁了,如果没有,则没有禁用。至于Porcess Monitor工具如何获取及如何使用请关注者自行百度。

最后,作为windows开发人员,要学会使用Process Explorer、Process Monitor、API Monitor、Process Hacker、Dependency Walker等工具。至于分析软件异常,需要使用windbg和IDA等反汇编工具。

解决win10系统中截图异常放大的问题相关推荐

  1. 解决win10系统中截图异常放大的问题 window10-win10 截图放大-缩放-问题解决办法

    解决win10系统中截图异常放大的问题 window10-win10 截图放大-缩放-问题解决办法 亲测: 1.应用程序–右击–选择属性–选择兼容性标签-勾选"禁用全屏优化". 支 ...

  2. 计算机截图工具无法运行,win10系统打开截图工具提示“截图工具当前未在计算机上运行”的解决办法...

    win10系统使用久了,好多网友反馈说win10系统打开截图工具提示"截图工具当前未在计算机上运行"的问题,非常不方便.有什么办法可以永久解决win10系统打开截图工具提示&quo ...

  3. 如何删除tmp计算机桌面,教你Win10系统中tmp文件删除不了应该如何解决?

    电脑现已成为我们工作.生活和娱乐必不可少的工具了,在使用电脑的过程中,可能会遇到Win10系统中tmp文件删除不了应该如何解决的问题,如果我们遇到了Win10系统中tmp文件删除不了应该如何解决的情况 ...

  4. win10html5无法播放,win10系统中网页中无法播放视频怎么办

    近日有win10系统用户要通过浏览器来打开网页观看视频的时候,却发现在网页中打开视频的时却无法播放,这是怎么回事呢,经过分析是由于Adobe Flash Player ActiveX插件未安装.版本过 ...

  5. 如何删除tmp计算机桌面,Win10系统中tmp文件删除不了应该如何解决?

    win10系统中tmp文件删除不了怎么办?有Win10系统用户反应,有后缀名为TMP的文件怎么也删除不了,当电脑开机重启之后又会出现,那么遇到这个问题应该如何解决呢?接下来就为大家分享win10系统删 ...

  6. 个人计算机的防毒软件无法防御,在win10系统中无法启动defender防御软件的解决方法...

    软件大小: 3.56 MB 软件版本: 3.3.30.180 软件类型: 系统补丁 查看详情 直接下载 win10系统是我们现在都在使用的系统,下面小编为大家在win10系统中无法启动defender ...

  7. win10系统中photoshop cs6中界面字体太小的解决方法

    win10系统中photoshop cs6中界面字体太小的解决方法 参考文章: (1)win10系统中photoshop cs6中界面字体太小的解决方法 (2)https://www.cnblogs. ...

  8. win10计算机管理没有蓝牙,win10系统中缺少打开或关闭蓝牙选项的解决方法

    在win10系统中,自带有蓝牙功能,但是有时候在使用蓝牙的时候,发现设置应用程序或操作中心中缺少打开蓝牙的选项,遇到这样的问题该怎么办呢,本文就给大家讲解一下win10系统中缺少打开或关闭蓝牙选项的解 ...

  9. html win10虚拟键盘,Win10系统中内置虚拟键盘过大应该如何解决?

    win10系统中虚拟键盘过大怎么办?用过Win10系统的用户都知道,在系统是有内置虚拟键盘的,用来应付简配设备突然坏了的情况.但有用户表示开启的虚拟键盘太大,占据了太多电脑屏幕,将其他应用程序给遮挡住 ...

最新文章

  1. 转载一个不错的Scrapy学习博客笔记
  2. 东南亚ERP仓储管理系统怎么样?
  3. redis数据库操作(3)
  4. 怎么用bat关闭远程协助计算机,Windows批处理请求远程协助
  5. Hudson 之旅(二)
  6. Java中的Filter过滤器
  7. 【垂直切换】TD-SCDMA与TD-LTE异构网络垂直切换仿真
  8. 使用Md5加密算法对密码进行加密(工具类)
  9. IT程序员常去的论坛、社区、网站有哪些?
  10. win10装debian 双系统_如何安装win10和linux [ubuntu14]双系统
  11. 云计算机技术的运用,三分钟为你详细解析云计算技术与应用
  12. 面经_京东广告部_实习_广告推荐_I面_20191105
  13. linux临时文件放哪,如何保障Linux的临时文件安全?
  14. 3060显卡系列cuda11.1
  15. 顺丰科技数据库中间件研发工程师面试经历
  16. 淘宝评论接口调用展示
  17. 阿里、有道科大讯飞齐为荣耀Magic2打call,透露YOYO想不到的技能
  18. Java 通过EasyExcel导出的Excel文档的字体,背景色,自动列宽等符合要求
  19. [图文]历届奥斯卡影帝(中)
  20. memset()函数的使用详解

热门文章

  1. python机器学习库_Python机器学习库 Top 10,你值得拥有!
  2. anaconda如何做python笔记_Anaconda常用命令笔记
  3. 爬虫单个ip代理设置_爬虫怎么设置代理ip池?
  4. 2020-10-19 Keil安装及使用
  5. 单引号内的双引号内的双引号怎么写
  6. Linux/Unix系统编程手册 第三章:系统编程概念
  7. 【SSH之旅】一步学习的步Struts1相框(三):分析控制Struts1示例
  8. 思科网络CCNA的学习笔记-关于IP和子网的计算
  9. Python标准库07 信号 (signal包,部分os包)
  10. 符合.net准则的事件