Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Лекция 6:

Работа с потоками данных

< Лекция 5 || Лекция 6: 12345678910111213

Упражнение 7. Байтовый поток MemoryStream

Для определения MemoryStream приведем выдержку из MSDN, поскольку точнее и не скажешь: " Класс MemoryStream создает потоки, которые в качестве резервного хранилища имеют память, а не подключение к сети или диску. Класс MemoryStream инкапсулирует данные, хранимые в виде массива байтов без знака, инициализированного при создании объекта MemoryStream . Массив может быть создан пустым. К инкапсулированным данным в памяти можно получить доступ напрямую. Потоки памяти могут снижать потребность во временных буферах и файлах в приложении ".

Поток MemoryStream, как и FileStream, тоже относится к рабочим потокам и допускает использование как напрямую, так и с помощью надстроек-оболочек. Для создания потока MemoryStream имеется несколько перегрузок конструктора и необходимые члены, приведенные в таблице

Таблица 19.4.
Конструктор или член Пояснения
MemoryStream() Создает пустой поток, размеры которого можно изменять. Открывается для записи и чтения данных
MemoryStream(byte[ ] buffer) Создает поток неизменяемого размера на базе массива, который сразу этим массивом инициируется. Открывается для записи и чтения данных
MemoryStream(byte[ ] buffer, bool writable) Создает поток, основанный на внешнем массиве беззнаковых байт неизменяемого размера в качестве резервного хранилища. Параметр writable определяет, будет ли этот поток только для записи или двухсторонний - для записи и чтения (как предыдущий без параметра)
MemoryStream(byte[ ] buffer, int index, int count) Создает поток, основанный на внешнем беззнаковом байтовом массиве фиксированной длины, из которого потоком контролируется только count байт, начиная с позиции index
MemoryStream(byte[ ] buffer, int index, int count, bool writable) Создает поток, основанный на внешнем беззнаковом байтовом массиве фиксированной длины, из которого потоку принадлежит только count байт, начиная с позиции index. Параметр writable определяет, будет ли этот диапазон только для записи или двухсторонний - для записи и чтения (как предыдущий без параметра)
MemoryStream(byte[ ] buffer, int index, int count, bool writable, bool publiclyVisible) Создает поток, основанный на внешнем беззнаковом байтовом буфере фиксированной длины, из которого потоком контролируется только count байт, начиная с позиции index. Параметр writable определяет, будет ли этот поток только для записи или двухсторонний - для записи и чтения. Если последний параметр установлен в true, то конролируемый потоком диапазон байт можно извлечь с помощью его метода GetBuffer()
MemoryStream(int capacity) Создает поток для записи и чтения данных с внутренним байтовым буфером изменяемой длины и начальной емкостью capacity. Когда в поток добавляются элементы, его емкость Capacity автоматически увеличивается блоками посредством перераспределения
void Flush() При выполнении этого метода содержимое внутреннего буфера, связанного с потоком, реально перепишется в целевое резервное хранилище (в базовый буфер)
byte[ ] GetBuffer() Возвращает массив байт без знака из базового хранилища потока MemoryStream, как бы он не создавался
int Read(byte[ ] buffer, int offset, int count) Читает из потока, начиная с индекса offset, count байт в заданный массив byte[ ]. Возвращает число фактически считанных байтов, которое может быть меньше, чем запрашивалось, если конец потока достигнут раньше. Может вернуть и нуль, если конец потока достигнут прежде, чем любые байты будут считаны
int ReadByte() Читает текущий байт, на который установлен указатель, и передвигает курсор потока на следующую байтовую позицию. Возвращает содержимое считанного байта в формате Int32 (поэтому результат нужно явно приводить) или -1, если достигнут конец потока
long Seek(long offset, System.IO.SeekOrigin loc) Устанавливает указатель потока на offset байт относительно начала, конца или текущей позиции. Возвращает новую позицию указателя относительно начала потока
void SetLength(long value) Устанавливает новую длину потока, у которого внутренний буфер допускает изменение длины и поддерживает запись
byte[ ] ToArray() Создает байтовый массив, заполняет его полным содержимым потока и возвращает ссылку на этот массив
void Write(byte[ ] buffer, int offset, int count) Записывает в поток count байт, начиная с текущей позиции указателя потока, блок байтов из указанного buffer массива с данными, начиная с индекса offset этого массива
void WriteByte(byte value) Пишет байт в текущую позицию потока и передвигает указатель
void WriteTo(System.IO.Stream stream) Копирует все содержимое текущего потока памяти в указанный целевой поток, возможно и в файловый
bool CanRead bool CanSeek bool CanWrite Свойства для чтения, подтверждающие поддерживаемые потоком возможности
int Capacity Свойство получает или устанавливает число байт в потоке, которые он способен вместить, прежде чем потребуется изменить размер его внутреннего буфера
long Length Число байт, которые поток фактически содержит
long Position Свойство получает или устанавливает указатель на текущую позицию потока

Упражнение построим в виде нескольких примеров, которые программно реализуем в методах графического приложения с консольным окном. Эти примеры позволят всесторонне проиллюстрировать возможности применения MemoryStream. В интерфейсном окне разместим список со ссылками для выбора примера, а результат выполнения будем выводить на консоль. Для разнообразия интерфейсное окно создадим на основе технологии WPF.

  • Командой File/Add/New Project добавьте к решению новый проект с именем App7, заполнив окно мастера так

  • При открытом на редактирование файле Window1.xaml выполните команду Project/Set as StartUp Project, чтобы назначить новый проект стартовым
  • В панели Solution Explorer раскройте узел App7, двойным щелчком на папке Properties вызовите панель конструктора проектов и для вкладки Application установите выпадающий список Output type в значение Console Application, чтобы включить консольное окно

Вначале сделаем заготовку для выбора примеров.

  • Отредактируйте разметку файла Window1.xaml следующим образом
<Window x:Class="App7.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Упражнение 7. Поток MemoryStream" 
    Height="300" 
    Width="300"
    MinHeight="300"
    MinWidth="300"
    ResizeMode="CanResizeWithGrip"
    WindowStartupLocation="CenterScreen"
    Closed="Window_Closed"
    >
    <ListBox SelectionChanged="ListBox_SelectionChanged" 
             ScrollViewer.VerticalScrollBarVisibility="Auto">
        <ListBox.Resources>
            <!--Selected color when the ListBox is selected-->
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Aqua" />
        </ListBox.Resources>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10397.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 1</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10398.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 2</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10402.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 3</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10423.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 4</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10418.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 5</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10440.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 6</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10434.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 7</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10436.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 8</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10438.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 9</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10424.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 10</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="Smiles/10408.jpg" Width="24" Height="24" Margin="2" />
            <Label VerticalContentAlignment="Center">Пример 11</Label>
        </StackPanel>
    </ListBox>
</Window>
  • В панели Solution Explorer вызовите контекстное меню для узла App7 и командой Add/New Folder добавьте к корню проекта папку Smiles для хранения рисунков
  • В панели Solution Explorer вызовите контекстное меню для папки Smiles и командой Add/Existing Item скопируйте в нее все рисунки из папки Source/Smiles, прилагаемой к данной работе (предварительно установите фильтр диалогового окна в значение All Files )
  • Заполните файл процедурного кода Window1.xaml.cs заготовкой, чтобы в итоге он стал таким
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
// Дополнительные пространства имен
using System.IO;
    
namespace App7
{
    public partial class Window1 : Window
    {
        // Объект для сохранения параметров приложения
        Properties.Settings settings = new App7.Properties.Settings();
    
        public Window1()
        {
            // Настройка консоли
            Console.ForegroundColor= ConsoleColor.White;
            Console.CursorVisible = false;
            Console.Title = "Упражнение 7. Поток MemoryStream";
    
            InitializeComponent();
        }
    
        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Очистим консоль перед новым примером
            //Console.Clear();
    
            switch (((ListBox)sender).SelectedIndex + 1)
            {
                case 1: // Пример 1
                    Console.WriteLine("Пример 1");
                    Example1();
                    break;
                case 2: // Пример 2
                    Console.WriteLine("Пример 2");
                    Example2();
                    break;
                case 3: // Пример 3
                    Console.WriteLine("Пример 3");
                    Example3();
                    break;
                case 4: // Пример 4
                    Console.WriteLine("Пример 4");
                    Example4();
                    break;
                case 5: // Пример 5
                    Console.WriteLine("Пример 5");
                    Example5();
                    break;
                case 6: // Пример 6
                    Console.WriteLine("Пример 6");
                    Example6();
                    break;
                case 7: // Пример 7
                    Console.WriteLine("Пример 7");
                    Example7();
                    break;
                case 8: // Пример 8
                    Console.WriteLine("Пример 8");
                    Example8();
                    break;
                case 9: // Пример 9
                    Console.WriteLine("Пример 9");
                    Example9();
                    break;
                case 10: // Пример 10
                    Console.WriteLine("Пример 10");
                    Example10();
                    break;
                case 11: // Пример 11
                    Console.WriteLine("Пример 11");
                    Example11();
                    break;
            }
        }
    
        void Example1()
        {
        }
    
        void Example2()
        {
        }
    
        void Example3()
        {
        }
    
        void Example4()
        {
        }
    
        void Example5()
        {
        }
    
        void Example6()
        {
        }
    
        void Example7()
        {
        }
    
        void Example8()
        {
        }
    
        void Example9()
        {
        }
    
        void Example10()
        {
        }
    
        void Example11()
        {
        }
    
        private void Window_Closed(object sender, EventArgs e)
        {
            // Сохраняем параметры приложения
        }
    }
}
  • Запустите приложение и испытайте работу заготовок вариантов, должно получиться примерно следующее

Конечно, в данном упражнении столько примеров у нас не наберется, но эта заготовка нам еще пригодится для выполнения других упражнений. Теперь можно приступать непосредственно к рассмотрению примеров использования потока MemoryStream, заключая код каждого из них в свою функцию ExampleX().

Пример 1. Из MSDN

Приведем пример из MSDN (http://msdn.microsoft.com/ru-ru/library/system.io.memorystream%28v=VS.90%29.aspx), который иллюстрирует некоторые аспекты применения класса MemoryStream.

  • В файле Window1.xaml измените заголовок списка для ' Пример 1 ' на ' Пример 1. Код из MSDN '
  • Заполните функцию Example1() следующим кодом
void Example1()
        {
            int count;
            byte[] byteArray;// Объявление итогового массива байтов, считанного из потока 
            char[] charArray;// Объявление итогового массива символов, считанного из потока
            UnicodeEncoding unicodeEncoding = new UnicodeEncoding();// Двухбайтный UTF-16
    
            // Генерируем исходные данные
            byte[] bytes1 = // Байтовый массив с допустимыми символами
                unicodeEncoding.GetBytes("Недопустимые символы в пути файла:\r\n");
            byte[] bytes2 = // Байтовый массив с недопустимыми в путях символами
                unicodeEncoding.GetBytes(System.IO.Path.GetInvalidPathChars());
    
            // Создает и открываем поток для чтения/записи с внутренним 
            // байтовым буфером изменяемой длины и нулевой начальной емкостью
            using (MemoryStream memoryStream = new MemoryStream())
            {
                // Пишем в поток допустимые символы блоком 
                memoryStream.Write(bytes1, 0, bytes1.Length);
                // Пишем в поток недопустимые символы побайтно
                count = 0;
                while (count < bytes2.Length)
                {
                    memoryStream.WriteByte(bytes2[count++]);
                }
                // Сбрасываем буфер
                memoryStream.Flush();   // Необязательно, данных немного 
    
                // Печатаем свойства потока на консоль
                Console.WriteLine("\nСвойства потока MemoryStream:");
                Console.WriteLine(
                    "Capacity = {0}, Length = {1}, Position = {2}\n",
                    memoryStream.Capacity.ToString(),  // Длина внутреннего буфера
                    memoryStream.Length.ToString(),    // Количество байт содержимого
                    memoryStream.Position.ToString()   // Текущая позиция указателя
                    );
    
                // Печатаем исходные данные
                Console.WriteLine("Исходные данные перед записью в MemoryStream:");
                Console.Write(unicodeEncoding.GetString(bytes1));   // Здесь Write()
                charArray = new char[unicodeEncoding.GetCharCount(
                    bytes2, 0, bytes2.Length)];
                unicodeEncoding.GetDecoder().GetChars(
                    bytes2, 0, bytes2.Length, charArray, 0);
                Console.WriteLine(charArray);
    
                // Читаем содержимое потока
                byteArray = new byte[memoryStream.Length]; // Создаем массив под все символы
                // Устанавливаем указатель в начало потока
                memoryStream.Seek(0, SeekOrigin.Begin);
                // Читаем блоком первые 20 байт
                count = memoryStream.Read(byteArray, 0, 20);
                // Читаем остальное побайтно, наращивая счетчик 
                while (count < memoryStream.Length)
                {
                    byteArray[count++] =
                        Convert.ToByte(memoryStream.ReadByte());
                }
    
                // Преобразуем батовый массив в символьный и печатаем
                Console.WriteLine("\nДанные, прочитанные из MemoryStream:");
                charArray = new char[unicodeEncoding.GetCharCount(
                    byteArray, 0, count)];
                unicodeEncoding.GetDecoder().GetChars(
                    byteArray, 0, count, charArray, 0);
                Console.WriteLine(charArray);
            }
        }

Приведенный код совсем несложный, подробно прокомментирован и в дальнейших пояснениях вряд ли нуждается.

  • Исполните Пример 1 - должен получиться такой результат

Обратите внимание на звуковой сигнал, который издает компьютер при выводе на консоль недопустимых управляющих символов. Когда для потока памяти не определен внешний неизменяемый байтовый массив, а работает внутренний буфер, то при необходимости его длина увеличивается дискретными блоками по 128 байт, удобными для тактовой обработки в регистрах процессора целиком. Емкость потока, которая автоматически увеличивается должным образом посредством перераспределения, может быть уменьшена с помощью явного задания свойства Capacity.

Пример 2. Создание файловой библиотеки рисунков

Сюжет предыдущего примера состоял в том, что приложение генерировало какую-то информацию, которую мы записывали в некоторый буфер потока MemoryStream, а затем извлекали ее для проверки, выводя на консоль. Более полезным местом применения MemoryStream будет избирательное извлечение информации из хранилища данных (файл или база данных), обработка ее и, либо представление пользователю, либо отправка на дальнейшее хранение.

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

Поскольку объем кода задуманного примера предполагается большим, то вынесем его в отдельный файл частичного класса Window1.

  • В панели Solution Explorer вызовите контекстное меню для узла App7 и командой Add/Class создайте новый файл с именем Example2.cs, заготовку которого заполните так
using System;
using System.Collections.Generic;
using System.Text;
    
// Дополнительные пространства имен
using System.IO;
    
namespace App7
{
    partial class Window1
    {
        void Example2()
        {
            Console.WriteLine("Вынесенный в отдельный файл код функции Example2()");
        }
    }
}
  • Удалите из файла Window1.xaml.cs каркас функции Example2(), поскольку он теперь в другом месте, и запустите приложение - заготовка Примера 2 работает нормально

В этой функции мы создадим код, упаковывающий рисунки в файловую библиотеку, поэтому

  • В файле Window1.xaml измените заголовок списка для ' Пример 2 ' на ' Пример 2. Создание файловой библиотеки '

Прежде всего добавим диалог смены каталога для перехода в папку с рисунками. Для этого воспользуемся библиотечным классом FolderBrowserDialog библиотеки System.Windows.Forms.dll. Поскольку мастер при создании заготовки проекта WPF не подключает ссылку на эту библиотеку автоматически, выполним это вручную.

  • В панели Solution Explorer вызовите контекстное меню для папки App7/References и командой Add Reference подключите к проекту библиотеку System.Windows.Forms.dll

Формат библиотечного файла выберем таким, как показано на рисунке


Он определит и код создания библиотеки, который выполнит задачи в следующей последовательности:

  1. В выбранном каталоге извлечем полные имена файлов рисунков и измерим их количество
  2. Обработаем массив с именами файлов, удалив из них пути и расширения
  3. Отсортируем массив имен файлов для быстрого их поиска в будущей библиотеке
  4. Откроем файловый поток, запишем в него число рисунков ( block1 ) и отсортированные имена файлов рисунков ( block2 )
  5. По отсортированным именам будем считывать рисунки и добавлять их в файловый поток ( block3 ). Одновременно в памяти будем формировать поток с информацией о локализации рисунков в файловом потоке ( block4 )
  6. После завершения записи всех рисунков допишем в поток памяти позицию ( block5 ), с которой в библиотеке начнет располагаться block4, и сбросим этот поток памяти в файловый поток библиотеки

Размещение блока с рисунками ( block3 ) перед информацией о их локализации в библиотеке ( block4 ) обусловлена тем, что рисунки могут быть большими и их удобнее писать сразу в файл. А информацию о их месте расположения в файле постепенно сформировать в потоке памяти и добавить последней. Изложенная схема позволит в будущем быстро определить место нахождения нужного рисунка и извлечь его из библиотеки. Разумеется, подобных схем можно придумать множество, главное, чтобы они взаимно согласовывались при записи и чтении библиотечных данных.

  • В соответствии с вышесказанным модифицируйте файл Example2.cs следующим кодом (код файла приводится полностью, является окончательным и обжалованию не подлежит!)
using System;
using System.Collections.Generic;
using System.Text;
    
// Дополнительные пространства имен
using System.IO;
    
namespace App7
{
    using FORMS = System.Windows.Forms; // Псевдоним
    
    partial class Window1
    {
        FORMS.FolderBrowserDialog dialogFolder = new FORMS.FolderBrowserDialog();
        String nameLibrary = "Pictures.my.lib"; // Будет находиться вместе с рисунками
        String extensionPictures = "jpg";       // Расширения файлов рисунков
    
        void Example2()
        {
            // Диалог смены каталога
            String currentDirectory;    // Полный путь
            dialogFolder.Description = "Выберите каталог, где находятся файлы с рисунками";
            dialogFolder.ShowNewFolderButton = false;// Отключить кнопку создания новой папки
            dialogFolder.RootFolder = Environment.SpecialFolder.MyComputer;// Компьютер
            FORMS.DialogResult result = dialogFolder.ShowDialog();  // Запускаем диалог выбора папки
            // Если был отказ или диалог закрыт системной кнопкой
            if (result == FORMS.DialogResult.Cancel)    
                return;
            currentDirectory = dialogFolder.SelectedPath;// Извлекаем новый каталог
    
            // Принудительная перерисовка окна WPF
            this.Height = this.ActualHeight + 1;
            this.Height = this.ActualHeight - 1;
    
            // Читаем полные имена всех файлов текущего каталога по заданному шаблону
            String[] files = Directory.GetFiles(currentDirectory, "*." + extensionPictures);
            if (files.Length == 0)
            {
                FORMS.MessageBox.Show(
                    "Нет в выбранном каталоге файлов для упаковки",
                    "Предупреждение",
                    FORMS.MessageBoxButtons.OK,
                    FORMS.MessageBoxIcon.Information);
                return;
            }
    
            // Получаем сокращенные имена файлов без пути и расширения
            for (int i = 0; i < files.Length; i++)
            {
                String str = files[i].Substring(files[i].LastIndexOf('\\') + 1);
                int len = str.LastIndexOf('.');
                files[i] = str.Substring(0, len);
            }
            // Сортируем массив для красоты и порядка
            Array.Sort<String>(files);
            // Array.Sort<String>(files, new ComparerNames());//Альтернативный компаратор!!!!!!    
            // Создаем поток MemoryStream переменной длины для имен файлов
            MemoryStream memoryStream = new MemoryStream();
            if (!memoryStream.CanWrite) // Проверять необязательно!
            {
                FORMS.MessageBox.Show("Не могу писать в MemoryStream");
                return;
            }
            // Создаем писатель-оболочку для записи в MemoryStream элементарных типов
            BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin); // Необязательно!
            // Первым записываем block1 для количества файлов
            binaryWriter.Write((Int32)files.Length);// Приводить тип необязательно, распознает сама 
            //!!!=4 Console.WriteLine(binaryWriter.BaseStream.Position);
            // Закачиваем все имена файлов (block2) из отсортированного массива
            for (int i = 0; i < files.Length; i++)
                binaryWriter.Write((String)files[i]);// Приводить тип необязательно, распознает сама
            memoryStream.Flush();   // Сливаем буфер
    
            // Создаем файловый поток и сбрасываем в него накопленное
            FileStream fs = null;
            try
            {
                // Создаем или усекаем файл в том же каталоге
                fs = new FileStream(
                    Path.Combine(currentDirectory, nameLibrary), FileMode.Create);
                memoryStream.WriteTo(fs);// Сбрасываем все из памяти в файл
    
                fs.Flush(); // Слить кэш (необязательно!)
            }
            catch
            {
                FORMS.MessageBox.Show("Не могу записать в файл " + nameLibrary);
                fs.Close();
                return;
            }
            // Закрываем поток-оболочку, которая закроет и базовый поток
            binaryWriter.Close();
    
            // Создаем поток памяти для записи block4 и block5
            MemoryStream dimension = new MemoryStream(
                files.Length * (sizeof(long) + sizeof(int)) 
                + sizeof(long));
            dimension.Seek(0, SeekOrigin.Begin);    // Необязательно!
            // Создаем поток-оболочку для записи пар "позиция-длина" в MemoryStream
            BinaryWriter dimensionWriter = new BinaryWriter(dimension);
    
            // Читаем отдельные рисунки и пишем сразу в библиотеку
            MemoryStream pictureStream;
            for (int i = 0; i < files.Length; i++)
            {
                // Читаем файл с рисунком
                byte[] bytes = File.ReadAllBytes(Path.Combine(
                    currentDirectory, files[i] + "." + extensionPictures));
    
                // Формируем в памяти block4 для локализации рисунков
                dimensionWriter.Write(fs.Position); // Позиция рисунка long в потоке
                dimensionWriter.Write(bytes.Length);// Длина рисунка int
    
                // Добавляем очередной рисунок в библиотеку (block3)
                pictureStream = new MemoryStream(bytes);// Создаем на базе массива
                pictureStream.WriteTo(fs);   // Добавляем рисунок в библиотечный файл
                pictureStream.Close();
            }
            fs.Flush(); // Сбрасываем буфер
    
            dimensionWriter.Write(fs.Position); // Дописываем в память block5
            dimensionWriter.Flush();
    
            // Дописываем в библиотеку последними block4 и block5
            dimension.WriteTo(fs);
            dimensionWriter.Close();    // Надстройка закрывает и базовый поток
    
            fs.Close(); // При закрытии автоматически выполнится и Flush()
    
            // Оповещаем пользователя
            FORMS.MessageBox.Show("Библиотека " + nameLibrary +
                " создана в каталоге:\n" + currentDirectory);
        }
    }
    
    // Альтернативный компаратор!!!!!!! 
    /**************************************************
    class ComparerNames : IComparer<String>
    {
        public int Compare(String x, String y)
        {
            int X, Y, XX, YY;
            int posX = x.IndexOf('_');
            string str1 = posX != -1 ? x.Substring(0, posX) : "";
            bool isNumber1 = int.TryParse(str1, out X);
    
            int posY = y.IndexOf('_');
            string str2 = posY != -1 ? y.Substring(0, posY) : "";
            bool isNumber2 = int.TryParse(str2, out Y);
    
            if (!isNumber1 || !isNumber2)
                return x.CompareTo(y);
    
            if (X < Y)
                return -1;
            else if (X == Y)
            {
                str1 = x.LastIndexOf('d') != -1 ?
                    x.Substring(0, x.Length - 1) : x;
                str1 = str1.Substring(posX + 1);
                isNumber1 = int.TryParse(str1, out XX);
    
                str2 = y.LastIndexOf('d') != -1 ?
                    y.Substring(0, y.Length - 1) : y;
                str2 = str2.Substring(posY + 1);
                isNumber2 = int.TryParse(str2, out YY);
    
                if (!isNumber1 || !isNumber2)
                    return str1.CompareTo(str2);
    
                if (XX < YY)
                    return -1;
                else if (XX == YY)
                    if (x.LastIndexOf('d') != -1 && y.LastIndexOf('d') != -1)
                        return 0;
                    else if (x.LastIndexOf('d') != -1)
                        return 1;
                    else
                        return -1;
                else
                    return 1;
            }
            else
                return 1;
        }
    }
    //************************************************/
}

Для сохранения параметров диалогового окна FolderBrowserDialog выполним следующее:

  • В файле Window1.xaml.cs добавьте код, выделенный в листинге
public partial class Window1 : Window
    {
        // Объект для сохранения параметров приложения
        Properties.Settings settings = new App7.Properties.Settings();
    
        public Window1()
        {
            // Настройка консоли
            Console.ForegroundColor= ConsoleColor.White;
            Console.CursorVisible = false;
            Console.Title = "Упражнение 7. Поток MemoryStream";
    
            InitializeComponent();
    
            // Читаем параметры, сохраненные ранее
            dialogFolder.SelectedPath = settings.dialogFolder;
        }
    
        .......................................................
    
        private void Window_Closed(object sender, EventArgs e)
        {
            // Сохраняем параметры приложения
            settings.dialogFolder = dialogFolder.SelectedPath;
            settings.Save();
        }
    }
  • В панели Solution Explorer для проекта App7 раскройте узел Properties и двойным щелчком на файле Settings.settings откройте окно установки параметров , которое заполните как показано на рисунке

Обратите внимание, что оболочка добавила к проекту файл конфигурации app.config, без которого механизм сохранения параметров работать не будет.

  • Запустите приложение, выберите в прилагаемом к работе каталоге Source подкаталог Images с рисунками *.jpg и сверните их в библиотеку Pictures.my.lib, которая разместится в том же подкаталоге
  • Разберитесь с приведенным кодом файла Example2.cs, который содержит подробные комментарии
< Лекция 5 || Лекция 6: 12345678910111213
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Dmitriy Ivanchenko
Dmitriy Ivanchenko
Украина, Кировоград, Виктория-П, 2011
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974