屏幕截图






3.21/5 (8投票s)
2006年11月8日
3分钟阅读

56748

1867
一个屏幕截图应用程序。
引言
有很多可用的屏幕截图程序,但我对大多数程序并不满意。所以我自己制作了一个屏幕截图程序。该程序不会为您提供截取下拉菜单的功能,并且该功能被有意排除。如果您想自己实现它;您会在互联网上找到足够的信息。此外,它还涉及另一个作为 DLL 的模块,我想保持简单。
背景
完整的应用程序不仅涉及屏幕截图,还涉及其他主题,如 WTL、STL 等。
使用代码
CMainFrame
窗口内有三个类:WindowInfo
、CBorderWnd
和 CClientView
。
Windows 有许多有趣的属性,但该程序只对三个属性感兴趣,即窗口的标题、应用程序(或模块)名称和应用程序的线程 ID。该应用程序不使用面向对象/封装指南,因此所有内容都是公开的。
首先,您需要使用 WTL 向导创建一个 MDI 应用程序。
向导将在 MainForm
(头文件和 CPP)文件中创建 CMainFrame
类。以下类在 MainFrm.h 中声明
// class WindowInfo { public: WindowInfo(TCHAR* pstrWindowTitle=NULL, TCHAR* pstrModuleName=NULL, DWORD dwPID=0) { memset(m_strWindowTitle,0,_MAX_PATH); memset(m_strModuleName,0,_MAX_PATH); if (pstrWindowTitle != NULL) strcpy_s(m_strWindowTitle,pstrWindowTitle); if (pstrModuleName != NULL) strcpy_s(m_strModuleName,pstrModuleName); m_dwPID = dwPID; } TCHAR m_strWindowTitle[_MAX_PATH]; TCHAR m_strModuleName[_MAX_PATH]; DWORD m_dwPID; }; //
类 CBorderWnd
负责在另一个窗口周围绘制边框,并且仅处理 WM_ERASEBKGND
。
// class CBorderWnd : public CWindowImpl<CBorderWnd> { public: BEGIN_MSG_MAP(CBorderWnd) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) END_MSG_MAP() LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { RECT rect; GetClientRect(&rect); bHandled = TRUE; HDC hDC = (HDC)wParam; HPEN hRedPen = ::CreatePen(PS_SOLID, 16, RGB(255,0,0)); HPEN hOldPen = (HPEN) ::SelectObject(hDC,hRedPen); HBRUSH hOldBrush = (HBRUSH) SelectObject(hDC, (HBRUSH)GetStockObject(NULL_BRUSH)); Rectangle(hDC,rect.left,rect.top,rect.right,rect.bottom); ::SelectObject(hDC,hOldPen); ::SelectObject(hDC,hOldBrush); DeleteObject(hRedPen); return 0; } }; //
而 CClientView
将在 MDI 应用程序内绘制任何图片。
// class CClientView : public CWindowImpl<CClientView> { public: HBITMAP m_bmSonyCamera; SIZE m_size; BEGIN_MSG_MAP(CSlideView) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_CREATE, OnCreate) END_MSG_MAP() LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); }; //
当我们使用 new 关键字创建一个 view 时,WTL 将删除该内存位置,并且不会发生内存泄漏,但我们仍然希望跟踪所有 view。为此,包含了 vector<CChildFrame*> m_bmList;
。在创建 view 之前,我们还需要保留有关所有桌面窗口的信息,其中使用了 map<HWND,WindowInfo> m_HanldeList;
。EnumDesktopWindows
函数也需要一个静态函数。
static BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);
此应用程序有一个自定义绘制的组合框,在自定义绘制代码中,您可以创建一个字体并在使用后删除该字体,但处理它的最佳方法是通过以下方式将字体分配给对象(组合框)
::SendMessage(m_hWndComboBox,(UINT) WM_SETFONT,(WPARAM) m_hFont,TRUE);
这样,您只需要创建一次字体,并在退出时将其删除。
从图片中可以看到,组合框在工具栏内部,因此必须修改资源文件。根据工具栏中任何对象的大小,需要许多 SEPARATOR (8 像素)。在这种情况下,包含了 32 个 SEPARATOR。现在,制作那个组合框并将工具栏设置为父级。必须使用文本编辑器修改资源文件,否则向导将覆盖您的修改,结果可能不理想。
IDR_MAINFRAME TOOLBAR 16, 15 BEGIN BUTTON ID_FILE_NEW SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR SEPARATOR BUTTON ID_REFRESH BUTTON ID_EDIT_COPY BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_APP_ABOUT END
在 CMainFrame::Create
函数内部,创建了组合框和字体。
HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE); DWORD dwComboStyle = CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_CHILD | WS_VISIBLE; dwComboStyle |= CBS_HASSTRINGS | CBS_OWNERDRAWFIXED; m_hWndComboBox = ::CreateWindowEx(0,"COMBOBOX", NULL, dwComboStyle, 20,0,400,20,hWndToolBar, (HMENU) COMBOBOX_ID, _Module.m_hInst, NULL); m_hFont = CreateFont(-14, // nHeight 0, // nWidth 0, // nEscapement 0, // nOrientation FW_BOLD, // nWeight FALSE, // bItalic FALSE, // bUnderline 0, // cStrikeOut ANSI_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQuality DEFAULT_PITCH | FF_SWISS, // PitchAndFamily "Comic Sans MS"); ::SendMessage(m_hWndComboBox,(UINT) WM_SETFONT,(WPARAM) m_hFont,TRUE);
在 CMainFrame
内部,也实现了 WM_SIZE
,因此当大小发生变化时,CClientView
将更改其大小,并将图片放置在客户端区域的中间。这里使用了 PostMessage
,因此客户端始终必须找出父窗口的区域。SendMessage
将无法工作。
您还将找到一个刷新或窗口指示器按钮。当您按下它时,它将在组合框中选定项目的窗口周围放置一个边框。实际上,它是一个弹出窗口,其中窗口的中间用 NULL
画笔绘制。这就是我实现它的方式,或者您可以使用 DrawAnimatedRects
函数。
LRESULT CMainFrame::OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { HWND hSavedWnd = BringToTheTop(); Sleep(5); RECT rectFrom; ::GetWindowRect(hSavedWnd,&rectFrom); // RECT rectTo; // ::SetRect(&rectTo,rectFrom.left,rectFrom.top, // rectFrom.right,rectFrom.bottom); // ::InflateRect(&rectTo,-100,-100); // ::DrawAnimatedRects(hSavedWnd, IDANI_CAPTION, // &rectFrom, &rectTo); WindowInfo WInfo = m_HanldeList[hSavedWnd]; if (_stricmp(_T("Program Manager"),WInfo.m_strWindowTitle)) ::InflateRect(&rectFrom,8,8); int nWidth = rectFrom.right - rectFrom.left; int nHeight = rectFrom.bottom - rectFrom.top; ::SetWindowPos(m_BackGround,HWND_BOTTOM,rectFrom.left, rectFrom.top,nWidth,nHeight,SWP_SHOWWINDOW); Sleep(2000); ::SetWindowPos(m_BackGround,HWND_TOP,rectFrom.left, rectFrom.top,nWidth,nHeight,SWP_HIDEWINDOW); return 0; }该程序的核心是将枚举窗口添加到列表框中。我们只对应用程序窗口感兴趣(不是子窗口,或不可见的窗口,或零大小的窗口)。
void CMainFrame::AddToTheList(HWND hWnd) { TCHAR tchCurrentSelected[_MAX_PATH]; TCHAR tchBuffer[_MAX_PATH]; TCHAR tchClassName[_MAX_PATH]; TCHAR tchModuleName[_MAX_PATH]; HANDLE hProcess; ::GetWindowText(hWnd,tchCurrentSelected,_MAX_PATH); int nCount = 0; bool bChanged = false; do { bChanged = false; nCount = SendMessage(m_hWndComboBox, (UINT) CB_GETCOUNT,0,NULL); for (int i = 0; i < nCount; i++) { HWND hSavedWnd = (HWND) SendMessage(m_hWndComboBox, (UINT) CB_GETITEMDATA,i,NULL); if (::IsWindow(hSavedWnd) == FALSE) { ::SendMessage(m_hWndComboBox,CB_DELETESTRING,i,0); bChanged = true; map<HWND,WindowInfo>::iterator Pos = m_HanldeList.find(hSavedWnd); if (Pos != m_HanldeList.end()) m_HanldeList.erase(Pos); int nSel = ::SendMessage(m_hWndComboBox,CB_FINDSTRING, -1,(LPARAM) (LPCTSTR)tchCurrentSelected); int NewSel = (nSel == CB_ERR) ? 0 : nSel; SendMessage(m_hWndComboBox, (UINT) CB_SETCURSEL,NewSel,NULL); break; } } }while (bChanged); if (::IsWindow(hWnd) == FALSE || ::IsWindowVisible(hWnd) == FALSE || ::GetParent(hWnd) || hWnd == m_hWnd) return; ::GetWindowText(hWnd,tchBuffer,_MAX_PATH); if (strlen(tchBuffer) == 0) return; int nResult = ::SendMessage(m_hWndComboBox,CB_FINDSTRING, -1,(LPARAM) (LPCTSTR)tchBuffer); if (nResult != CB_ERR) { for (int i = 0; i < nCount; i++) { HWND hSavedWnd = (HWND) SendMessage(m_hWndComboBox, (UINT) CB_GETITEMDATA,i,NULL); if (hSavedWnd == hWnd) return; } } ::GetClassName(hWnd,tchClassName,_MAX_PATH); RECT rect; ::GetWindowRect(hWnd,&rect); // get pid DWORD dwPID; GetWindowThreadProcessId(hWnd, &dwPID); hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, dwPID); GetModuleFileNameEx(hProcess, NULL, tchModuleName, _MAX_PATH); CloseHandle(hProcess); BOOL bDoNoAccept = ::IsWindow(hWnd) == FALSE || ::IsWindowVisible(hWnd) == FALSE || ::GetParent(hWnd) || hWnd == m_hWnd; if (!bDoNoAccept) { map<HWND,WindowInfo>::iterator it = m_HanldeList.find(hWnd); bool bHidden = (rect.left == rect.right || rect.top == rect.bottom); if (it == m_HanldeList.end() && bHidden == false) { m_HanldeList.insert(std::make_pair(hWnd, WindowInfo(tchBuffer,tchModuleName,dwPID))); nResult = ::SendMessage(m_hWndComboBox,CB_ADDSTRING, 0,(LPARAM) (LPCTSTR)tchBuffer); if (nResult != CB_ERR) ::SendMessage(m_hWndComboBox,CB_SETITEMDATA, nResult,(LPARAM) (HWND)hWnd); nCount = SendMessage(m_hWndComboBox, (UINT) CB_GETCOUNT,0,NULL); int CurSel = SendMessage(m_hWndComboBox, (UINT) CB_GETCURSEL,0,NULL); if (nCount && CurSel == -1) SendMessage(m_hWndComboBox, (UINT) CB_SETCURSEL,0,NULL); } } }
正如您所看到的,首先,我们找到应用程序窗口,然后找到它的进程来获取其图标,以便我们可以在组合框中显示它。
我希望每个人都会喜欢这个程序。