|
При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Использование библиотек кода в windows-формах
Подсчет времени выполнения задачи
Сравним теперь работу классов 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 (Code\Glava5\ PrecisionCounter).
Программа для фотографирования экрана. Библиотеки user32.dll и GDI32
Операционная система Windows предоставляет возможность снятия снимков с экрана (скриншотов) — при нажатии клавиши Print Screen текущее изображение помещается в буфер обмена. Затем можно вставить изображение непосредственно в документ или графический редактор — для последующего сохранения в нужном формате. Сделаем что-то подобное — простое приложение для фотографирования экрана и последующего сохранения изображений. Создайте новое Windows-приложение и назовите его 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 (Code\Glava5\ScreenShot).
Вызов COM-компонентов из управляемого кода
При вызове объекта COM-клиентом .NET-среда Common Language Runtime создает временную оболочку RCW (Runtime Callable Wrapper), причем всего одну, независимо от количества ссылок на объект. Это дает гарантию, что все обращения к объекту будут происходит только через одного проводника. Далее CLR, используя полученные из библиотеки метаданные, создает вызываемый объект и оболочку для возврата данных. Также CLR берет на себя функцию контроля сборки мусора в оболочке — разработчику не приходится задумываться об этом.
Главной задачей оболочки RCW является скрытие различий между управляемым и неуправляемым кодом. Она командует жизненным циклом COM-объекта, передает вызовы методов между управляемым и неуправляемым кодами, конвертирует параметры методов. В результате RCW позволяет разработчикам писать код, который использует COM-объекты как обычные .NET-объекты.


Code\Glava5\