главная продукты downloads форум тех. поддержка

 

Главная страница
 

  www.bousoft.com

   главная страница
   новости СМИ
   ссылки
   о проекте
   добавить в Избранное
   сделать стартовой
 

   наши разработки

   shareware
   freeware
   регистрация
 

справка и FAQ   

   статьи
   книги
   FAQ: WinForms
   FAQ: GDI+
   MS Office
   web-технологии
   wap-технологии
 

   программирование

   visual basic: ActiveX
   visual basic: примеры
   .net framework
   c++
   delphi
   rapidQ
 

   наши компакт-диски

   диск BouSoft #3
   диск Windows PE

Запись CD с данными используя IMAPI

 

Скачать к этой статье: IMAPI Type Library, Source.

 

Эта статья демонстрирует возможность записи CD с данными (формат Joliet) с использованием интерфейса IMAPI.

 

Иллюстрация:

 

 

Запись дата-CD.

 

Перед началом работы обратите внимание, что пример из этой статьи использует реализацию IMAPI Wrapper. Для запуска примера у Вас должна быть зарегистрирована библиотека acclImapiWrapper.DLL в Глобальном кэше сборки (GAC). Если вы расположите эту библиотеку в папке Debug или Release Вашего проекта, Вы также сможете запустить пример, но, возможно, понадобится изменить ссылки (references) проекта.

 

Запись дата-CD с использованием функций IMAPI состоит из следующих шагов:

  1. Инициализация библиотеки путем получения экземпляра класса DiscMaster, установка режима сборки в Joliet путем получения экземпляра JolietDiscMaster для сборки исходного образа, который будет записан на диск, и установки активного привода.

  2. Построение структуры JolietDiscMasterStorage, в которой представлены файлы, которые вы хотите записать на диск, и имена этих файлов на диске.

  3. Вызов JolietDiscMaster для копирования файлов из структуры JolietDiscMasterStorage в файл образа диска, и затем запись этого образа на диск.

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

 

Перечисленные выше шаги далее будут описаны на языке VB.NET. Программа на языке C# очень похожа, поэтому подробно на ней останавливаться не будем.

 

1. Инициализация библиотеки.

 

Единственный напрямую доступный и инициализируемый класс в IMAPI Wrapper - это объект DiscMaster, из которого могут быть получены все другие классы. Этот объект должен описываться в VB как WithEvents, так как мы хотим отлавливать сообщения о процессе записи и посылать ему сообщения о прерывании записи. Далее, режим записи задается путем инициализации экземпляра объекта JolietDiscMaster. Ссылка на этот экземпляр должна быть сохранена, так как она потребуется для организации образа диска (повторный вызов JolietDiscMaster() приведет к получению нового экземпляра, что в данном случае недопустимо). После выполнения всего выше перечисленного список записывающих приводов системы помещается в комбо-бокс и определяется индекс выбранного пользователем привода.

 

    Private WithEvents discMaster As discMaster = Nothing
    Private jolietDiscMaster As JolietDiscMaster = Nothing


    Private Sub GetRecorders()
        Dim ex As Exception
        Try
            discMaster = New DiscMaster()
            jolietDiscMaster = discMaster.JolietDiscMaster()
            Dim recorder As DiscRecorder
            For Each recorder In discMaster.DiscRecorders
                cboRecorder.Items.Add( _
                 New ComboRecorderWrapper(recorder))
            Next

            If (cboRecorder.Items.Count > 0) Then
                cboRecorder.SelectedIndex = 0
            End If
        Catch ex
            MessageBox.Show( _
                Me, String.Format("Unable to initialise the IMAPI library {0}", ex), _
                Text, MessageBoxButtons.OK, MessageBoxIcon.Stop)
            If Not (discMaster Is Nothing) Then
                discMaster.Dispose()
            End If
        End Try
    End Sub

    Private Sub cboRecorder_SelectedIndexChanged( _
            ByVal sender As Object, _
            ByVal e As System.EventArgs _
        ) Handles cboRecorder.SelectedIndexChanged
        If (cboRecorder.SelectedIndex > -1) Then
            Dim wrapper As ComboRecorderWrapper = cboRecorder.SelectedItem
            discMaster.DiscRecorders.ActiveDiscRecorder = wrapper.DiscRecorder
        End If
    End Sub

 

Как отмечалось ранее (в других статьях - Б.С.), вы должны быть уверены, что вызываете метод Dispose объекта DiscMaster до завершения вашего приложения. Невыполнение этого условия приведет к тому, что все последующие обращения любых программ к интерфейсу IMAPI будут заканчиваться ошибкой "Stash In Use". Для решения этой проблемы я добавил событие Application ThreadException, которое выполняется при возникновении любой неотслеживаемой ошибки или при экстренном завершении работы приложения.

 

    Public Sub New()

	' ... 

        'Add any initialization after the InitializeComponent() call
        AddHandler Application.ThreadException, 
            AddressOf application_ThreadException      

        Show()
        Refresh()

        GetRecorders()

    End Sub

    Private Sub application_ThreadException( _
        ByVal sender As Object, _
        ByVal e As ThreadExceptionEventArgs)
        MessageBox.Show(Me, _
           String.Format("An untrapped exception occurred: {0}.", _
             e.Exception), _
             Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
        Close()
    End Sub

    Protected Overrides Sub OnClosing( _
        ByVal e As System.ComponentModel.CancelEventArgs)

        ' ....

        '// Clear up:
        MyBase.OnClosing(e)
        Cursor.Current = Cursors.WaitCursor
        sbrMain.Text = "Closing IMAPI interface..."
        jolietDiscMaster.Dispose()
        discMaster.Dispose()
        Cursor.Current = Cursors.Default

    End Sub

 

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

 

2. Построение иерархии JolietDiscMasterStorage.

 

Объект JolietDiscMaster имеет свойство RootStorage, которое возвращает экземпляр объекта JolietDiscMasterStorage, который представляет собой корневую директорию CD-диска. Вы можете добавлять файлы, а также поддиректории к этому объекту; поддиректории также представляются в виде экземпляров объекта JolietDiscMasterStorage. При добавлении файла вам нужно указать путь к файлу, который будет скопирован на диск, и имя файла, под которым он будет записан на диске. У подкаталогов есть только имена. В данном примере я просто задаю папку для добавления, причем возможно рекурсивное добавление, то есть добавление всех подпапок.

 

   ''' <summary>
    ''' Adds a directory to the storage, optionally including
    ''' any subdirectories.
    ''' </summary>
    ''' <param name="path">Path to the directory to add</param>
    ''' <param name="recurse"><c>true</c> to include subdirectories,
    ''' <c>false</c> otherwise.</param>
    Public Sub AddDirectory(ByVal path As String, ByVal recurse As Boolean)
        Dim storage As JolietDiscMasterStorage = jolietDiscMaster.RootStorage
        AddFilesToStorage(storage, path, recurse)
    End Sub

    Private Sub AddFilesToStorage( _
        ByVal storage As JolietDiscMasterStorage, _
        ByVal startPath As String, _
        ByVal recurse As Boolean)

        If (recurse) Then
            Dim dir As String
            For Each dir In Directory.GetDirectories(startPath)
                Dim subStorage As JolietDiscMasterStorage = _
                    storage.CreateSubFolder(Path.GetFileName(dir))
                AddFilesToStorage(subStorage, dir, recurse)           
            Next
        End If

        Dim file As String
        For Each file In Directory.GetFiles(startPath)
            storage.AddFile(file, Path.GetFileName(file))
        Next
    End Sub

 

3. Запись диска.

 

После того, как выполнены все предыдущие шаги, дальнейший процесс создания диска достаточно прост: во-первых нужно удалить старое содержимое образа (если оно имеется); затем добавить данные, используя метод AddData объекта JolietDiscMaster, и, наконец, записать диск используя метод DiscMasterRecordDisc:

 

    ''' <summary>
    ''' Creates a data CD from the specified files
    ''' </summary>
    ''' <param name="simulate">Simulate CD burning</param>
    ''' <param name="ejectWhenComplete"><c>true</c> to eject the CD
    ''' tray when the burn is complete, <c>false</c> otherwise</param>
    ''' <param name="overwrite"><c>true</c> to overwrite existing files
    ''' on CDRW media, <c>false</c> otherwise</param>
    Public Sub CreateCD( _
              ByVal simulate As Boolean, _
              ByVal ejectWhenComplete As Boolean, _
              ByVal overwrite As Boolean
        )

        '// Ensure we don't have anything in the stage
        discMaster.ClearFormatContent()

        '// Stage the content
        jolietDiscMaster.AddData(overwrite)

        '// burn the disc
        discMaster.RecordDisc(simulate, ejectWhenComplete)

        '// Easy!
    End Sub

 

В процессе записи диска могут возникать следующие 6 событий:

  • AddProgress - вызывается при добавлении файлов в образ;

  • PreparingBurn - возникает один раз, когда создание образа завершено, и диск готов к записи;

  • BlockProgress - возникает по окончании записи каждого блока данных (2048 байтов) на диск;

  • ClosingDisk - возникает единственный раз, когда запись образа на диск завершена и диск (или сессия) готов к закрытию;

  • Complete - запись завершена;

  • QueryCancel - вызывается при создании образа или в процессе записи диска. Установка параметра Cancel в TRUE приведет к отмене или прерыванию операции.

Вот и все, что вам действительно необходимо для создания диска. Однако, если вы захотите добавить в свое приложение возможность прерывания записи или отображение хода операции, вам необходимо переписать программу так, чтобы процесс записи осуществлялся в параллельном фоновом потоке. Конструкция IMAPI Wrapper позволяет выполнить это достаточно просто, используя метод BeginInvoke.

 

Использование BeginInvoke для повышения удобства использования приложения.

 

Запись компакт-диска - это довольно длительная операция. Если вы будете вызывать процесс записи из основного потока программы, то в процессе записи пользователю будет казаться, что ваша программа зависла (блокирована). Кроме того, в системах Windows XP и выше введена функция, которая позволяет "заморозить" заголовок и границы окна, если система определит, что приложение блокировано достаточно продолжительное время. В приложениях для платформы .NET после этого любые вызовы для обновления элементов управления в окне приложения не будут иметь эффекта.

 

Таким образом, нам требуется способ выделения записи диска в отдельный фоновый поток. Так как интерфейс IMAPI Wrapper обеспечивает вызов события для определения, был ли прерван процесс записи и отображение процесса записи, то это также является простым способом прерывания процесса в любой момент.

 

Любой метод класса в .NET может быть вызван в асинхронном режиме путем создания делегата (delegate) для него. Как только вы это сделаете, компилятор готов для автоматического создания трех методов для делегата:

  • Метод Invoke. который имеет те же параметры и возвращаемое значение, что и сам делегат, и позволяет вызвать метод в синхронном режиме.

  • Метод BeginInvoke, который в целом аналогичен Invoke, за исключением того, что он добавляет еще два параметра: делегат AsyncCallback и объект для хранения состояния.

  • Метод EndInvoke, который возвращает то же значение, что и делегат, а все параметры ref и out, а также внешние параметры сохраняются в объекте IAsyncResult.

Все это делает асинхронное обращение достаточно простым. Оно состоит из 4 шагов:

  • Объявление делегата для метода, который вам необходимо вызвать асинхронно.

  • Написать полный обработчик события для вызываемого метода, который будет вызван по окончании работы метода.

  • Создать обработчик AsyncCallback и подсоединить его к созданному на предыдущем этапе обработчику события.

  • Вызвать метод-делегат асинхронно, используя метод BeginInvoke.

Далее приведен код на VB и C#, который выполняет перечисленные этапы. Неудивительно, что этот код очень прост; единственное отличие программы на VB заключается в использовании ключевого слова AddressOf для обращения к делегатам. В данном случае метод, который я хочу вызвать - это метод CreateCD, который включен в мой класс DataCDCreator. Этот метод получает три булевских параметра (для имитации записи, извлечения диска по окончании записи и перезапись), и не возвращает никаких значений:

 

Код на VB для асинхронного обращения

 

''' <summary>
''' Delegate representing the CreateCD method
''' </summary>
Public Delegate Sub CreateCDDelegate( _
    ByVal simulate As Boolean, _
    ByVal ejectWhenComplete As Boolean, _
    ByVal overwrite As Boolean)
    Private creator As DataCDCreator = Nothing
    Private createCDHandler As CreateCDDelegate = Nothing

        ' Asynchronously invoke the creator
        creator = New DataCDCreator(discMaster, jolietDiscMaster)
        creator.AddDirectory(txtFolder.Text, True)

        createCDHandler = AddressOf creator.CreateCD
        Dim callback As AsyncCallback = AddressOf createCD_Complete
        Dim ar As IAsyncResult = createCDHandler.BeginInvoke( _
            simulate, ejectWhenComplete, overwrite, callback, Nothing)


    ''' <summary>
    ''' Called when CD creation completes
    ''' </summary>
    ''' <param name="ar">Result of method call (none)</param>
    Private Sub createCD_Complete(ByVal ar As IAsyncResult)
        ' NB should surround with try - catch - end try
        SetApplicationMode(False)
        createCDHandler.EndInvoke(ar)
    End Sub

 

Код на C# для асинхронного обращения

 

   /// <summary>
   /// Delegate representing the CreateCD method
   /// </summary>
   public delegate void CreateCDDelegate(
       bool simulate, 
       bool ejectWhenComplete, 
       bool overwrite);
      private DataCDCreator creator = null;
      private CreateCDDelegate createCDHandler = null;


        // Asynchronously invoke the creator
        creator = new DataCDCreator(discMaster, jolietDiscMaster);
        creator.AddDirectory(txtFolder.Text, true);
               
        createCDHandler = new CreateCDDelegate(creator.CreateCD);
        AsyncCallback callback = new AsyncCallback(createCD_Complete);
        IAsyncResult ar = createCDHandler.BeginInvoke(
            simulate, ejectWhenComplete, overwrite, callback, null);


      /// <summary>
      /// Called when CD creation completes
      /// </summary>
      /// <param name="ar">Result of method call (none)</param>
      private void createCD_Complete(IAsyncResult ar)
      {
         // NB should surround with try-catch
         SetApplicationMode(false);
         createCDHandler.EndInvoke(ar);
      }

 

Идеи по улучшению приложения.

 

Приложение, описанное в данной статье, очень простое, и все, что оно позволяет сделать - это скопировать существующий каталог на CD. Однако это не является серьезным ограничением, так как когда вы создаете хранилище JolietDiscMasterStorage, вы можете указать файлы, находящиеся в других папках или по-другому расположить их на диске. Более полное приложение должно позволить пользователю перетащить файлы из любого места на диск и затем переименовать их если это необходимо.

 

   Перевод: Буторкин Сергей  Источник: vbAccelerator.com.

 

e-mail:

 

bousoft@mail.ru