При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using Microsoft.Xna.Framework.Graphics;
namespace Application1 { public partial class MainForm : Form { // Объявим поле графического устройства для видимости в методах GraphicsDevice device;
public MainForm() { InitializeComponent();
// Подпишемся на событие Load формы this.Load += new EventHandler(MainForm_Load);
// Попишемся на событие FormClosed формы this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed); }
void MainForm_FormClosed(object sender, FormClosedEventArgs e) { // Удаляем (освобождаем) устройство device.Dispose(); // На всякий случай присваиваем ссылке на устройство значение null device = null; }
void MainForm_Load(object sender, EventArgs e) { // Создаем объект представления для настройки графического устройства PresentationParameters presentParams = new PresentationParameters(); // Настраиваем объект представления через его свойства presentParams.IsFullScreen = false; // Включаем оконный режим presentParams.BackBufferCount = 1; // Включаем задний буфер // для двойной буферизации // Переключение переднего и заднего буферов // должно осуществляться с максимальной эффективностью presentParams.SwapEffect = SwapEffect.Discard; // Устанавливаем размеры заднего буфера по клиентской области окна формы presentParams.BackBufferWidth = this.ClientSize.Width; presentParams.BackBufferHeight = this.ClientSize.Height;
// Создадим графическое устройство с заданными настройками device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, this.Handle, presentParams); }
protected override void OnPaint(PaintEventArgs e) { device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
base.OnPaint(e); } } } Выбрасывается исключение: Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Работа с потоками данных
Полезные ссылки
Все необходимые для выполнения данной работы программы можно найти в http://old.intuit.ru/department/se/prcsharp08/19/Setup.zipприлагаемом каталоге.
- Работа с потоками слегка описана в intuit:
- Работа с альтернативными потоками:
- http://habrahabr.ru/blogs/windows/46990/
- http://wladm.narod.ru/C_Sharp/filesstreams.html#top
- Динамическое создание классов в модели CodeDOM:
- Динамическое создание классов в модели System.Reflection.Emit:
- Внедрение WPF в Windows Forms
- Как в WPF управлять проигрыванием видео с помощью MediaElement
- Пример по криптографии
- Класс Type
Введение
В современной литературе термином "Поток" обозначают два совершенно разных понятия: Thread (трэд) и Stream (стрим), из-за чего имеет место некоторая путаница. Под Thread понимают код, реально выполняемый процессором в данный момент времени в рамках одного процесса. Процесс - это приложение, загруженное в изолированное адресное пространство компьютера. Процесс может состоять из нескольких изолированных копий - доменов. Копия может иметь несколько Thread и каждому процессор уделяет определенное количество квантов времени, обслуживая их последовательно по бесконечному циклу. Так эмулируются (имитируются) параллельные вычисления и концепция многозадачности на однопроцессорных (малопроцессорных) системах.
Под Stream понимают логическую абстракцию, с помощью которой можно подключаться к различным типам источников данных одинаковым образом. Stream - это модель, через которую последовательно считываются или записываются элементы данных. Для исключения разночтения некоторые авторы предлагают такие термины: Thread - нить (или поток выполнения ), Stream - поток (или поток данных ), чем мы с удовольствием и воспользуемся.
Абстракция Stream создает канал связи между приложением и устройством хранения информации (резервным хранилищем), а также предоставляет необходимые для манипулирования данными инструменты. Для потока характерно направление: входной поток - данные передаются от устройства в приложение, выходной поток - от приложения к устройству. Есть однонаправленные потоки - например, файловый поток может открываться только для чтения или только для записи, с клавиатуры символы можно только считывать, и т.д. Есть и двунаправленные потоки - например, сессия по протоколу telnet или обмен данными между компьютером и модемом, которые представляют собой чередование однонаправленных потоков. Файл тоже может быть открыт для чтения и записи одновременно.
Потоки в библиотеке .NET Framework представлены классами ввода-вывода и инкапсулируют собой низкоуровневые операции, которые в конечном итоге заставляют операционную систему выполнять действия по чтению или записи информации на конкретные адресуемые устройства. Потоки можно трактовать и как последовательность байт, представляющих данные в буфере памяти, файле, или полученные по сетевому соединению, клавиатурному вводу, а также данные, выводимые на различные устройства, включая консоль.
Потоки, которые непосредственно связываются с устройствами, можно назвать рабочими (основными). На самом низком уровне система ввода-вывода .NET оперирует байтами, поэтому все рабочие потоки являются байтовыми. Однако есть несколько классов-надстроек (оболочек) к рабочим потокам, которые устанавливают свой формат данных при чтении/записи или облегчают пересылку данных определенного типа.
Создав основной поток и погрузив его в надстройку, можно значительно облегчить решение задачи пересылки некоторых типов данных. Так, для упрощения пересылки текстов имеются специализированные классы, автоматически преобразующие байтовые потоки в символьные и наоборот. Чтение информации с клавиатуры и вывод на консоль обеспечивают стандартные потоки, которые являются одновременно и символьными.
Базовым классом большей части потоков является класс Stream, который в свою очередь наследует от класса System.MarshalByRefObject
Несмотря на то, что Stream абстрактный класс, этим типом (для реализации принципа динамического полиморфизма) можно объявлять ссылки, адресующие экземпляры его потомков. Но такие ссылки будут адресовать только те члены экземпляров, которые определены в самом классе Stream, а для использования других членов требуется приведение типа ссылки к объекту-экземпляру. Однако большинство наследуемых членов в потомках Stream являются виртуальными, переопределяются в них и адресоваться ссылками типа Stream могут. Вот некоторые из этих наследуемых членов
Упражнение 1. Работа с FileStream как с чисто байтовым потоком
Знакомство с потоками начнем с файлового потока FileStream как инструмента для чтения/записи информации в файлы. Нужно понимать, что объектная модель FileStream является высокоуровневой оболочкой API -функций операционной системы по работе с файлами. Но в то же время сам FileStream имеет несколько специализированных высокоуровневых оболочек для облегчения чтения/записи в файл данных различных форматов.
Вначале рассмотрим использование только одного FileStream в чистом виде. Начнем с упражнения, иллюстрирующего пример из MSDN, который попутно немного 'подкрутим'.
- Создайте новый проект App1 консольного типа в решении Stream
Стартовым его назначать нет смысла, поскольку в решении он пока будет единственным.
- Заполните файл Program.cs следующим кодом
using System; using System.Text; using System.IO; namespace App1 { class Program { static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.White; string newLine = System.Environment.NewLine;// Пара "\r\n" string path = @"e:\temp1\MyTest.txt";// Задаем полное имя файла string dir = Path.GetDirectoryName(path); // Выделяем каталог // Если каталог не существует, создаем его if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } // Создаем новый или усекаем существующий файл // и сразу открываем его для записи using (FileStream fs = File.Create(path)) { AddText(fs, "Буря мглою "); AddText(fs, "небо кроет,"); AddText(fs, "\r\nВихри снежные крутя."); AddText(fs, "\r\nТо как зверь она завоет,\r\nТо заплачет как дитя.\r\n"); AddText(fs, newLine + "Сгенерированные символы:" + newLine); for (int i = 32; i < 123; i++) // Начинаем с пробела ASCII=32 { // Добавляем символ в поток AddText(fs, Convert.ToChar(i).ToString()); // Через каждые десять символов вставляем новую строку, // когда i делится без остатка //if (i % 10 == 0) // Вариант if (Math.IEEERemainder(Convert.ToDouble(i), 10) == 0) { // Вариант с библиотечным свойством //AddText(fs, newLine); // Вариант в управляющих символах //AddText(fs, "\r\n"); // Вариант в шестнадцатиричных кодах ASCII //AddText(fs, Convert.ToChar(0x0D).ToString() + // Convert.ToChar(0x0A).ToString()); // Вариант в целых кодах ASCII AddText(fs, Convert.ToChar(13).ToString() + // Попробуйте закомментировать эту строку!!! Convert.ToChar(10).ToString()); } } } // Здесь, по окончании инструкции using поток автоматически закрывается // Открываем файловый поток для чтения и обрабатываем его //using (FileStream fs = File.OpenRead(path)) // Вариант using(Stream fs = new FileInfo(path).OpenRead()) // Вариант для разнообразия { byte[] bytes = new byte[fs.Length]; // Заготавливаем байтовый массив int count = fs.Read(bytes, 0, bytes.Length);// Заполняем массив из файла UTF8Encoding tmp = new UTF8Encoding(true);// Объект кодировки символов String str = tmp.GetString(bytes);// Преобразуем массив в строку Console.WriteLine(str);// Печатаем на консоль Console.WriteLine("\nВсего прочитано байт: {0}", count); Console.WriteLine("Из них отображаемых символов: {0}", str.Length); } // Здесь, по окончании инструкции using поток автоматически закрывается // Печатаем ASCII 'перевод строки' - 'возврат каретки' int a = '\r', b = '\n'; String ascii = String.Format("Управляющие символы: "); ascii += String.Format("\\r={0}, \\n={1}", a, b); Console.WriteLine(ascii); // Проверяем строку NewLine byte[] bts = new ASCIIEncoding().GetBytes(newLine); a = bts[0]; b = bts[1]; ascii = String.Format("Управляющие символы NewLine: "); ascii += String.Format("\\r={0}, \\n={1}", a, b); Console.WriteLine(ascii); Console.ReadLine(); } private static void AddText(FileStream fs, string value) { // Преобразуем строку в массив байтов через объект кодировки символов byte[] bytes = new UTF8Encoding(true).GetBytes(value); // Записываем в открытый поток fs.Write(bytes, 0, bytes.Length); } } }
- Запустите приложение - результат должен быть таким
- Разберитесь с кодом
Интересно отметить следующее... Если не записывать в файл управляющий символ ' \r ' - возврат каретки, а только ' \n ' - новая строка, то при чтении файла на консоль функция Console.WriteLine() этого не заметит, а текстовый редактор Notepad заметит (попробуйте). Это значит, что при побайтовой записи нужно полностью обеспечивать документ всей необходимой информацией, не надеясь на умные устройства вывода. Альтернативной заменой строки " \r\n " может служить статическое свойство System.Environment.NewLine, которое означает то же самое и в приведенном коде частично используется.
Всякий раз, когда мы открываем файл через объект FileStream, следует помнить, что по завершении работы этот файл следует закрыть, чтобы освободить дескриптор файла и вернуть его операционной системе. В противном случае файл будет считаться занятым и будет освобожден только после завершения приложения. Закрыть файл можно методом Close() объекта FileStream, либо открывать в инструкции using(), по завершению работы которой файл автоматически будет закрыт. Последний прием и был использован нами в приведенном выше коде.
Упражнение 2. Подробности использования байтового потока FileStream
В данном упражнении рассмотрим файловый поток более подробно. Класс FileStream обеспечивает практически все мыслимые потребности программного управления чтением/записью информации в файлы. После создания экземпляра потока (объекта) связанный с ним файл будет представлен этим объектом и все дальнейшие манипуляции с файлом будут выполняться через этот объект.
Для создания экземпляра класс FileStream имеется множество перегрузок его конструктора, с которыми можно ознакомиться в MSDN. Наиболее типовая из них следующая:
FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share)
- path - относительный или абсолютный путь для файла, который будет представлен потоком
-
mode - значение перечисления FileMode, устанавливающее режим открытия или создания файла
- Append - открывает файл, если он существует, и переходит в его конец, либо создает новый файл. Этот режим может быть использован только в сочетании с доступом FileAccess.Write. Попытка чтения или перемещения указателя на другую позицию генерирует исключение
- Create - создает новый файл и открывает его для записи. Если файл уже существует, то он будет перезаписан на пустой
- CreateNew - создает новый файл и открывает его для записи. Если файл уже существует, генерируется исключение IOException
- Open - открывает существующий файл в режиме, определяемом перечислением FileAccess. Если указанный файл не существует, генерируется исключение FileNotFoundException
- OpenOrCreate - открывает существующий или создает новый файл с режимом доступа, определяемым перечислением FileAccess
- Truncate - открывает существующий файл для записи и усекает его длину до нуля байт. При попытке чтения генерируется исключение
-
access - значение перечисления FileAccess, устанавливающее режим доступа к файлу. Оно влияет на значения флагов CanRead - возможно чтение, CanWrite - возможна запись, CanSeek - возможен параллельный доступ к дисковому файлу
- Read - только для чтения
- ReadWrite - для чтения и записи
- Write - только для записи
-
share (разделять, делить, совместно использовать) - значение перечисления FileShare, устанавливающее режим доступа к тому же самому файлу других процессов или объектов FileStream
- Delete - разрешает последующее удаление файла
- Inheritable - делает дескриптор файла наследуемым дочерними процессами. Этот режим непосредственно не поддерживается Win32
- None - отключает режим разделения и переводит файл в монопольный доступ только текущего процесса
- Read - открывает совместный доступ только для чтения
- ReadWrite - открывает совместный доступ для чтения и записи
- Write - открывает совместный доступ только для записи
Не стоит забывать, что класс FileStream имеет в своем распоряжении и весь набор членов, наследуемых от базового класса Stream.
- В панели Solution Explorer вызовите контекстное меню для узла решения (не проекта!), командой Add/New Project добавьте новый проект с именем App2 типа консольного приложения и назначьте его стартовым
- Заполните файл Program.cs следующим кодом, иллюстрирующим работу с байтовым потоком FileStream
using System; using System.Collections.Generic; using System.Text; // Дополнительные пространства имен using System.IO;// Для потоков namespace App2 { class Program { static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.White; Console.Title = "Упражнение 2. Файловые потоки"; new MyClass().Foo(); Console.ReadLine();// Задержка консоли } } } namespace App2 { class MyClass { public void Foo() { byte[] bytes; // Только объявили // Сохраняемые данные string sData = "Привет всем!"; // В Unicode займет 12*2 байт int iData1 = (int)12345; // 4 байта int iData2 = int.MaxValue; // 4 байта float fData = 67F; // 4 байта double dData = 89D; // 8 байт // Сохраняем значения в файле String fileName = "Data.dat";// Без пути - будет создан в каталоге сборки FileStream fileStream = null; try { // Создаем или усекаем fileStream = new FileStream(fileName, FileMode.Create); // Сохраняем строку в универсальной кодировке, // занимающей 2 байта на каждый символ bytes = new UnicodeEncoding().GetBytes(sData); // Кодировка UTF-16 for (int i = 0; i < bytes.Length; i++) fileStream.WriteByte(bytes[i]); // Побайтно // Сохраняем первое целое число bytes = new byte[sizeof(int)]; for (int i = 0; i < sizeof(int); i++) bytes[i] = (byte)(iData1 >> 8 * i); // Делим на байты, начиная с младшего fileStream.Write(bytes, 0, sizeof(int));// Нулевой сдвиг относительно текущего // Сохраняем второе целое число bytes = new byte[sizeof(int)]; bytes = BitConverter.GetBytes(iData2); fileStream.Write(bytes, 0, sizeof(int));// Нулевой сдвиг относительно текущего // Сохраняем внутренне представление вещественного float (Single) bytes = BitConverter.GetBytes(fData);// Массив той же длины int fileStream.Write(bytes, 0, sizeof(float));// Нулевой сдвиг относительно текущего // Сохраняем внутренне представление удвоенного double bytes = new byte[sizeof(double)]; bytes = BitConverter.GetBytes(dData); fileStream.Write(bytes, 0, sizeof(double));// Нулевой сдвиг относительно текущего fileStream.Flush(); // Слить кэш (необязательно!) } finally { if (fileStream != null) fileStream.Close(); } // Читаем значения из файла и печатаем их на консоль try { // Открываем только для чтения fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); if (fileStream.CanSeek) // Поддерживает ли такую возможность fileStream.Seek(0L, SeekOrigin.Begin); // Необязательно! // Читаем строку, зная, сколько нужно прочитать bytes = new byte[sData.Length * 2];// Для UTF-16 это 2 байта на каждый символ fileStream.Read(bytes, 0, sData.Length * 2); Console.WriteLine((new UnicodeEncoding()).GetString(bytes)); // Далее идут байты для представления первого целого числа bytes = new byte[sizeof(int)]; fileStream.Read(bytes, 0, sizeof(int)); int a = 0; for (int i = 0; i < sizeof(int); i++) a |= bytes[i] << 8 * i; // Заполняем все байты, начиная с младшего Console.WriteLine(a); // Далее идут байты для представления второго целого числа fileStream.Read(bytes, 0, sizeof(int)); a = BitConverter.ToInt32(bytes, 0); Console.WriteLine(a); // Считываем и печатаем вещественное float // Побитовые операции к типам с плавающей точкой применять нельзя bytes = new byte[sizeof(float)]; fileStream.Read(bytes, 0, sizeof(float)); float b = BitConverter.ToSingle(bytes, 0); Console.WriteLine(b); // Считываем и печатаем double bytes = new byte[sizeof(Double)]; fileStream.Read(bytes, 0, sizeof(double)); double c = BitConverter.ToDouble(bytes, 0); Console.WriteLine(c); // Измеряем длину потока, пока открыт Console.WriteLine("==============\nВсего байт: {0}", fileStream.Length); } finally { fileStream.Close(); } // Измеряем длину файла Console.WriteLine("Длина файла: {0}", new FileInfo(fileName).Length); } } }
Приведенный код иллюстрирует важный вывод о том, что байтовый поток может хранить любой тип информации, все равно это будет последовательность байт. Главная задача состоит в том, чтобы эту информацию правильно разбить при записи, а затем правильно извлечь, соединить и интерпретировать при чтении.
- Запустите приложение App2 - результат будет таким