C#高仿腾讯QQ截图程序

最近把以前制作的截图程序重新写了一下 动了一个大手术 高质量仿照的TX的截图程序

源码下载1(2012-12-09)

http://download.csdn.net/detail/crystal_lz/4863320


源码下载2(2012-12-15)

http://download.csdn.net/download/crystal_lz/4889763


新改版见:http://blog.csdn.net/crystal_lz/article/details/9200859

- -!、发现bug 在文章后面更改

先看几个效果图

拖动过程中显示当前鼠标下一小块的图像信息 尺寸、颜色信息的  注意 这里颜色是用的ARGB 本来截图的话RGB就够了 可是我把那个做成了控件 不仅截图可用 其他地方也可用作图像的选取 具体看代码就知道了



并且我还加了一个可以截图的同时把鼠标也捕获下来 现在看到的是我自己的截图程序 那个工具条啥的 是从TX的截图程序上面拔下来的


C#高仿腾讯QQ截图程序_第1张图片

上面是几个工具条上的工具的三种粗细型号的展示 看到的蓝色的粗的刷笔 本来想的不应该是这个效果的 应该是颜色填充均匀的那种 但是仔细一想代码中用的是DrawLine(P,PointLast,PointCurrent);这种方式来画的自由线条 如果是一个像素的没什么问题 粗点的就是上面看到的那种效果 也就是由许多小线段拼接出来的自由线条而每个线段两端都是方的所以线段与线段之间的接缝处 可能就有问题 反正就那个意思 你懂得  不过再仔细一想虽然不是想要的效果但却意外的出现了 蜡笔的效果 果断也就不改了这样也不错


C#高仿腾讯QQ截图程序_第2张图片

同样的具有自动捕获窗体边框的功能


C#高仿腾讯QQ截图程序_第3张图片

使用也非常简单 这个截图的功能写到了一个dll中 引用名称空间 然后FrmCapture 就是截图的了给了他几个属性 也就是上面看到的

同时在拖动过程中可以通过键盘 wasd 四个键来控制鼠标精确移动

操作方式也和TX的一样

右键鼠标 如果有选择的区域则取消选择的区域   没有则退出截图

双击将选择的区域复制到剪切板


不过话说回来 平时聊天的时候 或许常用到截图 但是除了聊天外就很少用到截图了

大多数时候弹出 alt + ctrl + a 都不是为了截图 而是为了框选一下某一个区域的大小 或者 在屏幕上去选择一个点的颜色信息(所以我搞了 w a s d 四个键来精确移动)

所以显示坐标、尺寸、还有颜色信息的那些功能做得比较全

工具栏上面的 工具 我也就是象征性的做了一下 前面几个都还成 字体哪里就有点水了 TX的可以选择字号 而我还是只有三种选择主要是为了 和前面的公用那个调色和选择大小的那一个控件 这样就不用再搞新的东西了(我怀疑工具条上面的一些东西 对于大多数其他人来说 估计用都没有用过)

==========================分割线=======================

在上面的 源码下载2 中把自己修改的自己用的放了上来 和源码1相比 修复了已知的双击的那个bug - -!、、坑爹的发现 写到这里脑子又想起一个bug 就是 如果截图好了弹出保存对话框后 然后右键会取消截图 然后再右键截图关闭 关闭了倒好没啥 可是 弹出保存对话框后取消截图保存的时候就异常了  不过一般情况下貌似只要正常操作是不会出现这个问题了  算了一会儿还是把解决办法搞到下面 这里就说说我改了后的源码 (对了 在弹出保存对话框的时候默认是 保存bmp格式 如果不习惯把对话框索引改一下就行了)

- -!、其实也没改啥源码出了修复bug直接新建了一个工程导入dll然后写代码

C#高仿腾讯QQ截图程序_第4张图片

Oh God 突然发现窗体上面的 Other 写错了 坑爹啊 下载自己改吧、、、

这个是设置窗体可以自定义快捷键 还有是否开机自起 是否捕获鼠标(开始运行不会出现窗体 隐藏启动在托盘显示)

我一直觉得那里不对劲 终于发现了 那个最小化按钮还在 居然忘了 要么屏蔽最小化按钮(this.MinimizeBox = false;)要么直接设置成toolwindow

this.FormBorderStyle = FormBorderStyle.FixedToolWindow;

C#高仿腾讯QQ截图程序_第5张图片

注意如果要设置开机自起的话 务必确保你的程序的路径开机后能找到 也就是要保持你程序的当前路径 因为开机自启的时候 是获取的程序的当前路径然后写入注册表的

反正我是把它放到我的文档下面然后新建了一个文件夹

C#高仿腾讯QQ截图程序_第6张图片

那个dll是截图的模块 exe是那个设置的窗体 那个cfg保存的是设置的信息(默认情况下是木有滴 是要设置后生成滴)  要放哪里看个人爱好 如果你要选择自起的话



整个思路也和前面几篇文章中提到的一样 只是在代码层面上动了一个大手术 因为原来写的主要是凸显那个自动捕获窗体的功能

//根据鼠标位置找寻窗体平绘制边框
private void FoundAndDrawWindowRect() {
	Win32.LPPOINT pt = new Win32.LPPOINT();
	pt.X = MousePosition.X; pt.Y = MousePosition.Y;
	IntPtr hWnd = Win32.ChildWindowFromPointEx(Win32.GetDesktopWindow(), pt,
		Win32.CWP_SKIPINVISIBL | Win32.CWP_SKIPDISABLED);
	if (hWnd != IntPtr.Zero) {
		IntPtr hTemp = hWnd;
		while (true) {      //循环的根据坐标向内部找寻子窗体 直到无法找到位置
			Win32.ScreenToClient(hTemp, out pt);
			hTemp = Win32.ChildWindowFromPointEx(hTemp, pt, Win32.CWP_All);
			if (hTemp == IntPtr.Zero || hTemp == hWnd)
				break;
			hWnd = hTemp;
			pt.X = MousePosition.X; pt.Y = MousePosition.Y; //坐标还原为屏幕坐标
		}
		Win32.LPRECT rect = new Win32.LPRECT();
		Win32.GetWindowRect(hWnd, out rect);
		imageProcessBox1.SetSelectRect(
			new Rectangle(rect.Left, rect.Top, 
			rect.Right - rect.Left, rect.Bottom - rect.Top));
	}
}

同前几篇文章一样是通过禁用自身窗体然后通过ChildWindowFromPointEx函数来根据鼠标位置 获得鼠标下面的窗体 因为在获取的时候 鼠标下面是截图程序的一个窗体 所以在找寻窗体的时候得把自己忽略掉 而ChildWindowFromPointEx在查找过程中可以忽略禁用的窗体 所以讲自己禁用就到到目的了 然后通过Hook来监视鼠标的行为 来恢复禁用的窗体

private void m_MHook_MHookEvent(object sender, MHookEventArgs e) {
	
	........
	
	//鼠标点下恢复窗体禁用
	if (e.MButton == ButtonStatus.LeftDown || e.MButton == ButtonStatus.RightDown) {
		this.Enabled = true;
		imageProcessBox1.IsDrawOperationDot = true;
	}
	
	........
}

还有一点 就只捕获鼠标的时候

//获取桌面图像
private Bitmap GetScreen() {
	Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
			Screen.PrimaryScreen.Bounds.Height);
	if (this.isCaptureCursor) {     //是否捕获鼠标
		//如果直接将捕获当的鼠标画在bmp上 光标不会反色 指针边框也很浓 也就是说
		//尽管bmp上绘制了图像 绘制鼠标的时候还是以黑色作为鼠标的背景 然后在将混合好的鼠标绘制到图像 会很别扭
		//所以 干脆直接在桌面把鼠标绘制出来再截取桌面
		using (Graphics g = Graphics.FromHwnd(IntPtr.Zero)) {   //传入0默认就是桌面 Win32.GetDesktopWindow()也可以
			Win32.PCURSORINFO pci;
			pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Win32.PCURSORINFO));
			Win32.GetCursorInfo(out pci);
			if (pci.hCursor != IntPtr.Zero) {
				Cursor cur = new Cursor(pci.hCursor);
				g.CopyFromScreen(0, 0, 0, 0, bmp.Size); //在桌面绘制鼠标前 先在桌面绘制一下当前的桌面图像
				//如果不绘制当前桌面 那么cur.Draw的时候会是用历史桌面的快照 进行鼠标的混合 那么到时候混出现底色(测试中就是这样的)
				cur.Draw(g, new Rectangle((Point)((Size)MousePosition - (Size)cur.HotSpot), cur.Size));
			}
		}
	}
	//做完以上操作 才开始捕获桌面图像
	using (Graphics g = Graphics.FromImage(bmp)) {
		g.CopyFromScreen(0, 0, 0, 0, bmp.Size);
	}
	return bmp;
}

我总感觉上面的方式很别扭 可是目前我也就只能通过这种方式去捕获鼠标了

有兴趣的就自己改造吧 导入那个dll自己想咋改造就咋改造

=========================================================================

更改bug

如果在选区内部双击 会出现异常

解决办法


在FrmCapture中的  mouseDown事件中 把 m_ptOriginal = e.Location 放到if外面去 这个是记录鼠标点下时候的位置的

还有

C#高仿腾讯QQ截图程序_第7张图片

mouseup中 加上这一句 如果与点下的时候的坐标一样啥都不做

好吧 就算上面的这样还是有问题 是用画刷工具的时候 就有问题了 因为画刷的m_ptOriginal在mousemove中不停的给他赋值 

而且 逻辑还有点问题 鼠标抬起只要在绘制状态下无论如何都要 把m_isStartDraw赋值成false 和 Cursor.Clip 也要清空

那个return只是决定是否 SetLayer 将图层绘制上去而已

所以

if (!m_isStartDraw) return;
Cursor.Clip = Rectangle.Empty;
m_isStartDraw = false;
if (e.Location == m_ptOriginal && !tBtn_Brush.IsSelected) return;
this.SetLayer();        //将绘制的图形绘制到历史图层中
顺序这样放置

这样都还有问题 还是出在画刷上 所以在下面的SetLayer()方法前面加一句 if(this.IsDisposed) return;如果窗体都关闭了就 不执行操作 解释看下面、、

再解释一下为什么在选取类双击会出问题 因为在选区内做鼠标的一些操作的时候 mousedown 和 up 都会根据情况执行一些代码

然后双击事件众所周知 是两个单击 而单击是 mousedown 和 mouseup的组合 所以 doubleclick 的时候会分别执行两次 mousedown 和 mouseup 不过双击这个过程中顺序是这样的

mousedown - mouseup - mousedown - doubleclick - mouseup

双击事件 会在最后一个mouseup之前执行  而我却在双击中把图拷贝到剪切板 然后就关闭窗体了 所以 之后执行的mouseup的时候 调用函数的时候 就有问题 因为一些资源被我释放了

顺便再说一个

C#高仿腾讯QQ截图程序_第8张图片

在FrmCapture中的InitMember里面加上上面三条

之所以这样 是可能会出现在不同电脑上 运行出来 工具条的大小什么的会有些错位

尤其是panel1 因为panel1里面放的button是自定义的 而button的大小也是用户不能控制的

我对自定义的那个button 搞23 是固定的 宽至少23 (也就是没有文本显示只有图)  如果在有文本的情况下 比如最后 一个是 finish 那么button的宽自动根据文本的宽度啥的自动调整大小

所以在不同电脑上 字体啥的可能不一样 所以 自定义的button的大小也可能不一样 所以用上面三条代码来控制两个panel的大小 

在截图后 然后点击保存出现保存对话框的时候 还可以右键取消截图选取 

(- -!、、虽然保存对话框是模态对话框 hook无法可挡..)虽然正常情况不会出现这个情况 不过还是说一下

解决办法

再搞一个全局变量 m_bSave然后在 保存的那个按钮的事件函数里面

private void tBtn_Save_Click(object sender, EventArgs e) {
        m_bSave = true;    //进入的时候设置成true
        ...
        m_bSave = false;   //结束的时候设置成false
}
然后在 m_MHook_MHookEvent 事件中的 右键抬起 部分

#region 右键抬起

if (e.MButton == ButtonStatus.RightUp) {
	if (!imageProcessBox1.IsDrawed) //没有绘制那么退出(直接this.Close右键将传递到下面)
		this.BeginInvoke(new MethodInvoker(() => this.Close()));
	if (m_bSave) return;    //如果正在保存 不执行下面的代码
	......
}

#endregion



你可能感兴趣的:(C#高仿腾讯QQ截图程序)