Click here to Skip to main content
Click here to Skip to main content

How to Burn Optical File Images with IMAPI 2.0 from Managed Code

, 30 May 2008
Rate this:
Please Sign up or sign in to vote.
Burn ISO files using IMAPI v2.0 and C#
burn

Introduction

If you've seen my previous article, How to Create Optical File Images using IMAPIv2.0, you might expect me to do something with that file image, like... burn it. It is the same Image Mastering API v2.0 I'm using to achieve this goal, so you might want to install it before running this application. There might be other burning engines available, but I've chosen to use this API because it is likely to have long term support from Microsoft. Using COM Interop to wrap the native components has some quirks I've tried to fix, but it is not very difficult, thanks to the samples from Microsoft. If you were interested only in burning folders and files on to media and skip the file image creation, you will be better served by Eric Haddan's great article. I recommend you check the articles mentioned above should you need a better understanding of the inner workings or COM Interop issues. I've made only minor changes to the code from my previous article, and I also got some cues from the sources I have mentioned below.

Background

If you are wondering why you would need to burn an ISO image when you could cold burn the files directly, then you should be aware that you cannot easily create every kind of format using IMAPI 2.0. However, there is no problem in burning a file stream if the recorder supports the media type. In other words, if you have an ISO file containing a DVD movie, you will be able to burn it, but you can't create a proper DVD disk even if you copy all the files from the media. While you might be able to play the latter on your computer and a few new players, most DVD players won't play it, because they expect a customized UDF format. This is the case when burning an existing ISO file image created with other tools overcomes this issue.

Burning a File Optical Image

Burning an optical image boils down to writing a stream on your media. The code for writing and formatting is similar to the code in Eric's article, with a few changes. Getting the stream interface is done using the ImageRepository.LoadCOMStream that was described in my previous article. I've packaged most of the burning capability in a helper class called BurnHelper, and below is its WriteStream method:

public int WriteStream(System.Runtime.InteropServices.ComTypes.IStream stream, 
                       string initburner, string clientName, 
                       bool forceMediaToBeClosed, int speed, bool eject)
{
    MsftDiscRecorder2 discRecorder = null;
    MsftDiscFormat2Data discFormat = null;
    int result = -1;
    try
    {
        discRecorder = new MsftDiscRecorder2();
        discRecorder.InitializeDiscRecorder(initburner);
        discFormat = new MsftDiscFormat2Data();
        //remove the comment for next 2 lines
        discFormat.Recorder = discRecorder;
        discRecorder.AcquireExclusiveAccess(true, clientName);

        // initialize the IDiscFormat2Data
        discFormat.ClientName = clientName;
        discFormat.ForceMediaToBeClosed = forceMediaToBeClosed;

        // add the Update event handler
        discFormat.Update += DiscFormatData_Update;

        //this is how it worked for my burner
        //speed = 0 => minimum speed descriptor in update
        // 0 < speed < minimum speed descriptor => half of minimum
        //             speed descriptor in update
        // minimum speed descriptor <= speed < next speed
        //         descriptor => minimum speed descriptor in update
        // next speed descriptor <= speed  => next speed descriptor in update
        discFormat.SetWriteSpeed(speed, true);

        //write the stream
        discFormat.Write(stream);
        if (_backgroundWorker.CancellationPending)
        {
            return 1;
        }
        if (eject)
        {
            //wait to flush all the content on the media
            IMAPI_FORMAT2_DATA_MEDIA_STATE state = 
              IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_UNKNOWN;
            while (state == IMAPI_FORMAT2_DATA_MEDIA_STATE.
                            IMAPI_FORMAT2_DATA_MEDIA_STATE_UNKNOWN && 
                            !_backgroundWorker.CancellationPending)
            {
                try
                {
                    state = discFormat.CurrentMediaStatus;
                }
                catch (Exception)
                {

                    state = IMAPI_FORMAT2_DATA_MEDIA_STATE.
                            IMAPI_FORMAT2_DATA_MEDIA_STATE_UNKNOWN;
                    System.Threading.Thread.Sleep(3000);
                }
            }
            if (!_backgroundWorker.CancellationPending)
                discRecorder.EjectMedia();
        }
        result = 0;
    }
    finally
    {
        if (_backgroundWorker.CancellationPending)
            result = 1;
        if (discFormat != null)
        {
            // remove the Update event handler
            //
            discFormat.Update -= DiscFormatData_Update;
            Marshal.FinalReleaseComObject(discFormat);
        }
        if (discRecorder != null)
        {
            //discRecorder.EnableMcn();
            discRecorder.ReleaseExclusiveAccess();
            Marshal.FinalReleaseComObject(discRecorder);
        }
    }
    return result;
}

If you see my code comments relative to Write Speed, you can notice that there might be speeds that are not shown as descriptors by IMAPI. You might be able to discover them by using other numbers than the ones provided by the descriptors that will show up in the progress update UI. I also had to delay the media being ejected until everything was flushed on the media, just to avoid a crash.

Detecting Media Changes

Synchronizing the media with the UI should not be taken for granted. Some systems might have the media notification turned off by Active Directory Policy or registry settings. I've originally tried using WMI to detect these events, but I did not get any notification when inserting blank media. Hence, I ended up using the old fashioned WM_DEVICECHANGE Windows message to get the media changes. There is quite a bit of code to achieve this in the DeviceMonitor class, which was based on this article as the starting point. Below is the most important method of this class:

protected override void WndProc(ref Message m)
{
    // There has been a change in the devices
    const int WM_DEVICECHANGE = 0x0219;
    switch (m.Msg)
    {
        case WM_DEVICECHANGE:
            {
                
                if (m.LParam == IntPtr.Zero)
                    break;
                DEV_BROADCAST_VOLUME vol = (DEV_BROADCAST_VOLUME)
                  Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));

                if ((vol.dbcv_devicetype == _dt) && (vol.dbcv_flags == _vf))
                    PostMessage(new HandleRef(this, Handle), 
                                POST_MSG_DEVICECHANGE, m.WParam, 
                                (IntPtr)vol.dbcv_unitmask);
            }
            break;
        default:
            if (POST_MSG_DEVICECHANGE == m.Msg)
            {
                DeviceEventBroadcast ev = (DeviceEventBroadcast)m.WParam.ToInt32();
                List<char> drives = DriveNames((uint)m.LParam);
                List<char>notifyDrives = new List<char>(1);
                _drives.ForEach(delegate(char drv)
                {
                    if (drives.Contains(drv)) notifyDrives.Add(drv);
                });
                if (DeviceAction != null && notifyDrives.Count > 0)
                    DeviceAction(notifyDrives, ev);
            }
            break;
    }
    base.WndProc(ref m);
}

If you are wondering why I needed to repost the original WM_DEVICECHANGE to POST_MSG_DEVICECHANGE, it is because WM_DEVICECHANGE occurs before IMAPI becomes aware of the media change, and would crash otherwise.

Other Features

Unlike the previous version, I can't use the only UI thread by pumping messages with Application.DoEvents because the IMAPI events are too scarce for a smooth user experience, so I had to use the BackgroundWorker for burning. You have the option to format RW media, and sometimes even to force formatting unrecognizable media, a simple feature I've been unable to find in other burning programs. You'll also find many new features and enhancements when compared to my previous article. I have to mention that rebooting, sleeping, and the like are based on the mentalis code, and the making of the media bootable by adding a file has a bug I could not fix yet. If you want to take a stab at it, the code is in the ImageRepositoty.CreateBootDisc method, so feel free to tinker with it. I tried both IMAPI2FS.FsiStream and COMTypes.IStream as arguments, but to no avail. If you find a way around, please let me know.

Adding a boot image option is done by checking the checkbox on the ‘Select Files’ tab of the application, as shown in the image below:

Files and Folders

How to Use It

I did not test it extensively because of my limited environment, so what worked on my system might not work on yours. Creating an optical file image is the same as in my previous article sans the optional bootable option. Burning a file image from the ‘Build Image' tab should be quite easy, you have to select the image and burner, optionally the speed, eject, and close the media, and hit the 'Burn Image' button. You should be able to cancel the operation if you wish, but you might damage the media if it is not RW.

Reusing the Code

The solution has two projects, to separate the UI from the classes that actually do the image creation and burning. You can easily reuse this functionality if you add a reference to the ISOImage.dll or to the OpticalImage project that comes in two flavours for Visual Studio 2005 and 2008. You must install the Image Mastering API v2.0 if you are not running on Vista, to use it.

History

  • 16th May, 2008: Initial version
  • 29th May, 2008: Version 2.0.0.1 - Fixed some UI and VolumeName bugs

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

dmihailescu
Software Developer (Senior)
United States United States
Decebal Mihailescu is a software engineer with interest in .Net, C# and C++.

Comments and Discussions

 
QuestionFolder repeated in ISO create from Folder. PinmemberFabiancito More Barrios28-Feb-14 1:04 
QuestionIncremental Iso image PinmemberKhayralla29-Aug-13 6:37 
QuestionSet VolumeName before burn? PinmemberTrency20-Jul-11 6:30 
QuestionDiscFormatData.update event handler does not work PinmemberJardel29-Nov-10 6:25 
AnswerRe: DiscFormatData.update event handler does not work Pinmemberpcbbc9-Dec-10 3:48 
GeneralThanks! PinmemberCrimsonAbundance13-Aug-10 4:46 
GeneralC++ source codes Pinmemberpodeeshk1-Apr-10 20:33 
GeneralE_IMAPI_RECORDER_MEDIA_BUSY Error Pinmemberjaredharley16-Jan-10 15:37 
GeneralThis code appears to be (partially) broken for the backup of non-root folders. PinmemberMember 2424732-Jan-10 3:03 
GeneralRe: This code appears to be (partially) broken for the backup of non-root folders. Pinmemberdmihailescu4-Jan-10 5:56 
Generalopen source in c++ Pinmembersriman_vema5-Dec-09 1:20 
QuestionHow to add files to the Existing Iso Image Pinmemberpoojary07315-Dec-09 1:06 
GeneralTwo problems PinmemberHasterson28-Aug-09 22:08 
QuestionHow do you create an ISO from a Disk? Pinmemberyarborg31-Dec-08 4:13 
QuestionISO of audio cd Pinmembergiorgiodipietro2-Dec-08 5:11 
Questionmultiple folder occurence in source folders: Internal error occured: Removing key from data structure. PinmemberViRiX6-Nov-08 12:30 
AnswerRe: multiple folder occurence in source folders: Internal error occured: Removing key from data structure. Pinmemberdmihailescu6-Nov-08 13:13 
GeneralThanks PinmemberMuhammed Sahin30-May-08 23:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 30 May 2008
Article Copyright 2008 by dmihailescu
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid