עד עכשיו נגעתי בסדרה בכלי שליטה מתוכנתים בצד הלקוח, השלב הבא הוא לכידת התמונה בצד השרת באמצעות הספריה הקלאסית GDI, זה לא המנגנון היעיל ביותר אבל מספיק מתאים בשביל המשימה, לכידת המסך צריכה לעמוד בזמנים יחסית טובים על מנת לשמור על חווית משתמש, לא פחות חשוב שניהול הזיכרון צריך להיות מתוחזק כמו שצריך למרות שמדובר ב .Net על מנת למנוע זליגת זיכרון.
בנוסף לקובץ user32.dll שהשתמשנו בעבר נעבוד עם קובץ נוסף GDI.dll שמאפשר לנו לגשת למשאבים הגרפיים במחשב ע"י מספר פונקציות תחילה מקבלים את המצביע עבור הרכיב הפיזי בעזרת הפונקציה CreateDC לאחר מכן יוצרים Buffer משותף עם הפונקציה CreateCompatibleDC, השלב הבא להצהיר על Pointer ל Bitmap של Win32Api עם הפונקציה CreateCompatibleBitmap , מחברים בין ה Bitmap ל Buffer וקוראים לפונקציה BitBlt שאוספת את הפריים מהרכיב הפיזי ומעתיקה אותו ל Buffer ומשם ממשיכים הלאה.
captureControl.cs
//global stream
MemoryStream stream;
//isRunning flag
public bool isRunning = false;
int width = 0;
int height = 0;
//capture signature
[DllImport("GDI32.dll")]
public static extern bool BitBlt(
int hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, int hdcSrc,
int nXSrc, int nYSrc, int dwRop);
//create compatible bitmap for device context
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleBitmap
(int hdc, int nWidth, int nHeight);
//create compatible device context for the device
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleDC(int hdc);
//delete driver context
[DllImport("GDI32.dll")]
public static extern bool DeleteDC(int hdc);
//delete native bitmap type
[DllImport("GDI32.dll")]
public static extern bool DeleteObject(int hObject);
//create device context
[DllImport("gdi32.dll")]
static extern int CreateDC(string lpszDriver,
string lpszDevice,string lpszOutput,
IntPtr lpInitData);
//get device information
[DllImport("GDI32.dll")]
public static extern int GetDeviceCaps(int hdc, int nIndex);
//add object to the device context
[DllImport("GDI32.dll")]
public static extern int SelectObject(int hdc, int hgdiobj);
//cursor object win32 api structure
[StructLayout(LayoutKind.Sequential)]
private struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINTAPI ptScreenPos;
}
//point type win32 structure
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
////for more information visit
////http://msdn.microsoft.com/en-us/library/windows/desktop/dd145130(v=vs.85).aspx
public enum TernaryRasterOperations : uint
{
/// <summary>dest = source</summary>
SRCCOPY = 0x00CC0020,
/// <summary>dest = source OR dest</summary>
SRCPAINT = 0x00EE0086,
/// <summary>dest = source AND dest</summary>
SRCAND = 0x008800C6,
/// <summary>dest = source XOR dest</summary>
SRCINVERT = 0x00660046,
/// <summary>dest = source AND (NOT dest)</summary>
SRCERASE = 0x00440328,
/// <summary>dest = (NOT source)</summary>
NOTSRCCOPY = 0x00330008,
/// <summary>dest = (NOT src) AND (NOT dest)</summary>
NOTSRCERASE = 0x001100A6,
/// <summary>dest = (source AND pattern)</summary>
MERGECOPY = 0x00C000CA,
/// <summary>dest = (NOT source) OR dest</summary>
MERGEPAINT = 0x00BB0226,
/// <summary>dest = pattern</summary>
PATCOPY = 0x00F00021,
/// <summary>dest = DPSnoo</summary>
PATPAINT = 0x00FB0A09,
/// <summary>dest = pattern XOR dest</summary>
PATINVERT = 0x005A0049,
/// <summary>dest = (NOT dest)</summary>
DSTINVERT = 0x00550009,
/// <summary>dest = BLACK</summary>
BLACKNESS = 0x00000042,
/// <summary>dest = WHITE</summary>
WHITENESS = 0x00FF0062
}
//for more info visit
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx
private const Int32 CURSOR_SHOWING = 0x1;
private const Int32 CURSOR_SUPPRESSED = 0x2;
//getting cursor info signature
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
//getting cursor info signature
[DllImport("user32.dll", EntryPoint = "GetIconInfo")]
public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
//getting icon buffer signature
[DllImport("user32.dll", EntryPoint = "CopyIcon")]
public static extern IntPtr CopyIcon(IntPtr hIcon);
//return handle to the desktop window
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
//getting device context
[DllImport("user32.dll", EntryPoint = "GetDC")]
public static extern IntPtr GetDC(IntPtr ptr);
/// <summary>
/// initialize settings
/// </summary>
/// <param name="captureWidth">the width to capture</param>
/// <param name="captureHeight">the height to cature</param>
public captureControl(int captureWidth, int captureHeight)
{
width = captureWidth;
height = captureHeight;
}
/// <summary>
/// capture screen shot
/// </summary>
/// <returns>return stream</returns>
public MemoryStream makeScreenShot()
{
//process flag to true
isRunning = true;
//create stream for return
stream = new MemoryStream();
//X draw position - cursor
int x = 0;
//Y draw position - cursor
int y = 0;
//create device context for Display device
int hdcSrc = CreateDC("Display", null, null, IntPtr.Zero);
//make it compatible.
int hdcDest = CreateCompatibleDC(hdcSrc);
int hBitmap = CreateCompatibleBitmap(hdcSrc,
Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
//add object to device context
SelectObject(hdcDest, hBitmap);
//make bit transfer from device
BitBlt(hdcDest, 0, 0, Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height, hdcSrc, 0, 0,
(int)TernaryRasterOperations.SRCCOPY);
//get image from native bitmap pointer
Image imf = Image.FromHbitmap(new IntPtr(hBitmap));
//start graphics from image
Graphics g = Graphics.FromImage(imf);
//get cursor bitmap and position
Bitmap f = getCursorIcon(out x, out y);
//draw the cursor the image by position
if (f != null)
g.DrawImage(f, x, y);
//encoding parameters
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] =
new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);
//save image as stream
imf.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
//remove device context
DeleteDC(hdcSrc);
//remove device context
DeleteDC(hdcDest);
//remove native bitmap
DeleteObject(hBitmap);
//resize the image
stream = resizeImage(Image.FromStream(stream), new Size(width, height));
//process flag to false
isRunning = false;
return stream;
}
/// <summary>
/// getting the cursor image
/// </summary>
/// <param name="x">cursor x position</param>
/// <param name="y">cursor y poistion</param>
/// <returns>the cursor bitmap</returns>
private Bitmap getCursorIcon(out int x, out int y)
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
CURSORINFO pci;
pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
x = 0;
y = 0;
if (GetCursorInfo(out pci))
{
x = pci.ptScreenPos.x - bounds.X;
y = pci.ptScreenPos.y - bounds.Y;
IntPtr hicon = CopyIcon(pci.hCursor);
ICONINFO iconInfo;
GetIconInfo(hicon, out iconInfo);
Bitmap maskCursor = Bitmap.FromHbitmap(iconInfo.hbmMask);
// if the size of the icon is double
//so is in inverted mode
if (maskCursor.Height == maskCursor.Width * 2)
{
x = pci.ptScreenPos.x - bounds.X;
y = pci.ptScreenPos.y - bounds.Y - 8;
//create new bitmap for cursor
Bitmap resultBitmap = new Bitmap(maskCursor.Width, maskCursor.Height);
//get the desktop
Graphics desktopGraphics = Graphics.FromHwnd(GetDesktopWindow());
//get it's device context
IntPtr desktopHdc = desktopGraphics.GetHdc();
//make it compatible
int maskHdc = CreateCompatibleDC(desktopHdc.ToInt32());
//set object between context and the cursor
int oldPtr = SelectObject(maskHdc, (int)maskCursor.GetHbitmap());
//start graphics from image
Graphics resultGraphics = Graphics.FromImage(resultBitmap);
//get device context
IntPtr resultHdc = resultGraphics.GetHdc();
//combines image bit depends the Ternary Raster Operations
//first (0 - 32) pixels draw normaly, (32 - 64) as inverted
BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
0, 32, (int)TernaryRasterOperations.SRCCOPY);
BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
0, 0, (int)TernaryRasterOperations.SRCINVERT);
//release device context
resultGraphics.ReleaseHdc(resultHdc);
//dispose grahics
resultGraphics.Dispose();
//make white pixels transparent
resultBitmap.MakeTransparent(Color.White);
return resultBitmap;
}
else
{
//get bitmap from pointer
Bitmap colorCursor = Bitmap.FromHbitmap(iconInfo.hbmColor);
//make black pixels transparent
colorCursor.MakeTransparent(Color.Black);
return colorCursor;
}
}
return null;
}
/// <summary>
/// resize the capture image
/// </summary>
/// <param name="imgToResize">instance of the image</param>
/// <param name="size">the new size</param>
/// <returns>new image stream</returns>
private MemoryStream resizeImage(Image imgToResize, Size size)
{
MemoryStream resizeStream = new MemoryStream();
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
resizeStream = new MemoryStream();
Bitmap b = new Bitmap(destWidth, destHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
b.Save(resizeStream, ImageFormat.Jpeg);
return resizeStream;
}
/// <summary>
/// get the jpg codec
/// </summary>
/// <param name="format">by Imageformat</param>
/// <returns></returns>
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
MemoryStream stream;
//isRunning flag
public bool isRunning = false;
int width = 0;
int height = 0;
//capture signature
[DllImport("GDI32.dll")]
public static extern bool BitBlt(
int hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, int hdcSrc,
int nXSrc, int nYSrc, int dwRop);
//create compatible bitmap for device context
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleBitmap
(int hdc, int nWidth, int nHeight);
//create compatible device context for the device
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleDC(int hdc);
//delete driver context
[DllImport("GDI32.dll")]
public static extern bool DeleteDC(int hdc);
//delete native bitmap type
[DllImport("GDI32.dll")]
public static extern bool DeleteObject(int hObject);
//create device context
[DllImport("gdi32.dll")]
static extern int CreateDC(string lpszDriver,
string lpszDevice,string lpszOutput,
IntPtr lpInitData);
//get device information
[DllImport("GDI32.dll")]
public static extern int GetDeviceCaps(int hdc, int nIndex);
//add object to the device context
[DllImport("GDI32.dll")]
public static extern int SelectObject(int hdc, int hgdiobj);
//cursor object win32 api structure
[StructLayout(LayoutKind.Sequential)]
private struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINTAPI ptScreenPos;
}
//point type win32 structure
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
////for more information visit
////http://msdn.microsoft.com/en-us/library/windows/desktop/dd145130(v=vs.85).aspx
public enum TernaryRasterOperations : uint
{
/// <summary>dest = source</summary>
SRCCOPY = 0x00CC0020,
/// <summary>dest = source OR dest</summary>
SRCPAINT = 0x00EE0086,
/// <summary>dest = source AND dest</summary>
SRCAND = 0x008800C6,
/// <summary>dest = source XOR dest</summary>
SRCINVERT = 0x00660046,
/// <summary>dest = source AND (NOT dest)</summary>
SRCERASE = 0x00440328,
/// <summary>dest = (NOT source)</summary>
NOTSRCCOPY = 0x00330008,
/// <summary>dest = (NOT src) AND (NOT dest)</summary>
NOTSRCERASE = 0x001100A6,
/// <summary>dest = (source AND pattern)</summary>
MERGECOPY = 0x00C000CA,
/// <summary>dest = (NOT source) OR dest</summary>
MERGEPAINT = 0x00BB0226,
/// <summary>dest = pattern</summary>
PATCOPY = 0x00F00021,
/// <summary>dest = DPSnoo</summary>
PATPAINT = 0x00FB0A09,
/// <summary>dest = pattern XOR dest</summary>
PATINVERT = 0x005A0049,
/// <summary>dest = (NOT dest)</summary>
DSTINVERT = 0x00550009,
/// <summary>dest = BLACK</summary>
BLACKNESS = 0x00000042,
/// <summary>dest = WHITE</summary>
WHITENESS = 0x00FF0062
}
//for more info visit
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx
private const Int32 CURSOR_SHOWING = 0x1;
private const Int32 CURSOR_SUPPRESSED = 0x2;
//getting cursor info signature
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
//getting cursor info signature
[DllImport("user32.dll", EntryPoint = "GetIconInfo")]
public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
//getting icon buffer signature
[DllImport("user32.dll", EntryPoint = "CopyIcon")]
public static extern IntPtr CopyIcon(IntPtr hIcon);
//return handle to the desktop window
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
//getting device context
[DllImport("user32.dll", EntryPoint = "GetDC")]
public static extern IntPtr GetDC(IntPtr ptr);
/// <summary>
/// initialize settings
/// </summary>
/// <param name="captureWidth">the width to capture</param>
/// <param name="captureHeight">the height to cature</param>
public captureControl(int captureWidth, int captureHeight)
{
width = captureWidth;
height = captureHeight;
}
/// <summary>
/// capture screen shot
/// </summary>
/// <returns>return stream</returns>
public MemoryStream makeScreenShot()
{
//process flag to true
isRunning = true;
//create stream for return
stream = new MemoryStream();
//X draw position - cursor
int x = 0;
//Y draw position - cursor
int y = 0;
//create device context for Display device
int hdcSrc = CreateDC("Display", null, null, IntPtr.Zero);
//make it compatible.
int hdcDest = CreateCompatibleDC(hdcSrc);
int hBitmap = CreateCompatibleBitmap(hdcSrc,
Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
//add object to device context
SelectObject(hdcDest, hBitmap);
//make bit transfer from device
BitBlt(hdcDest, 0, 0, Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height, hdcSrc, 0, 0,
(int)TernaryRasterOperations.SRCCOPY);
//get image from native bitmap pointer
Image imf = Image.FromHbitmap(new IntPtr(hBitmap));
//start graphics from image
Graphics g = Graphics.FromImage(imf);
//get cursor bitmap and position
Bitmap f = getCursorIcon(out x, out y);
//draw the cursor the image by position
if (f != null)
g.DrawImage(f, x, y);
//encoding parameters
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] =
new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);
//save image as stream
imf.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
//remove device context
DeleteDC(hdcSrc);
//remove device context
DeleteDC(hdcDest);
//remove native bitmap
DeleteObject(hBitmap);
//resize the image
stream = resizeImage(Image.FromStream(stream), new Size(width, height));
//process flag to false
isRunning = false;
return stream;
}
/// <summary>
/// getting the cursor image
/// </summary>
/// <param name="x">cursor x position</param>
/// <param name="y">cursor y poistion</param>
/// <returns>the cursor bitmap</returns>
private Bitmap getCursorIcon(out int x, out int y)
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
CURSORINFO pci;
pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
x = 0;
y = 0;
if (GetCursorInfo(out pci))
{
x = pci.ptScreenPos.x - bounds.X;
y = pci.ptScreenPos.y - bounds.Y;
IntPtr hicon = CopyIcon(pci.hCursor);
ICONINFO iconInfo;
GetIconInfo(hicon, out iconInfo);
Bitmap maskCursor = Bitmap.FromHbitmap(iconInfo.hbmMask);
// if the size of the icon is double
//so is in inverted mode
if (maskCursor.Height == maskCursor.Width * 2)
{
x = pci.ptScreenPos.x - bounds.X;
y = pci.ptScreenPos.y - bounds.Y - 8;
//create new bitmap for cursor
Bitmap resultBitmap = new Bitmap(maskCursor.Width, maskCursor.Height);
//get the desktop
Graphics desktopGraphics = Graphics.FromHwnd(GetDesktopWindow());
//get it's device context
IntPtr desktopHdc = desktopGraphics.GetHdc();
//make it compatible
int maskHdc = CreateCompatibleDC(desktopHdc.ToInt32());
//set object between context and the cursor
int oldPtr = SelectObject(maskHdc, (int)maskCursor.GetHbitmap());
//start graphics from image
Graphics resultGraphics = Graphics.FromImage(resultBitmap);
//get device context
IntPtr resultHdc = resultGraphics.GetHdc();
//combines image bit depends the Ternary Raster Operations
//first (0 - 32) pixels draw normaly, (32 - 64) as inverted
BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
0, 32, (int)TernaryRasterOperations.SRCCOPY);
BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
0, 0, (int)TernaryRasterOperations.SRCINVERT);
//release device context
resultGraphics.ReleaseHdc(resultHdc);
//dispose grahics
resultGraphics.Dispose();
//make white pixels transparent
resultBitmap.MakeTransparent(Color.White);
return resultBitmap;
}
else
{
//get bitmap from pointer
Bitmap colorCursor = Bitmap.FromHbitmap(iconInfo.hbmColor);
//make black pixels transparent
colorCursor.MakeTransparent(Color.Black);
return colorCursor;
}
}
return null;
}
/// <summary>
/// resize the capture image
/// </summary>
/// <param name="imgToResize">instance of the image</param>
/// <param name="size">the new size</param>
/// <returns>new image stream</returns>
private MemoryStream resizeImage(Image imgToResize, Size size)
{
MemoryStream resizeStream = new MemoryStream();
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
resizeStream = new MemoryStream();
Bitmap b = new Bitmap(destWidth, destHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
b.Save(resizeStream, ImageFormat.Jpeg);
return resizeStream;
}
/// <summary>
/// get the jpg codec
/// </summary>
/// <param name="format">by Imageformat</param>
/// <returns></returns>
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
ברגע הקריאה לפונקציה makeScreenShot מתחילה שרשרת של תהליכים, תחילה לוכדים את התמונה כפי שהסברתי בהתחלה, אבל לכידת המסך לא אוספת את סמן העכבר ולכן יש לקרוא למספר פונקציות נוספות על מנת למצוא אותו ולצייר אותו ידנית אבל גם זה לא פשוט כמו שזה נשמע, למצביעים בעכבר (Cursor) יש 2 מצבים הגודל שלהם בדר"כ הוא 32 פיקסלים אבל לפעמים הוא משתנה ל 64 פיקסלים בגלל הרקע שמתחת, נעזר במספר פונקציות ומבנים מ user32.dll על מנת לטפל במצבים השונים, בסיום התהליך לוקחים את הפריים, מורידים לו את האיכות ,מקטינים ומחזרים אותו להמשך טיפול.
בדומה לשאר המאמרים גם כאן נבנה תוכנית שנועדה לבדיקת התהליך, התוכנית מפעילה את הפונקציה makeScreenShot בעזרת שעון שדוגם (אם אין תהליך דגימה שכבר רץ) כל 10 ms, שומרים את הפריים כ Jpg על מנת להקטין נפחים וטוענים את ה Stream ל PictureBox שמדמה את הלקוח המרוחק.
מסך בתוך מסך בתוך מסך... |
captureForm.cs
captureControl c;
int frameCounter = 0;
public captureForm()
{
InitializeComponent();
}
private void captureForm_Load(object sender, EventArgs e)
{
//create new instance for capture control
c = new captureControl(1200, 600);
//fit image to picturebox
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
/// <summary>
/// running every 10ms
/// </summary>
private void timer1_Tick(object sender, EventArgs e)
{
//getting screenshot as stream
if (c.isRunning)
return;
try
{
//watch for checking performance
Stopwatch watch = new Stopwatch();
watch.Start();
MemoryStream st = c.makeScreenShot();
watch.Stop();
//loaded to the pictureBox
pictureBox1.Image = Image.FromStream(st);
//change title and calculate the frame size
frameCounter = frameCounter + 1;
this.Text = "captureScreen Frame Counter "
+ frameCounter.ToString() + " Frame Size: "
+ (st.Length / 1024).ToString() + " KB width:"
+ pictureBox1.Image.Size.Width.ToString() + " px Height: "
+ pictureBox1.Image.Size.Height.ToString() +
" px Capture Time: " + watch.ElapsedMilliseconds + " ms";
//dispose the stream, avoid memory leak
st.Dispose();
//call the garbage collector
GC.Collect();
}
catch (Exception ex)
{
c.isRunning = false;
}
}
}
ביצועים
לכידת התמונה יחד עם ציור סמן העכבר לוקחת בסביבות 150 ms ברזולוציה 1920X1080 עם מעבד 7 Icore (יחסית ישן) מה שנותן בין 6 ל 7 פריימים בשניה, זה לא הכי אידיאלי אבל אם מורידים את הרזולוציה משפרים את המהירות בצורה משמעותית, מבדיקה נוספת שביצעתי על מחשב נייד עם Icore 7 (גם הוא יחסית ישן) לכידת תמונה ברזולוציה 1366X768 לוקחת בסביבות 110 ms.
לכידת התמונה יחד עם ציור סמן העכבר לוקחת בסביבות 150 ms ברזולוציה 1920X1080 עם מעבד 7 Icore (יחסית ישן) מה שנותן בין 6 ל 7 פריימים בשניה, זה לא הכי אידיאלי אבל אם מורידים את הרזולוציה משפרים את המהירות בצורה משמעותית, מבדיקה נוספת שביצעתי על מחשב נייד עם Icore 7 (גם הוא יחסית ישן) לכידת תמונה ברזולוציה 1366X768 לוקחת בסביבות 110 ms.
סיכום
לכידת מסך היא הרכיב האחרון בפאזל לפני שמתחילים לחבר את הכל, אבל חשוב לי להוסיף שקיימות עוד דרכים שאיתן ניתן לבצע את המשימה כמו:
DirectX - לכידת מסך גם מחוץ ל Desktop לדוגמה במשחקים.
Windows Media Encoder - מתאים בעיקר ללכידת מסך כוידאו.
Mirror Display Driver - הדרך הטובה ביותר אבל מאוד מסובכת, נמצאת בשימוש ב UltraVnc, לכל מי שרוצה לדעת עוד יש דוגמה טובה ב Wdk גרסת 7.600 אולי עוד אחזור לזה בהמשך.
עוד תמונה ועוד תמונה...
אין תגובות:
הוסף רשומת תגובה