@@ -489,6 +489,8 @@ BEGIN_MESSAGE_MAP(CScreenSpyDlg, CDialog)
ON_WM_VSCROLL ( )
ON_WM_LBUTTONDOWN ( )
ON_WM_LBUTTONUP ( )
ON_WM_RBUTTONDOWN ( )
ON_WM_RBUTTONUP ( )
ON_WM_MOUSEWHEEL ( )
ON_WM_MOUSEMOVE ( )
ON_WM_MOUSELEAVE ( )
@@ -689,7 +691,7 @@ BOOL CScreenSpyDlg::OnInitDialog()
if ( m_bIsCtrl ) {
ImmAssociateContext ( m_hWnd , NULL ) ; // 控制模式:禁用 IME
}
m_bIsTraceCursor = FALSE ; //不是跟踪
m_bIsTraceCursor = ! m_bIsCtrl ; // 非控制状态,则跟踪鼠标
m_ClientCursorPos . x = 0 ;
m_ClientCursorPos . y = 0 ;
m_bCursorIndex = 0 ;
@@ -1641,16 +1643,19 @@ void CScreenSpyDlg::OnPaint()
BitBlt ( m_hFullDC , 0 , 0 , srcW , srcH , m_hFullMemDC , m_ulHScrollPos , m_ulVScrollPos , SRCCOPY ) ;
}
// 绘制框选矩形
if ( m_bSelectingZoom ) {
CRect rcSelec t ;
rcSelect . left = min ( m_ptZoomStart . x , m_ptZoom Current . x ) ;
rcSelect . top = min ( m_ptZoomStart . y , m_ptZoomCurrent . y ) ;
rcSelect . right = max ( m_ptZoomStart . x , m_ptZoomCurrent . x ) ;
rcSelect . bottom = max ( m_ptZoomStart . y , m_ptZoomCurrent . y ) ;
// 绘制框选矩形(左键放大用红色,右键截图用绿色,二者颜色错开避免误操作)
if ( m_bSelectingZoom | | m_bSelectingShot ) {
CPoint ptStart = m_bSelectingZoom ? m_ptZoomStart : m_ptShotStar t ;
CPoint ptCur = m_bSelectingZoom ? m_ptZoomCurrent : m_ptShot Current ;
COLORREF clr = m_bSelectingZoom ? RGB ( 255 , 0 , 0 ) : RGB ( 0 , 180 , 0 ) ;
// 使用虚线边框绘制选择框
HPEN hPen = CreatePen ( PS_DASH , 1 , RGB ( 255 , 0 , 0 ) ) ;
CRect rcSelect ;
rcSelect . left = min ( ptStart . x , ptCur . x ) ;
rcSelect . top = min ( ptStart . y , ptCur . y ) ;
rcSelect . right = max ( ptStart . x , ptCur . x ) ;
rcSelect . bottom = max ( ptStart . y , ptCur . y ) ;
HPEN hPen = CreatePen ( PS_DASH , 1 , clr ) ;
HPEN hOldPen = ( HPEN ) SelectObject ( m_hFullDC , hPen ) ;
HBRUSH hOldBrush = ( HBRUSH ) SelectObject ( m_hFullDC , GetStockObject ( NULL_BRUSH ) ) ;
Rectangle ( m_hFullDC , rcSelect . left , rcSelect . top , rcSelect . right , rcSelect . bottom ) ;
@@ -2849,29 +2854,10 @@ void CScreenSpyDlg::OnLButtonUp(UINT nFlags, CPoint point)
}
// 将屏幕坐标转换为原图坐标
int srcW = m_BitmapInfor_Full - > bmiHeader . biWidth ;
int srcH = m_BitmapInfor_Full - > bmiHeader . biHeight ;
int dstW = m_CRect . Width ( ) ;
int dstH = m_CRect . Height ( ) ;
if ( m_bAdaptiveSize ) {
m_rcZoomSrc . left = ( int ) ( rcSelect . left * m_wZoom ) ;
m_rcZoomSrc . top = ( int ) ( rcSelect . top * m_hZoom ) ;
m_rcZoomSrc . right = ( int ) ( rcSelect . right * m_wZoom ) ;
m_rcZoomSrc . bottom = ( int ) ( rcSelect . bottom * m_hZoom ) ;
} else {
m_rcZoomSrc . left = rcSelect . left + m_ulHScrollPos ;
m_rcZoomSrc . top = rcSelect . top + m_ulVScrollPos ;
m_rcZoomSrc . right = rcSelect . right + m_ulHScrollPos ;
m_rcZoomSrc . bottom = rcSelect . bottom + m_ulVScrollPos ;
if ( ! ScreenRectToImageRect ( rcSelect , m_rcZoomSrc ) ) {
return ;
}
// 限制在原图范围内
m_rcZoomSrc . left = max ( 0L , min ( m_rcZoomSrc . left , ( LONG ) srcW ) ) ;
m_rcZoomSrc . top = max ( 0L , min ( m_rcZoomSrc . top , ( LONG ) srcH ) ) ;
m_rcZoomSrc . right = max ( 0L , min ( m_rcZoomSrc . right , ( LONG ) srcW ) ) ;
m_rcZoomSrc . bottom = max ( 0L , min ( m_rcZoomSrc . bottom , ( LONG ) srcH ) ) ;
// 进入放大状态
m_bZoomedIn = true ;
Invalidate ( ) ;
@@ -2897,6 +2883,145 @@ void CScreenSpyDlg::OnLButtonUp(UINT nFlags, CPoint point)
}
void CScreenSpyDlg : : OnRButtonDown ( UINT nFlags , CPoint point )
{
// 非控制模式下:右键框选 → 截图保存。控制模式下右键由 PreTranslateMessage 转发给客户端。
if ( ! m_bIsCtrl & & ! m_bIsFirst & & m_BitmapInfor_Full ) {
// 与左键互斥:左键正在框选/拖拽时不接管右键,避免冲突
if ( m_bSelectingZoom | | m_bZoomDragging ) {
return ;
}
m_bSelectingShot = true ;
m_ptShotStart = point ;
m_ptShotCurrent = point ;
SetCapture ( ) ;
return ;
}
__super : : OnRButtonDown ( nFlags , point ) ;
}
void CScreenSpyDlg : : OnRButtonUp ( UINT nFlags , CPoint point )
{
if ( ! m_bIsCtrl & & ! m_bIsFirst & & m_BitmapInfor_Full & & m_bSelectingShot ) {
ReleaseCapture ( ) ;
m_bSelectingShot = false ;
CRect rcSelect ;
rcSelect . left = min ( m_ptShotStart . x , point . x ) ;
rcSelect . top = min ( m_ptShotStart . y , point . y ) ;
rcSelect . right = max ( m_ptShotStart . x , point . x ) ;
rcSelect . bottom = max ( m_ptShotStart . y , point . y ) ;
// 太小视为误触(与左键放大同阈值)
if ( rcSelect . Width ( ) < 20 | | rcSelect . Height ( ) < 20 ) {
Invalidate ( FALSE ) ;
return ;
}
CRect rcImage ;
if ( ScreenRectToImageRect ( rcSelect , rcImage ) & &
rcImage . Width ( ) > 0 & & rcImage . Height ( ) > 0 )
{
SaveRegionScreenshot ( rcImage ) ;
}
Invalidate ( FALSE ) ; // 清掉绿色选框
return ;
}
__super : : OnRButtonUp ( nFlags , point ) ;
}
// 屏幕(窗口)选框 → 原图坐标,考虑放大状态、自适应、滚动
bool CScreenSpyDlg : : ScreenRectToImageRect ( const CRect & rcScreen , CRect & rcImage )
{
if ( ! m_BitmapInfor_Full ) return false ;
int srcW = m_BitmapInfor_Full - > bmiHeader . biWidth ;
int srcH = m_BitmapInfor_Full - > bmiHeader . biHeight ;
if ( srcW < = 0 | | srcH < = 0 ) return false ;
if ( m_bZoomedIn & & ! m_rcZoomSrc . IsRectEmpty ( ) ) {
// 放大状态:屏幕坐标 → 当前可视的子区域内的原图坐标
int dstW = m_CRect . Width ( ) ;
int dstH = m_CRect . Height ( ) ;
if ( dstW < = 0 | | dstH < = 0 ) return false ;
double scaleX = ( double ) m_rcZoomSrc . Width ( ) / dstW ;
double scaleY = ( double ) m_rcZoomSrc . Height ( ) / dstH ;
rcImage . left = ( int ) ( m_rcZoomSrc . left + rcScreen . left * scaleX ) ;
rcImage . top = ( int ) ( m_rcZoomSrc . top + rcScreen . top * scaleY ) ;
rcImage . right = ( int ) ( m_rcZoomSrc . left + rcScreen . right * scaleX ) ;
rcImage . bottom = ( int ) ( m_rcZoomSrc . top + rcScreen . bottom * scaleY ) ;
} else if ( m_bAdaptiveSize ) {
rcImage . left = ( int ) ( rcScreen . left * m_wZoom ) ;
rcImage . top = ( int ) ( rcScreen . top * m_hZoom ) ;
rcImage . right = ( int ) ( rcScreen . right * m_wZoom ) ;
rcImage . bottom = ( int ) ( rcScreen . bottom * m_hZoom ) ;
} else {
rcImage . left = rcScreen . left + m_ulHScrollPos ;
rcImage . top = rcScreen . top + m_ulVScrollPos ;
rcImage . right = rcScreen . right + m_ulHScrollPos ;
rcImage . bottom = rcScreen . bottom + m_ulVScrollPos ;
}
// 限制在原图范围内
rcImage . left = max ( 0L , min ( rcImage . left , ( LONG ) srcW ) ) ;
rcImage . top = max ( 0L , min ( rcImage . top , ( LONG ) srcH ) ) ;
rcImage . right = max ( 0L , min ( rcImage . right , ( LONG ) srcW ) ) ;
rcImage . bottom = max ( 0L , min ( rcImage . bottom , ( LONG ) srcH ) ) ;
return true ;
}
// 把原图中 [rcImage] 区域裁出来,写成独立 BMP( 24bpp 或 32bpp 由源图决定)
void CScreenSpyDlg : : SaveRegionScreenshot ( const CRect & rcImage )
{
if ( ! m_BitmapInfor_Full | | ! m_BitmapData_Full ) return ;
if ( rcImage . Width ( ) < = 0 | | rcImage . Height ( ) < = 0 ) return ;
auto path = GetScreenShotPath ( this , m_IPAddress , _TR ( " 位图文件(*.bmp)|*.bmp| " ) , " bmp " ) ;
if ( path . empty ( ) ) return ;
// 源 DIB 是 BGR 24bpp 或 BGRA 32bpp, bottom-up( biHeight > 0)
const BITMAPINFOHEADER & srcHdr = m_BitmapInfor_Full - > bmiHeader ;
int bpp = srcHdr . biBitCount ;
if ( bpp ! = 24 & & bpp ! = 32 ) return ; // 仅支持当前实际使用的两种位深
int srcW = srcHdr . biWidth ;
int srcH = srcHdr . biHeight ;
int srcStride = ( ( srcW * bpp + 31 ) / 32 ) * 4 ;
int dstW = rcImage . Width ( ) ;
int dstH = rcImage . Height ( ) ;
int dstStride = ( ( dstW * bpp + 31 ) / 32 ) * 4 ;
int dstSize = dstStride * dstH ;
std : : vector < BYTE > dstPixels ( dstSize , 0 ) ;
const BYTE * srcBase = ( const BYTE * ) m_BitmapData_Full ;
// bottom-up: 原图第 y 行(从顶起算)位于 srcBase + (srcH - 1 - y) * srcStride
int byteX = rcImage . left * ( bpp / 8 ) ;
int copyBytes = dstW * ( bpp / 8 ) ;
for ( int y = 0 ; y < dstH ; + + y ) {
int srcRowFromTop = rcImage . top + y ;
int srcRowOffset = ( srcH - 1 - srcRowFromTop ) * srcStride + byteX ;
int dstRowOffset = ( dstH - 1 - y ) * dstStride ;
memcpy ( & dstPixels [ dstRowOffset ] , & srcBase [ srcRowOffset ] , copyBytes ) ;
}
// 拼装 BITMAPINFO( 裁剪后只需要 BITMAPINFOHEADER; 24/32bpp 不需要调色板)
BITMAPINFO dstBmi = { } ;
dstBmi . bmiHeader = srcHdr ;
dstBmi . bmiHeader . biWidth = dstW ;
dstBmi . bmiHeader . biHeight = dstH ;
dstBmi . bmiHeader . biSizeImage = dstSize ;
dstBmi . bmiHeader . biCompression = BI_RGB ;
if ( WriteBitmap ( & dstBmi , dstPixels . data ( ) , path ) ) {
m_strSaveNotice = path ;
m_nSaveNoticeTime = GetTickCount64 ( ) ;
}
}
BOOL CScreenSpyDlg : : OnMouseWheel ( UINT nFlags , short zDelta , CPoint pt )
{
// Convert screen coordinates to client coordinates
@@ -2926,6 +3051,11 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
Invalidate ( FALSE ) ; // FALSE表示不擦除背景, 减少闪烁
return ;
}
if ( m_bSelectingShot ) {
m_ptShotCurrent = point ;
Invalidate ( FALSE ) ;
return ;
}
if ( m_bZoomDragging ) {
// 拖拽平移:计算偏移量并移动放大区域
@@ -3060,9 +3190,14 @@ void CScreenSpyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
void CScreenSpyDlg : : UpdateCtrlStatus ( BOOL ctrl )
{
m_bIsCtrl = ctrl ;
// 进入控制模式时重置放大状态
if ( m_bIsCtrl & & m_bZoomedIn ) {
ResetZoom ( ) ;
// 进入控制模式时重置放大状态 + 中止任何正在进行的右键截图框选
if ( m_bIsCtrl ) {
if ( m_bZoomedIn ) ResetZoom ( ) ;
if ( m_bSelectingShot ) {
m_bSelectingShot = false ;
if ( GetCapture ( ) = = this ) ReleaseCapture ( ) ;
Invalidate ( FALSE ) ;
}
}
SetClassLongPtr ( m_hWnd , GCLP_HCURSOR , m_bIsCtrl ? ( LONG_PTR ) m_hRemoteCursor : ( LONG_PTR ) LoadCursor ( NULL , IDC_NO ) ) ;
// 控制模式:禁用本地 IME; 查看模式: 启用本地 IME
@@ -3072,9 +3207,10 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
void CScreenSpyDlg : : OnCaptureChanged ( CWnd * pWnd )
{
// 捕获丢失时重置框选/拖拽状态
if ( m_bSelectingZoom | | m_bZoomDragging ) {
if ( m_bSelectingZoom | | m_bZoomDragging | | m_bSelectingShot ) {
m_bSelectingZoom = false ;
m_bZoomDragging = false ;
m_bSelectingShot = false ;
Invalidate ( ) ;
}
__super : : OnCaptureChanged ( pWnd ) ;