自制截图工具V1.0

之前比较空闲,就尝试着做了个截图工具,功能还算实用。等以后有时间再研究V2.0。

这个项目都是用C语言做的,所以还算精简。

#include <windows.h>
#include <stdio.h>
#include "resource.h"

#pragma comment(lib,"winmm.lib")

HINSTANCE g_hInstance = NULL;//全局进程句柄
HWND g_hwndMain = NULL;//全局主窗口句柄

#define UM_SCREENCUT	WM_USER+1
#define UM_CUTSUCCESS	WM_USER+2

INT_PTR CALLBACK MyDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK MyScreenCutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

//Win程序的入口
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	g_hInstance = hInstance;
	DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),(HWND)NULL,MyDialogProc);//对话框模态显示
	return 0;
}

//批量处理按钮
void EnableButtons(HWND hwndDlg, BOOL flag, int num, ...)
{
	int i = 0;
	int iButton = 0;
	va_list var_arg;
	va_start(var_arg,num);
	for(; i < num; i++)
	{
		iButton = va_arg(var_arg,int);
		EnableWindow(GetDlgItem(hwndDlg,iButton), flag);
	}
	va_end(var_arg);
}

//保存截图图片
BOOL SavePicture(HBITMAP hbitmap,TCHAR* filepath)
{
	HDC hdc= GetDC(NULL);
	BITMAP bitmap = {0};
	WORD clrbits = 0;
	BITMAPINFO* bitmapinfo = NULL;
	BITMAPFILEHEADER fileheader;
	DWORD temp;
	void* buf = NULL;
	FILE* fp = NULL;

	GetObject(hbitmap,sizeof(BITMAP),&bitmap);
	clrbits = bitmap.bmBitsPixel * bitmap.bmPlanes;//每点的颜色位数*平面数
	if(clrbits == 1) ;
	else if(clrbits <= 4) clrbits = 4;
	else if(clrbits <= 8) clrbits = 8;
	else if(clrbits <= 16) clrbits = 16;
	else if(clrbits <= 24) clrbits = 24;
	else clrbits = 32;
	
	if(clrbits < 24)//24位以下需要调色板
		bitmapinfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*(1<<clrbits));
	else
		bitmapinfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD));

	bitmapinfo->bmiHeader.biBitCount = clrbits;//图像颜色位数
	bitmapinfo->bmiHeader.biClrImportant = 0;
	bitmapinfo->bmiHeader.biClrUsed = (clrbits<24) ? (1<<clrbits) : 0;//使用的颜色数
	bitmapinfo->bmiHeader.biCompression = BI_RGB;//压缩方式,BI_RGB不压缩
	bitmapinfo->bmiHeader.biHeight = bitmap.bmHeight;//位图高度
	bitmapinfo->bmiHeader.biPlanes = bitmap.bmPlanes;//设备平面数
	bitmapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);//BITMAPINFOHEADER结构体大小
	bitmapinfo->bmiHeader.biSizeImage = (bitmap.bmWidth * clrbits + 31) / 32 * 4 * bitmap.bmHeight;//位图数据大小
	bitmapinfo->bmiHeader.biWidth = bitmap.bmWidth;//位图宽度
	bitmapinfo->bmiHeader.biXPelsPerMeter = bitmapinfo->bmiHeader.biYPelsPerMeter = 0;

	fileheader.bfType = 0x4d42;
	fileheader.bfSize = sizeof(BITMAPFILEHEADER) + bitmapinfo->bmiHeader.biSize + 
		sizeof(RGBQUAD)*(bitmapinfo->bmiHeader.biClrUsed==0 ? 0 : bitmapinfo->bmiHeader.biClrUsed)
		+ bitmapinfo->bmiHeader.biSizeImage;
	fileheader.bfReserved1 = fileheader.bfReserved2 = 0;
	fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 
		sizeof(RGBQUAD)*(bitmapinfo->bmiHeader.biClrUsed==0 ? 0 : bitmapinfo->bmiHeader.biClrUsed);

	buf = malloc(bitmapinfo->bmiHeader.biSizeImage);
	GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,buf,bitmapinfo,DIB_RGB_COLORS);

	fp = _wfopen(filepath,TEXT("wb"));
	fwrite(&fileheader,sizeof(BITMAPFILEHEADER),1,fp);
	fwrite(bitmapinfo,sizeof(BITMAPINFO),1,fp);
	fwrite(buf,bitmapinfo->bmiHeader.biSizeImage,1,fp);
	fclose(fp);

	ReleaseDC(NULL,hdc);
}

//主对话框回调函数
INT_PTR CALLBACK MyDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL bRet = TRUE;//默认已处理
	RECT rWindow = {0};//窗口大小矩形
	int iXscreen = 0;//屏幕宽度
	static HWND hScreenCut = NULL;//截图窗口句柄
	HICON hicon = NULL;
	HBITMAP hbitmap = NULL;//用来获取剪切板中的HBITMAP
	BITMAP bm = {0};
	HDC hdc = NULL;
	HDC hdc2 = NULL;

	switch(uMsg)
	{
	case WM_CLOSE:
		SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_CANCEL,0),0);
		EndDialog(hwndDlg,0);
		break;
	case WM_INITDIALOG://没有WM_CREATE
		g_hwndMain = hwndDlg;
		hicon = LoadIcon(g_hInstance,MAKEINTRESOURCE(IDI_APPICON));
		if(hicon)
		{
			SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)hicon);//修改程序图标
			SendMessage(hwndDlg,WM_SETICON,ICON_BIG,(LPARAM)hicon);
			FreeResource(hicon);
		}
		EnableButtons(hwndDlg, TRUE, 1, IDC_BUTTON_START);
		EnableButtons(hwndDlg, FALSE, 4,IDC_BUTTON_CUT, IDC_BUTTON_RESTART, IDC_BUTTON_SAVE, IDC_BUTTON_CANCEL);
		iXscreen = GetSystemMetrics(SM_CXSCREEN);
		GetWindowRect(hwndDlg,&rWindow);
		MoveWindow(hwndDlg, iXscreen - rWindow.right - 50, 50, rWindow.right - rWindow.left, rWindow.bottom - rWindow.top, TRUE);
		break;
	case UM_CUTSUCCESS://截图成功
		SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_CANCEL,0),0);
		EnableButtons(hwndDlg,TRUE,1,IDC_BUTTON_SAVE);
		break;
	case WM_COMMAND://子窗口控件给父窗口发送的消息
		switch(LOWORD(wParam))//LOWORD(wParam)控件ID
		{
		case IDC_BUTTON_START://start
			if(hScreenCut)
			{
				DestroyWindow(hScreenCut);
				hScreenCut = NULL;
			}
			hScreenCut = CreateDialog(g_hInstance,MAKEINTRESOURCE(IDD_DIALOG_SCREENCUT),NULL,MyScreenCutProc);//这里父窗口句柄参数为NULL
			if(hScreenCut)
			{
				ShowWindow(hScreenCut,SW_SHOW);
				EnableButtons(hwndDlg,TRUE, 2,IDC_BUTTON_RESTART,IDC_BUTTON_CANCEL);
				EnableButtons(hwndDlg,FALSE,3,IDC_BUTTON_START,IDC_BUTTON_CUT,IDC_BUTTON_SAVE);
				/*SetWindowPos(hwndDlg,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);*/
			}
			break;
		case IDC_BUTTON_RESTART:
		case IDC_BUTTON_CANCEL://cancel
			if(hScreenCut)
			{
				DestroyWindow(hScreenCut);
				hScreenCut = NULL;
			}
			EnableButtons(hwndDlg, TRUE, 1, IDC_BUTTON_START);
			EnableButtons(hwndDlg, FALSE, 4,IDC_BUTTON_CUT, IDC_BUTTON_RESTART, IDC_BUTTON_SAVE, IDC_BUTTON_CANCEL);
			if(IDC_BUTTON_RESTART == LOWORD(wParam))
			{
				SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_START,0),0);
			}
			break;
		case IDC_BUTTON_SAVE:
			if(IsClipboardFormatAvailable(CF_BITMAP))
			{
				if(OpenClipboard(hwndDlg))
				{
					hbitmap = GetClipboardData(CF_BITMAP);
					SavePicture(hbitmap,TEXT("hello.bmp"));
					CloseClipboard();
				}
			}
			break;
		case IDC_BUTTON_CUT:
			SendMessage(hScreenCut,UM_SCREENCUT,0,0);
			break;
		default:
			bRet = FALSE;
			break;
		}
		break;
	default:
		bRet = FALSE;
		break;
	}

	return bRet;
}

//截图对话框回调函数
INT_PTR CALLBACK MyScreenCutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL bRet = TRUE;//默认标记TRUE表示已处理消息
	HDC ScreenDC = NULL;//屏幕DC
	static HDC MemDC = NULL;//内存DC
	HDC ClientDC = NULL;//窗口DC
	static HBRUSH hbrush = NULL;
	static int iXscreen = 0;//屏幕宽度
	static int iYscreen = 0;//屏幕高度
	static RECT RectCut = {0};//截图区域
	static POINT PointOrg = {0};//原点
	int temp = 0;

	int width = 0;
	int height = 0;
	HDC CutDC = NULL;
	HBITMAP hbitmap = NULL, hbitmap2 = NULL;
	BITMAP bm;//图像信息

	switch(uMsg)
	{
	case WM_INITDIALOG:
		ScreenDC = GetDC(NULL);
		if(!ScreenDC)
		{
			DestroyWindow(hwndDlg);
			break;
		}
		iXscreen = GetSystemMetrics(SM_CXSCREEN);
		iYscreen = GetSystemMetrics(SM_CYSCREEN);
		MemDC = CreateCompatibleDC(ScreenDC);
		hbitmap = CreateCompatibleBitmap(ScreenDC,iXscreen,iYscreen);
		if(!MemDC || !hbitmap)
		{
			DeleteObject(hbitmap);
			hbitmap = NULL;
			ReleaseDC(hwndDlg,ScreenDC);
			DestroyWindow(hwndDlg);
			break;
		}
		SelectObject(MemDC,hbitmap);
		BitBlt(MemDC,0,0,iXscreen,iYscreen,ScreenDC,0,0,SRCCOPY);
		DeleteObject(hbitmap);
		hbitmap = NULL;
		ReleaseDC(hwndDlg,ScreenDC);
		hbrush = CreateSolidBrush(RGB(255,0,0));
		/*SendMessage(hwndDlg,WM_SYSCOMMAND,SC_MAXIMIZE,0);*/
		MoveWindow(hwndDlg,0,0,iXscreen,iYscreen,FALSE);
		break;
	case WM_PAINT:
		if(MemDC)
		{
			if(ClientDC = GetDC(hwndDlg))
			{
				BitBlt(ClientDC,0,0,iXscreen,iYscreen,MemDC,0,0,SRCCOPY);
				if(!IsRectEmpty(&RectCut))//IsRectEmpty当矩形的宽度或高度等于0或小于0都算空!
				{
					FrameRect(ClientDC,&RectCut,hbrush ? hbrush : GetStockObject(BLACK_BRUSH));
					EnableButtons(g_hwndMain,TRUE,1,IDC_BUTTON_CUT);
				}
				else
				{
					EnableButtons(g_hwndMain,FALSE,1,IDC_BUTTON_CUT);
				}
				ReleaseDC(hwndDlg,ClientDC);
				ClientDC = NULL;
				break;
			}
		}
		break;
	case WM_LBUTTONDOWN://WM_LBUTTONDOWN消息中LOWORD(lParam)是鼠标的x坐标(客户坐标),HIWORD(lParam)是鼠标的y坐标(客户坐标)
		RectCut.left = RectCut.right = LOWORD(lParam);
		RectCut.top = RectCut.bottom = HIWORD(lParam);
		PointOrg.x = RectCut.left;
		PointOrg.y = RectCut.top;
		EnableButtons(g_hwndMain,FALSE,1,IDC_BUTTON_CUT);
		break;
	case WM_MOUSEMOVE:
		if(MK_LBUTTON & wParam)//WM_MOUSEMOVE消息的wParam参数有一些状态标记
		{
			if(PointOrg.x > LOWORD(lParam))
			{
				RectCut.left = LOWORD(lParam);
				RectCut.right = PointOrg.x;
			}
			else
			{
				RectCut.left = PointOrg.x;
				RectCut.right = LOWORD(lParam);
			}
			if(PointOrg.y > HIWORD(lParam))
			{
				RectCut.top = HIWORD(lParam);
				RectCut.bottom = PointOrg.y;
			}
			else
			{
				RectCut.top = PointOrg.y;
				RectCut.bottom = HIWORD(lParam);
			}
		}
		break;
	case UM_SCREENCUT:
		width = RectCut.right - RectCut.left;
		height = RectCut.bottom - RectCut.top;
		CutDC = CreateCompatibleDC(MemDC);
		hbitmap = CreateCompatibleBitmap(MemDC,width,height);
		SelectObject(CutDC,hbitmap);
		BitBlt(CutDC,0,0,width,height,MemDC,RectCut.left,RectCut.top,SRCCOPY);
		if(OpenClipboard(hwndDlg))
		{
			EmptyClipboard();
			SetClipboardData(CF_BITMAP,hbitmap);
			CloseClipboard();
			PlaySound(TEXT("kd.wav"),g_hInstance,SND_FILENAME | SND_ASYNC);
			SendMessage(g_hwndMain,UM_CUTSUCCESS,0,0);
		}
		DeleteObject(hbitmap);
		DeleteDC(CutDC);
		break;
	case WM_KEYDOWN:
		if(wParam == VK_ESCAPE)//ESC
		{
			SendMessage(g_hwndMain,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_CANCEL,0),0);
		}
		break;
	case WM_DESTROY:
		if(MemDC)
		{
			DeleteDC(MemDC);
			MemDC = NULL;
		}
		SetRectEmpty(&RectCut);
		PointOrg.x = PointOrg.y = 0;
		break;
	default:
		bRet = FALSE;
		break;
	}

	return bRet;
}

工程链接:http://download.csdn.net/detail/gongluck93/9701535

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏曾大稳的博客

结合ffmpeg用SDL播放YUV实现简易播放器

通过解码之后得到的yuv视频数据我们直接可以进行播放,本篇使用SDL来实现视频播放。

741
来自专栏函数式编程语言及工具

Akka(30): Http:High-Level-Api,Routing DSL

  在上篇我们介绍了Akka-http Low-Level-Api。实际上这个Api提供了Server对进来的Http-requests进行处理及反应的自定义F...

19310
来自专栏码匠的流水账

spring security reactive获取security context

本文主要研究下reactive模式下的spring security context的获取。

1212
来自专栏wOw的Android小站

[Android][Framework]系统手势和状态栏

里面switch语句处理的操作也是MotionEvent最基本的操作。这里主要看一下从上往下滑动的事件是怎么计算的:

1271
来自专栏PPV课数据科学社区

【学习】七天搞定SAS(三):基本模块调用

搞定基本的函数之后,开始鼓捣SAS里面的模型。也就是说,要开始写PROC了。说实话,越学SAS,越觉得SAS像Stata...无论是从输出的样式,还是语法。好不...

2865
来自专栏c#开发者

domaincontext load 回调

This post is not specific to RIA Services but I thought I'd add it to the title ...

3259
来自专栏函数式编程语言及工具

Akka(31): Http:High-Level-Api,Route rejection handling

   Route 是Akka-http routing DSL的核心部分,使用户能比较方便的从http-server的角度筛选http-request、进行se...

2207
来自专栏Hongten

spring开发_JDBC操作MySQL数据库_使用xml配置事务管理

http://www.cnblogs.com/hongten/archive/2012/03/09/java_spring_jdbc.html

581
来自专栏xingoo, 一个梦想做发明家的程序员

windows程序设计-第四章 system2.c 新增滚动条功能

新添加的滚动条功能,首先就是要在createWindow的时候,修改参数风格模式 hwnd = CreateWindow (szAppName, TE...

1839
来自专栏.net core新时代

数据字典生成工具之旅(9):多线程使用及介绍

      这一篇将在之前的代码生成器上讲解多线程的应用,多线程的概念和好处这里就不多说了,另外从本篇开始后面的实例代码都将放到SVN管理工具上维护,大家可以直...

1776

扫码关注云+社区