четверг, 7 февраля 2013 г.

Исходники класса по захвату экран. Часть 2

Вот обещал выложить исходники по захвату экрана, к ранее опубликованной статье, как говорится суть.

Предыдущая статья.

Для начала работы всего этого безобразия нужно импортировать Вин АПИ функции. Вот они.

#region
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

#endregion

namespace Utils.OCR
{
/// <summary>
/// Оболочка для Win32 функций
/// </summary>
public class Win32
{
public const int SRCCOPY = 0x00CC0020;
public const int R2_NOT = 6;
public const int R2_NOP = 11;
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;

[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("User32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);

[DllImport("Gdi32.dll")]
public static extern int BitBlt(
IntPtr hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hdcSrc,
int nXSrc,
int nYSrc,
int dwRop
);

[DllImport("Gdi32.dll")]
public static extern int StretchBlt(
IntPtr hdcDest,
int nXOriginDest,
int nYOriginDest,
int nWidthDest,
int nHeightDest,
IntPtr hdcSrc,
int nXOriginSrc,
int nYOriginSrc,
int nWidthSrc,
int nHeightSrc,
int dwRop
);

[DllImport("Gdi32.dll")]
public static extern int SetROP2(IntPtr hDC, int fnDrawMode);

[DllImport("Gdi32.dll")]
private static extern int MoveToEx(IntPtr hDC, int x, int y, IntPtr lpPoint);

public static int MoveTo(IntPtr hDC, int x, int y)
{
return MoveToEx(hDC, x, y, IntPtr.Zero);
}

[DllImport("Gdi32.dll")]
public static extern int LineTo(IntPtr hDC, int nXEnd, int nYEnd);

[DllImport("Gdi32.dll")]
private static extern int GetPixel(
IntPtr hdc,
int nXPos,
int nYPos
);

public static Color GetPixelColor(IntPtr hDC, int x, int y)
{
long colorRef = GetPixel(hDC, x, y);
return Color.FromArgb((byte) colorRef, (byte) (colorRef >> 8), (byte) (colorRef >> 16));
}

[DllImport("Gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hGdiObj);

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);

[DllImport("Gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);

// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);

// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern void mouse_event(UInt32 dwFlags, UInt32 dx, UInt32 dy, UInt32 dwData, IntPtr dwExtraInfo);

public static void SendClick(System.Drawing.Point location)
{
Cursor.Position = location;
Thread.Sleep(100);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, new IntPtr());
Thread.Sleep(100);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, new IntPtr());
}

public enum Bool
{
False = 0,
True
};


[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;

public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}


[StructLayout(LayoutKind.Sequential)]
public struct Size
{
public Int32 cx;
public Int32 cy;

public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
}


[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}


public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;

public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;


[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);



[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);


}
}



Их несколько больше чем нужно но не чего страшного, можете потом оставить только нужные вам.


Собственно исходник.


#region
using System;
using System.Drawing;
using System.Windows.Forms;
#endregion

namespace Utils.OCR
{
/// <summary>
/// Осуществляет всю работу по захвату изображения
/// </summary>
internal class ScreenCapturer
{
private readonly IntPtr hWndDesktop;
private Control control;
private int desktopBottom;
private int desktopRight;
private bool isFormDeactivated;
private bool needGrab;
private Rectangle oldRectangle;
private int zoomRate = 1;

public ScreenCapturer(Control control)
{
this.control = control;
hWndDesktop = IntPtr.Zero;
desktopRight = Screen.PrimaryScreen.Bounds.Right - 1;
desktopBottom = Screen.PrimaryScreen.Bounds.Bottom - 1;
}

public int ZoomRate
{
set { zoomRate = value; }
}

public Rectangle CaptureRectangle
{
get { return oldRectangle; }
}

public Point DesktopDimension
{
set
{
desktopRight = value.X - 1;
desktopBottom = value.Y - 1;
}
}

public void BeginCapture(Point cursorPosition)
{
needGrab = true;
Rectangle rectangle = GetZoomedRectangle(cursorPosition.X, cursorPosition.Y);
DrawRectangle(rectangle);
oldRectangle = rectangle;
}

public Rectangle StopCapture()
{
if (needGrab || isFormDeactivated)
{
if (isFormDeactivated)
isFormDeactivated = false;

DrawRectangle(oldRectangle);
}
needGrab = false;
return oldRectangle;
}

public void CopyImage(Point cursorPosition)
{
if (!needGrab)
return;

Rectangle rectangle = GetZoomedRectangle(cursorPosition.X, cursorPosition.Y);
DrawRectangle(oldRectangle);
CopyDesktopRectangle(rectangle.Left, rectangle.Top, null);
DrawRectangle(rectangle);
oldRectangle = rectangle;
}

public void PaintControl(Graphics graphics)
{
CopyDesktopRectangle(oldRectangle.Left, oldRectangle.Top, graphics);
}

//Рисует на Destkop'e прямоугольник, для выделения копируемой оьласти
private void DrawRectangle(Rectangle rectangle)
{
IntPtr hDCDesktop = Win32.GetDC(hWndDesktop);
Win32.SetROP2(hDCDesktop, Win32.R2_NOT);

Win32.MoveTo(hDCDesktop, rectangle.Left, rectangle.Top);
Win32.LineTo(hDCDesktop, rectangle.Right, rectangle.Top);
Win32.LineTo(hDCDesktop, rectangle.Right, rectangle.Bottom);
Win32.LineTo(hDCDesktop, rectangle.Left, rectangle.Bottom);
Win32.LineTo(hDCDesktop, rectangle.Left, rectangle.Top);

Win32.SetROP2(hDCDesktop, Win32.R2_NOP);
Win32.ReleaseDC(hWndDesktop, hDCDesktop);
}

/// <summary>
/// Возвращает Rectangle с координатами рисования прямоугольника копируемой области
/// </summary>
private Rectangle GetZoomedRectangle(int x, int y)
{
Point point = control.PointToScreen(new Point(x, y));
x = point.X;
y = point.Y;

int halfWidth = control.Width/zoomRate/2;
int halfHeight = control.Height/zoomRate/2;

int left = x - halfWidth;
int top = y - halfHeight;

if (left < 0)
left = 0;

if (top < 0)
top = 0;

int right = left + control.Width/zoomRate - 1;
int bottom = top + control.Height/zoomRate - 1;

if (right > desktopRight)
{
right = desktopRight;
left = right - control.Width/zoomRate + 1;
}

if (bottom > desktopBottom)
{
bottom = desktopBottom;
top = bottom - control.Height/zoomRate + 1;
}

return Rectangle.FromLTRB(left, top, right, bottom);
}

/// <summary>
/// Копирует прямоугольную область с Desktop'a в Panel
/// </summary>
/// <param name="left"> X координата левого верхнего угла Desktop'a </param>
/// <param name="top"> Y координата левого верхнего угла Desktop'a </param>
/// <param name="createDesktopDC"> Нужно ли получать HDC Desktop'a? </param>
private void CopyDesktopRectangle(int left, int top, Graphics graphics)
{
if (graphics == null)
graphics = control.CreateGraphics();

IntPtr hDCPanel = graphics.GetHdc();
IntPtr hDCDesktop = Win32.GetDC(hWndDesktop);

Win32.StretchBlt(hDCPanel, 0, 0, control.Width, control.Height,
hDCDesktop, left, top, control.Width/zoomRate, control.Height/zoomRate,
Win32.SRCCOPY);

graphics.ReleaseHdc(hDCPanel);
Win32.ReleaseDC(hWndDesktop, hDCDesktop);
}
}
}


Собственно этим кодом я не пользуюсь. Так что приведу свой способ.
Также импортируем ВИН 32 АПИ для роботы с содержимым экрана. Собственно буквально пару функций.
далее

using System;
using System.Collections.Generic;
using System.Drawing;

namespace Utils.OCR
{
// собственно код извлекающий картинку.
[Serializable]
public class SCP
{

public static SCP Recognizer= new SCP();
public static IntPtr window;
// область захвата
public Rectangle WorcRect = new Rectangle() ;
// указатель на окно которое мы захватываем
public IntPtr grappointer;

public SCP()
{
// создаем все что нужно для захвата графики согласо установленых настроек
RecrateGraphics();
}
// открытие графического контекста, для операцыии с екраном.
public void OpenContext()
{
// устанавливаем контекст для захвата всего что есть на екране включая рабочий стол и окна. Есле нужно захватить какоето
// конкретное окно подставляем его индентификатор вместо IntPtr.Zero.
Win32.SetForegroundWindow(IntPtr.Zero);
grappointer = Win32.GetDC(IntPtr.Zero);
}

// метод получение значения цвета в заданой точке.
private Color getPixelColor(int X, int Y)
{
return Win32.GetPixelColor(grappointer, X, Y);
}

//Закрыть контекст после окончания операцыи захвата
private void CloseContext()
{
Win32.ReleaseDC(IntPtr.Zero, grappointer);
}

[NonSerialized]
public Bitmap CapturetBitmap;



// собственно функцыя захвата может находится в цыкле и переодически обновлять изображение
public void UpdatesScanBitmap()
{
if(WorcRect == Rectangle.Empty || lockUpdate ) return;
OpenContext();
// перебор попиксельно области захвата
for (int i =WorcRect .Left; i < WorcRect .Right; i++)
{
for (int j = WorcRect .Top; j < WorcRect .Bottom; j++)
{
Color curent = getPixelColor(i, j);
CapturetBitmap.SetPixel(i, j, curent);
}
}
CloseContext();
}

// создание изображений
private void RecrateGraphics()
{
CapturetBitmap = new Bitmap(WorcRect .Width, WorcRect .Height);
}

// установить зону захвата, используютса абсолютные позицыи точек четырехугольника на екране.
// ето требуетса когда вы устанавливаете зону захвата с помощбю мышы.
public void MuveWorckRect(int X, int Y, int X1, int Y1)
{
WorcRect = new Rectangle(X,Y,X1-X,Y1-Y);
}
// флаг обновления содержымого
[NonSerialized]
bool lockUpdate=false;

}
}



Из за чего такой странный подход? Мне нужно было анализировать каждый пиксел, поэтому захватывать картинку и в последствии опять таки анализировать эти пиксели опять из картинки для меня не имело смысла.


Данный код работает отлично на вин ХР, на семерке и висте только в режиме совместимости(Сильно тормозит и выдает не верные цвета). В режиме совместимости нужно вроде как отключать полупрозрачные окна. Но связи с тем что использовал я данный сабж для перевода цвета в биты то для корректной работы вам может и не придётся выключать визуальное оформление. Но включать совместимость с ХР обязательно. Иначе можете забыть о захвате изображения в реальном времени, с помощью данного кода.

среда, 6 февраля 2013 г.

Универсальная глубокая копия объекта.

Часто приходится создавать копию объекта, и каждый раз приходится переписывать весь код для учета специфику объекта. Несколько поразмыслив я нашел полу универсальное решение  проблемы. Почему полу универсальное? Потому что все равно при каждой новой задаче создания копии объекта , приходится учитывать его специфические требования. Так что данное решение хоть и не панацея, но одно из самых простых, оно использует стерилизацию и последующую десерелизацию из памяти.

   1:  
   2: using System.IO;
   3: using 
   4: System.Runtime.Serialization.Formatters.Binary;
   5: namespace Utils.Data
   6: {
   7: public class DeepCopy
   8: {
   9: public static object Clone(object inobject)
  10: {
  11: BinaryFormatter BF = new BinaryFormatter();
  12: MemoryStream memStream = new MemoryStream();
  13: BF.Serialize(memStream, inobject);
  14: memStream.Flush();
  15: memStream.Position = 0;
  16: return BF.Deserialize(memStream);
  17: }
  18: }
  19: }



Не правда ли просто крошечный код):.

Изменение размеренности массива. Redim.

 

Изменение размеренности массива.

   1:  
   2: using System;
   3: namespace Utils.Collections.Array
   4: {
   5:  public class 
   6: arrayExt
   7:  {
   8:  
   9: public static System.Array Redim(System.Array origArray, Int32 
  10: desiredSize)
  11:  
  12: {
  13:  
  14: //Определяем тип элемента 
  15: массива.
  16:  
  17: Type t = 
  18: origArray.GetType().GetElementType();
  19:  
  20: //Создаем новый массив с требуемым числом 
  21: элементов.
  22:  
  23: //Тип массива должен совпадать с типом исходного 
  24: массива.
  25:  
  26: System.Array newArray = System.Array.CreateInstance(t, 
  27: desiredSize);
  28:  
  29: //Копируем элементы из исходного массива в новый 
  30: массив.
  31:  
  32: System.Array.Copy(origArray, 
  33: 0,
  34:  
  35: newArray, 0, Math.Min(origArray.Length, 
  36: desiredSize));
  37:  
  38: //Возвращаем новый 
  39: массив,
  40:  
  41: return newArray;
  42:  
  43: }
  44:  }
  45: }

Автостарт программы при загрузке виндовс.

При написании резидентных программ и сервисов, возникает потребность в автоматическом запуске программы при загрузке виндовс. Каждый мало-мальски знакомый с этой операционной системой пользователь скажет вам, что для решения этой проблемы нужно воспользоваться реестром.
Вот с помощью столь не хитрого кода я записываю программу в реестр для автостарта:

   1:  
   2: // Alexandr Lysy
   3: using System.Reflection;
   4: using Microsoft.Win32;
   5: namespace Utils.AppAutostart
   6: {
   7:  public class 
   8: AutoStart
   9:  {
  10:  
  11: public static void Enableautostart(string 
  12: progname)
  13:  
  14: {
  15:  
  16: RegistryKey key = 
  17: Registry.CurrentUser.CreateSubKey("Software\\microsoft\\windows\\currentversion\\run");
  18:  
  19: key.SetValue(progname, 
  20: Assembly.GetEntryAssembly().Location);
  21:  
  22: }
  23:  public static void 
  24: Enableautostart(string progname, string 
  25: path)
  26:  
  27: {
  28:  
  29: RegistryKey key = 
  30: Registry.CurrentUser.CreateSubKey("Software\\microsoft\\windows\\currentversion\\run");
  31:  
  32: key.SetValue(progname, path);
  33:  
  34: }
  35:  public static bool 
  36: IsEnabled(string progname)
  37:  
  38: {
  39:  
  40: RegistryKey key = 
  41: Registry.CurrentUser.OpenSubKey("Software\\microsoft\\windows\\currentversion\\run", 
  42: true);
  43:  if 
  44: (key.GetValue(progname) != null) return 
  45: true;
  46:  
  47: return false;
  48:  }
  49:  public static void 
  50: Disableautostart(string progname)
  51:  
  52: {
  53:  
  54: RegistryKey key = 
  55: Registry.CurrentUser.OpenSubKey("Software\\microsoft\\windows\\currentversion\\run", 
  56: true);
  57:  
  58: key.DeleteValue(progname);
  59:  
  60: }
  61:  }
  62: }


Собственно использование: Enableautostart(“myProg”); Проверено на вин ХР.
Вроде не чего сложного программа, как и должна, запускается при старте виндовс. Но замечена довольно не красивая проблема, приложение принципиально отказывается загружать ресурсы (картинки) как внешние, так и встроенные в ресурсы. Я пытался обойти проблему с помощью не сложной комбинации. Первой загружается программа загрузчик, задача которой запустить основную программу с полными правами. Но из этого не чего не вышло);.  Так же не чего не дали пляски с безопасностью, в коде. Так что, я получил множество информации для размышления. Чем делюсь и с вами.
Уважаемые коллеги, если у вас есть решение данной проблемы, прошу поделиться им в комментариях. Заранее благодарю.