这个文章还是有一些可以借鉴的地方
-------------------------------------------------------------------------
最近在程序中做一动态截屏功能的小程序;
在完成过程中,遇到了许多问题,
现将其中重要的过程记录如下:
1,要实现动态截屏的原理
刚开始并不知,以为可以得到桌面的句柄直接调用左右鼠标点击得到.
哪有那么如想像中的美事
看了许多别人的程序.,特别是QQ的动态截屏功能,
他们在截屏的时候桌面都是静态的.
原来此时的背景是一个最大化的FORM,把其标题栏,按钮等都取消了.
像做桌面保护程序那样的风格.
在这个FORM中得到它的两个坐标是很容易的事;
也就是说:先截全屏到一个FORM中显示
在这个FORM中截你所想要的一部分并在第一个FORM中显示所截部分;
这样就出现了要在两个之间传值;
最初为了避免传值(呵呵,当时不会传值),我先把所截部分保存一张图片,
在第一个FORM中检测是晋中有图片存在,有则显示出来
功能是实现了,但有了新问题?只能截一次!
原因是,第二要截屏则要删除保存的图片,为第二次截屏准备,但异常显示
图片资源被调用,无法删除.也是为什么第二次截图不能保存的原因.
mypicturebox.Image=null;这样的操作也不行的.(有谁知道的怎么样消除调用资源的告诉我一下)
看来这个笨方法不工作了.
在网上找到两种FORM间传值的方法.
一]新建类过渡传值.
二]传递第一个FORM的地址.
2具体实现
清楚了实现原理,就不难实现其功能了~*~
我先用第二个方法实现!
(1)先在第一个FORM中用一按钮启动截屏程序
this.Hide();//隐藏主对话框.
Thread.Sleep(150);//停止一下
AllBitmap = Getallscreen();//调用动态截屏
CaptureScreen CaptureS = new CaptureScreen(AllBitmap,this);// 传递全屏BITMAP和地址
CaptureS.ShowDialog();
this.Show();
其中调用了截全屏的函数Getallscreen()
private Bitmap Getallscreen()
{
//建立屏幕Graphics
Graphics grpScreen = Graphics.FromHwnd(IntPtr.Zero);
//根据屏幕大小建立位图
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, grpScreen);
//建立位图相关Graphics
Graphics grpBitmap = Graphics.FromImage(bitmap);
//建立屏幕上下文
IntPtr hdcScreen = grpScreen.GetHdc();
//建立位图上下文
IntPtr hdcBitmap = grpBitmap.GetHdc();
//将屏幕捕获保存在图位中
BitBlt(hdcBitmap, 0, 0, bitmap.Width, bitmap.Height, hdcScreen, 0, 0, 0x00CC0020);
//关闭位图句柄
grpBitmap.ReleaseHdc(hdcBitmap);
//关闭屏幕句柄
grpScreen.ReleaseHdc(hdcScreen);
//释放位图对像
grpBitmap.Dispose();
//释放屏幕对像
grpScreen.Dispose();
//返回捕获位图
return bitmap;
}
在截全屏函数中用到了一个API函数.
则进行如下操作
using System.Runtime.InteropServices;
[DllImportAttribute("gdi32.dll")]
public static extern bool BitBlt(
IntPtr hdcDest, //目标设备的句柄
int nXDest, // 目标对象的左上角的X坐标
int nYDest, // 目标对象的左上角的X坐标
int nWidth, // 目标对象的矩形的宽度
int nHeight, // 目标对象的矩形的长度
IntPtr hdcSrc, // 源设备的句柄
int nXSrc, // 源对象的左上角的X坐标
int nYSrc, // 源对象的左上角的X坐标
System.Int32 dwRop // 光栅的操作值
);
(2)到了最重要的地方,要想把第二个FORM中的数据传递过来就得在第一个FORM中的相应的显示处理程序
我用下列函数
public void SetMyImage(Bitmap mybitmap)//注意是public类型
{
this.mypictureBox.Image = mybitmap;//显示
myimage = mybitmap; //用于重画
PreBtn.Enabled = true; //用于保存图片
}
第一个FORM中用到一些变量
public Image myimage=null;
public Bitmap AllBitmap;
(3)在第一个FORM中调用了第二个FORM,其中构造函数传递了全屏的BITMAP,
所以第二个FORM中加上一个构造函数:
public CaptureScreen(Bitmap bit,Form parentForm)
{
InitializeComponent();
this.allbitmap = bit;
this._parentForm = parentForm;
}
传递了图片信息在FORM_LOAD中加载显示
private void CaptureScreen_Load(object sender, EventArgs e)
{
this.TopMost = true;//让其最前显示
this.allpicture.Image = allbitmap;
gg = this.allpicture.CreateGraphics();//创建一个Graphics为后面建立提示信息准备
}
(4)要截部分屏幕,当然要确定一下范围.用左右鼠标键来确定.
用到下面三个函数
private void allpicture_MouseDown(object sender, MouseEventArgs e)
{
if (isdoubleclick == true)
{
point1.X = e.X;
point1.Y = e.Y;
isdraw = true;
gg.DrawString("按Esc键退出截图程序程序", new Font("Tahoma", 13, FontStyle.Underline),
new SolidBrush(Color.Blue), e.X,e.Y);
}
}
private void allpicture_MouseMove(object sender, MouseEventArgs e)
{
this.allpicture.Refresh();
if (isdraw == true)
{
int hh,ww; //计算截图位置和开始点位置
Point startpoint=new Point(0,0);
if (e.X < point1.X && e.Y < point1.Y)
{
startpoint.X = e.X;
startpoint.Y = e.Y;
hh = (int)(point1.X - e.X);
ww = (int)(point1.Y - e.Y);
}
if (e.X > point1.X && e.Y < point1.Y)
{
startpoint.X = point1.X;
startpoint.Y = e.Y;
hh = (int)(e.X - point1.X);
ww = (int)(point1.Y - e.Y);
}
if (e.X < point1.X && e.Y > point1.Y)
{
startpoint.X = e.X;
startpoint.Y = point1.Y;
hh = (int)(point1.X - e.X);
ww = (int)(e.Y - point1.Y);
}
else
{
startpoint = point1;
hh = (int)(e.X - point1.X);
ww = (int)(e.Y - point1.Y);
}
gg.DrawRectangle(new Pen(Color.FromArgb(9, 247, 32)), startpoint.X,
startpoint.Y, hh, ww);//截图范围示意图
int hhh, www;//显示提示信息位置
if ((int)(e.X - point1.X) > 0)
hhh = (int)(e.X - point1.X);
else
hhh = (int)(point1.X - e.X);
if ((int)(e.Y - point1.Y) > 0)
www = (int)(e.Y - point1.Y);
else
www = (int)(point1.Y - e.Y);
string mystr = "截取范围:\n 宽:" + hhh + "\n 高:" + www;
gg.DrawString(mystr, new Font("Tahoma", 10, FontStyle.Underline),
new SolidBrush(Color.Red), e.X,e.Y);//h1, w1);
gg.DrawString("按Esc键退出截图程序程序", new Font("Tahoma", 13, FontStyle.Underline),
new SolidBrush(Color.Blue), point1.X, point1.Y-22);
}
}
private void allpicture_MouseUp(object sender, MouseEventArgs e)
{
if (isdoubleclick == true)
{
point2.X = e.X;
point2.Y = e.Y;
isdoubleclick = false;
}
}
第二个FORM中用到的变量:
private Point point1 = new Point(); //开始位置
private Point point2 = new Point(); //最后位置
private bool isdraw = false; //是否开始画直线
private bool isdoubleclick = true; //是否可以双击
public Bitmap newbitmap; //截的部分图像
private Bitmap allbitmap; //全屏图像
private Form _parentForm = null;
private Graphics gg;
public static int h1;//显示提示位置
public static int w1;
截屏用到的API函数:
#region 导入函数
[DllImport("gdi32.dll")]
private static extern IntPtr CreateDC(string lpszDriver, string lpszDrivse, string lpszOutput, Int32 lpInitData);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, Int32 nIndex);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, UInt32 dwRop);
[DllImport("gdi32.dll")]
private static extern int DeleteDC(IntPtr hdc);
#endregion
(5)现在可以调用截取部分屏幕的函数
private void allpicture_DoubleClick(object sender, EventArgs e)
{
this.allpicture.Refresh();//先擦出提示信息,食品店提示信息什么地方都可以显示;
newbitmap = GetPartScreen(point1, point2);
((MyTool)_parentForm).SetMyImage(newbitmap);//通过传地址调用第一个FORM的图片显示函数
this.Close();//该窗口关闭 // Application.Exit();应用程序退出
}
下面是GetPartScreen函数的具体实现 private static Bitmap GetPartScreen(Point p, Point pp)
{
IntPtr hscrdc, hmemdc;
IntPtr hbitmap, holdbitmap;
int nx, ny, nxx, nyy;
nx = nxx = ny = nyy = 0;
int nwidth, nheight;
int xscrn, yscrn;
hscrdc = CreateDC("DISPLAY", null, null, 0);//(IntPtr)null);// 0);//创建DC句柄
hmemdc = CreateCompatibleDC(hscrdc);//创建一个内存DC
xscrn = GetDeviceCaps(hscrdc, 8);//*HORZRES*/);//获取屏幕宽度//wingdi.h
yscrn = GetDeviceCaps(hscrdc, 10);//*VERTRES*/);//获取屏幕高度//wingdi.h
nx = p.X;
ny = p.Y;
nxx = pp.X;
nyy = pp.Y;
if (nx < 0) //检查数值合法性
nx = 0;
if (ny < 0)
ny = 0;
if (nxx > xscrn)
nxx = xscrn;
if (nyy > yscrn)
nyy = yscrn;
if (nxx - nx > 0)//截取范围的宽度
{
nwidth = nxx - nx;
}
else
{
nwidth = nx - nxx;
}
if (nyy - ny > 0)//截取范围的高度
{
nheight = nyy - ny;
}
else
{
nheight = ny - nyy;
}
if (nxx < nx && nyy < ny)
{
h1 = nx;
w1 = ny;
int k;
k = nxx;
nxx = nx;
nx = k;
k = nyy;
nyy = ny;
ny = k;
}
if (nxx < nx && nyy > ny)
{
h1 = nx;
w1 = nyy;
nx = nxx;
}
if (nx < nxx && ny > nyy)
{
h1=nx;
w1 = nyy;
ny = nyy;
}
hbitmap = CreateCompatibleBitmap(hscrdc, nwidth, nheight);//从内存DC复制到hbitmap句柄
holdbitmap = SelectObject(hmemdc, hbitmap);
BitBlt(hmemdc, 0, 0, nwidth, nheight, hscrdc, nx, ny, (UInt32)0xcc0020);
hbitmap = SelectObject(hmemdc, holdbitmap);
DeleteDC(hscrdc);//删除用过的对象
DeleteDC(hmemdc);//删除用过的对象
return Bitmap.FromHbitmap(hbitmap);//用Bitmap.FromHbitmap从hbitmap返回Bitmap
}
(6)我们传递了地址.怎么实现的传地址呢.委托!(具体委托知识请查教程委托部分!)
在第一个FORM中申请委托:
public delegate void mydelegate(Bitmap mybitmap);//类外
全部功能实现,可你会发现传递的图片没有显示在第一个FORM中的图片控件中
为什么呢,我可是花了七八个小时才知,因为那时我晕了
原来我们在截图时隐藏了FORM1,又接着显示,再关闭了FORM2,使显示的图片没有更新,
所以现在你知道该怎么做了吧.
重载OnPaint函数:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (myimage != null)
this.mypictureBox.Image = myimage;
else
{
this.mypictureBox.Image = null;
this.mypictureBox.Refresh();
}
}
(7)大功告成,不知你是否!
(8)现在简单用过渡类实现一下传值:
我只给出不相同的部分,有不明白的可以联系我共同讨论;
第一个FORM截屏时
PassBitmap newpassbitmap = new PassBitmap();//注意这.
newpassbitmap.PassBitmapEvent += new SendBitmap(this.SetMyImage);
this.Hide();
Thread.Sleep(150);
AllBitmap = Getallscreen();//调用动态截屏
CaptureScreen CaptureS = new CaptureScreen(AllBitmap, newpassbitmap);
CaptureS.ShowDialog();
this.Show();
第二个FORM的构造函数
public CaptureScreen(Bitmap bit,PassBitmap bitmapss)
{
InitializeComponent();
this.allbitmap = bit;
this.bitmapss=bitmapss;
}
双击截屏时:
newbitmap = GetPartScreen(point1, point2);
bitmapss.PassGetBitmap(newbitmap);
其中过渡类:
using System;
using System.Drawing;
namespace BoberTool
{
public delegate void SendBitmap(Bitmap mybitmap);
public class PassBitmap
{
public Bitmap mybitmaps;
public event SendBitmap PassBitmapEvent;
public void PassGetBitmap(Bitmap bitmaps)
{
if (bitmaps != null)
PassBitmapEvent(bitmaps);
}
}
}
基本上实现了功能!
由于我是初学者.其中必然存在许多小问题,欢迎大家和我共同讨论!
QQ47145481
E-MAIL:bob_er@sina.com
2006.10.28