Опубликован: 11.09.2006 | Доступ: свободный | Студентов: 7648 / 473 | Оценка: 4.26 / 3.45 | Длительность: 30:46:00
ISBN: 978-5-9556-0080-2
Лекция 5:

Использование библиотек кода в windows-формах

< Лекция 4 || Лекция 5: 123456 || Лекция 6 >

Подсчет времени выполнения задачи

Сравним теперь работу классов StringBuilder и String, используя точный счетчик времени выполнения задачи. Фактически вычисления производятся в тактах процессора, а потом переводятся в миллисекунды. Создайте новое консольное приложение и назовите его PrecisionCounter. Добавляем новый класс к проекту и называем его MechanismCounter. Полный листинг этого класса с комментариями:

using System;
using System.Runtime.InteropServices;
using System.Security;

namespace PrecisionCounter
{
	public class MechanismCounter
	{
	
			static Int64 _start;
			static Int64 finish;
			static Int64 freq;

			/// <summary>
			/// Начало подсчета времени выполнения.
			/// </summary>
			public static void Start()
			{
				MechanismCounter._start = 0;
				MechanismCounter.QueryPerformanceCounter(ref _start);
			}

			/// <summary>
			/// Завершение подсчета времени выполнения  и возвращение  времени в секундах.
			/// </summary>
			/// <returns>Время в секундах, потраченное на выполнение участка
			/// кода. Десятичная часть отражает доли секунды.</returns>
			public static float Finish()
			{
				MechanismCounter.finish = 0;
				MechanismCounter.QueryPerformanceCounter(ref finish);
				MechanismCounter.freq = 0;
				MechanismCounter.QueryPerformanceFrequency(ref freq);
				return (((float)(MechanismCounter.finish - MechanismCounter._start))/((float)MechanismCounter.freq));
			}
			/// <summary>
			/// Получение текущего значения тактов процессора.
			/// </summary>
			/// <param name="performanceCount">Переменная, в которую запишется значение.</param>
			/// <returns></returns>
			[SuppressUnmanagedCodeSecurity]
			[DllImport("Kernel32.dll")]
			static extern bool QueryPerformanceCounter(ref Int64 performanceCount);
			/// <summary>
			/// Получение текущей частоты (количество тактов / секунда) работы процессора.
			/// </summary>
			/// <param name="frequency">Переменная, в которую запишется значение.</param>
			/// <returns></returns>
			[SuppressUnmanagedCodeSecurity]
			[DllImport("Kernel32.dll")]
			static extern bool QueryPerformanceFrequency(ref Int64 frequency);
		}
	}
Листинг 5.10.

Итак, механизм счетчика готов. Запустим теперь две задачи: будем слагать строки, используя один раз String, а второй — StringBuilder:

using System;
using System.Text;

namespace PrecisionCounter
{

	class TestCounter
	{		
		[STAThread]
		static void Main(string[] args)
		{
			Output("Запуск процесса подсчета времени выполнения сложения строк с использованием типа данных string");
			MechanismCounter.Start();
			string s = String.Empty;
			for(int i = 0; i < 10000; i++)
			{
				s+="TestPerfCounter";
			}
			float timeString = MechanismCounter.Finish();
			Output("Завершение процесса подсчета времени. Время выполнения (сек): " + timeString);
			
			Output("Запуск процесса подсчета времени выполнения сложения строк с использованием типа данных StringBuilder");
			MechanismCounter.Start();
			StringBuilder sb = new StringBuilder();
			for(int i = 0; i < 10000; i++)
			{
				sb.Append("TestCounter");
			}
			float timeStringBuilder = MechanismCounter.Finish();
			Output("Завершение процесса подсчета времени. Время выполнения (сек): " + timeStringBuilder);
		}
		/// <summary>
		/// Вывод на экран заданного значения.
		/// </summary>
		/// <param name="s">Значение.</param>
		static void Output(string s)
		{
			Console.WriteLine(s);
		}
	}
}
Листинг 5.11.

Как и следовало ожидать, использование класса StringBuilder значительно ускоряет работу (рис. 5.9).

Результат работы приложения PrecisionCounter

Рис. 5.9. Результат работы приложения PrecisionCounter

На диске, прилагаемом к книге, вы найдете приложение PrecisionCounter (Code\Glava5\ PrecisionCounter).

Программа для фотографирования экрана. Библиотеки user32.dll и GDI32

Операционная система Windows предоставляет возможность снятия снимков с экрана (скриншотов) — при нажатии клавиши Print Screen текущее изображение помещается в буфер обмена. Затем можно вставить изображение непосредственно в документ или графический редактор — для последующего сохранения в нужном формате. Сделаем что-то подобное — простое приложение для фотографирования экрана и последующего сохранения изображений. Создайте новое Windows-приложение и назовите его ScreenShot. Добавляем на него одну кнопку и устанавливаем следующие значения формы и кнопки:

Form1, форма, свойство Значение
FormBorderStyle FixedSingle
Size 174; 94
Text ScreenShot!
Button1, свойство Значение
Name btnCreate
BackgroundImage КнопкаCode\Glava5\ScreenShot\Image\button.bmp
Location 0; 0
Size 176; 64
Text

Из окна Toolbox перетаскиваем на форму элемент управления SaveFileDialog — его свойства установим программно. Добавляем класс к проекту и называем его ScreenShotDll.cs. Далее привожу полный листинг этого класса с комментариями:

using System;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;

namespace ScreenShot
{

	 /// <summary>
	 /// Класс для создания снимков экрана
	 /// </summary>
	 public class ScreenShotDll
	 {
		 /// <summary>
		 ///Создание снимка
		 /// </summary>
		 /// <param name="fileName">Название файла.</param>
		 /// <param name="imageFormat">Формат изображения.</param>
		 public void CaptureScreen(string fileName,ImageFormat imageFormat)
		 {
			 int hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow()), 
				 hdcDest = GDI32.CreateCompatibleDC(hdcSrc),
				 hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,
				 GDI32.GetDeviceCaps(hdcSrc,8),GDI32.GetDeviceCaps(hdcSrc,10)); 
			 GDI32.SelectObject(hdcDest,hBitmap);
			 GDI32.BitBlt(hdcDest,0,0,GDI32.GetDeviceCaps(hdcSrc,8),
				 GDI32.GetDeviceCaps(hdcSrc,10),
				 hdcSrc,0,0,0x00CC0020);
			 SaveImageAs(hBitmap,fileName,imageFormat);
			 Cleanup(hBitmap,hdcSrc,hdcDest);
		 }
		 /// <summary>
		 /// Получение снимка в формате Windows Bitmap
		 /// </summary>
		 /// <returns></returns>
		 public Bitmap CaptureScreen()
		 {
			 int hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow()), 
				 hdcDest = GDI32.CreateCompatibleDC(hdcSrc),
				 hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,
				 GDI32.GetDeviceCaps(hdcSrc,8),GDI32.GetDeviceCaps(hdcSrc,10)); 
			 GDI32.SelectObject(hdcDest,hBitmap);
			 GDI32.BitBlt(hdcDest,0,0,GDI32.GetDeviceCaps(hdcSrc,8),
				 GDI32.GetDeviceCaps(hdcSrc,10),
				 hdcSrc,0,0,0x00CC0020);
			 Bitmap btm = GetImage(hBitmap);
			 Cleanup(hBitmap,hdcSrc,hdcDest);
			 return btm;
		 }
		 /// <summary>
		 /// Освобождение ресурсов системы.
		 /// </summary>
		 /// <param name="hBitmap">Указатель на структуру данных.</param>
		 /// <param name="hdcSrc">Указатель на структуру данных источника.</param>
		 /// <param name="hdcDest">Указатель на структуру данных назначения.</param>
		 private void Cleanup(int hBitmap, int hdcSrc, int hdcDest)
		 {
			 User32.ReleaseDC(User32.GetDesktopWindow(),hdcSrc);
			 GDI32.DeleteDC(hdcDest);
			 GDI32.DeleteObject(hBitmap);
		 }      
		 /// <summary>
		 /// Сохранение снимка
		 /// </summary>
		 /// <param name="hBitmap">Указатель на изображение.</param>
		 /// <param name="fileName">Название файла.</param>
		 /// <param name="imageFormat">Формат изображения.</param>
		 private void SaveImageAs(int hBitmap, string fileName, ImageFormat imageFormat)
		 {
			 Bitmap image = new Bitmap(Image.FromHbitmap(new IntPtr(hBitmap)),
				 Image.FromHbitmap(new IntPtr(hBitmap)).Width,
				 Image.FromHbitmap(new IntPtr(hBitmap)).Height);
			 image.Save(fileName,imageFormat);
		 }
		 /// <summary>
		 /// Возвращение  изображения по указателю.
		 /// </summary>
		 /// <param name="hBitmap">Указатель на изображение.</param>
		 /// <returns></returns>
		 private Bitmap GetImage(int hBitmap)
		 {
			 Bitmap image = 
				 new Bitmap(Image.FromHbitmap(new IntPtr(hBitmap)),
				 Image.FromHbitmap(new IntPtr(hBitmap)).Width,
				 Image.FromHbitmap(new IntPtr(hBitmap)).Height);
			 return image;
		 }
	 }
	 /// <summary>
	 /// Реализация методов библиотеки User32
	 /// </summary>
	 class User32
	 {
		 /// <summary>
		 /// Возвращение  указателя на рабочий стол.
		 /// </summary>
		 /// <returns></returns>
		 [DllImport("User32.dll")]
		 public static extern int GetDesktopWindow();
		 /// <summary>
		 /// Получение структуры данных.
		 /// </summary>
		 /// <param name="hWnd">Указатель на окно.</param>
		 /// <returns></returns>
		 [DllImport("User32.dll")]
		 public static extern int GetWindowDC(int hWnd);
		 /// <summary>
		 /// Освобождение  структуры данных.
		 /// </summary>
		 /// <param name="hWnd">Указатель на окно.</param>
		 /// <param name="hDC">Указатель на структуру данных.</param>
		 /// <returns></returns>
		 [DllImport("User32.dll")]
		 public static extern int ReleaseDC(int hWnd,int hDC);
	 }
	 /// <summary>
	 /// Реализация методов библиотеки GDI32
	 /// </summary>
	 class GDI32
	 {
		 /// <summary>
		 /// Передача изображения.
		 /// </summary>
		 /// <param name="hdcDest">Указатель на назначение передачи.</param>
		 /// <param name="nXDest">Х координата верхнего левого угла назначения.</param>
		 /// <param name="nYDest">У координата верхнего левого угла назначения.</param>
		 /// <param name="nWidth">Ширина прямоугольника.</param>
		 /// <param name="nHeight">Высота прямоугольника.</param>
		 /// <param name="hdcSrc">Указатель на источник.</param>
		 /// <param name="nXSrc">Х координата верхнего левого угла источника.</param>
		 /// <param name="nYSrc">У координата верхнего левого угла источника.</param>
		 /// <param name="dwRop">Код растровой операции.</param>
		 /// <returns></returns>
		 [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);
		 /// <summary>
		 /// Создание  и запись в структуру данных изображения.
		 /// </summary>
		 /// <param name="hdc">Указатель на структуру данных.</param>
		 /// <param name="nWidth">Ширина изображения.</param>
		 /// <param name="nHeight">Высота изображения.</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern int CreateCompatibleBitmap(int hdc,int nWidth, int nHeight);
		 /// <summary>
		 /// Создание и сохранение  в памяти структуры данных, совместимой с указанным устройством вывода.
		 /// Для помещения  изображения в структуру данных 
		 /// необходимо указать высоту, ширину и цветовой  режим  изображения. 
		 /// Это можно сделать с помощью функции CreateCompatibleBitmap, поддерживаемой устройствами с растровым выводом. 
		 /// Для получения информации об этих устройствах используется функция GetDeviceCaps. 
		  /// После использования структуры данных ее нужно удалить с помощью функции DeleteDC.
		 /// </summary>
		 /// <param name="hdc">Указатель на существующую структуру данных. Если указатель равен null,
		 /// то функция создает  структуру для экрана текущего приложения.</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern int CreateCompatibleDC(int hdc);
		 /// <summary>
		 /// Удаление  указанной структуры данных.
		 /// </summary>
		 /// <param name="hdc">Структура данных.</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern bool DeleteDC(int hdc);
		 /// <summary>
		 /// Удаление графических объектов с освобождением системных ресурсов.
		 /// </summary>
		 /// <param name="hObject">Указатель на графический объект.</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern bool DeleteObject(int hObject);
		 /// <summary>
		 /// Возвращение  информации об указанной структуре.
		 /// </summary>
		 /// <param name="hdc">Указатель на структуру данных.</param>
		 /// <param name="nIndex">Индекс совместимости. .</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern int GetDeviceCaps(int hdc, int nIndex);
		 /// <summary>
		 /// Выбор объекта, находящегося в указанной структуре данных.
		 /// </summary>
		 /// <param name="hdc">Указатель на структуру данных.</param>
		 /// <param name="hgdiobj">Указатель на объект.</param>
		 /// <returns></returns>
		 [DllImport("GDI32.dll")]
		 public static extern int SelectObject(int hdc, int hgdiobj);
	 }
 }
Листинг 5.12.

Вся функциональность практически готова. Осталось только добавить обработчик кнопки btnCreate:

private void btnCreate_Click(object sender, System.EventArgs e)
  {
		
	SaveFileDialog diag = new SaveFileDialog();
	diag.Filter = "Файлы bmp|*.bmp";
	if(diag.ShowDialog() != DialogResult.OK)
	  return;
	// Получаем адрес файла.
	string filename = diag.FileName;
	// Создаем экземпляр  класса ScreenShotDll
	ScreenShotDll shoter = new ScreenShotDll();
	// Создаем и сохраняем изображение.
	shoter.CaptureScreen(filename, System.Drawing.Imaging.ImageFormat.Bmp);
			
		
  }

Запускаем приложение (рис. 5.10). После нажатия на кнопку задаем в появившемся окне SaveFileDialog имя файла и сохраняем его.

Приложение ScreenShot! и сделанный им снимок

увеличить изображение
Рис. 5.10. Приложение ScreenShot! и сделанный им снимок

На диске, прилагаемом к книге, вы найдете приложение ScreenShot (Code\Glava5\ScreenShot).

Вызов COM-компонентов из управляемого кода

При вызове объекта COM-клиентом .NET-среда Common Language Runtime создает временную оболочку RCW (Runtime Callable Wrapper), причем всего одну, независимо от количества ссылок на объект. Это дает гарантию, что все обращения к объекту будут происходит только через одного проводника. Далее CLR, используя полученные из библиотеки метаданные, создает вызываемый объект и оболочку для возврата данных. Также CLR берет на себя функцию контроля сборки мусора в оболочке — разработчику не приходится задумываться об этом.

Главной задачей оболочки RCW является скрытие различий между управляемым и неуправляемым кодом. Она командует жизненным циклом COM-объекта, передает вызовы методов между управляемым и неуправляемым кодами, конвертирует параметры методов. В результате RCW позволяет разработчикам писать код, который использует COM-объекты как обычные .NET-объекты.

< Лекция 4 || Лекция 5: 123456 || Лекция 6 >
Елена Дьяконова
Елена Дьяконова

При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: 

Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll

Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан.

Затем:

Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll

Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз.

Александр Сороколет
Александр Сороколет

Свойство WindowState формы blank Maximized. Не открывается почемуто на всё окно, а вот если последующую форму бланк открыть уже на макс открывается :-/