Scripting CDs and DVDs

Burning CDs and DVDs

In our last episode, Clair had just told Jeremy she was pregnant with Elizabeth’s baby, while Ken was about to ask Agnes to marry him but accidentally asked her mother, Delta, instead….Oops, wrong show. In ourlast episode we showed you how to connect to a CD or DVD device and read some properties. What do you mean, you’d rather hear what happened with Agnes? All right, so maybe our last episode wasn’t all that engrossing. But hey, we also showed you how to open and close the device door, and that has to count for something, right?

Okay, fine. When Ken discovered his mistake, it was too late: Agnes’ mother (Delta) had accepted his proposal. But it was a selfless act – Delta hated Ken and knew he was all wrong for Agnes. Delta also knew that she was suffering from a brain tumor and only had three months to live ,so her marriage to Ken would be short, especially since it takes a good six months to plan a wedding …..

Hey, give us another shot. Maybe opening and closing doors isn’t all that exciting, but how about burning CDs and DVDs? Even better, how about burning CDs and DVDs using a script? All right, it’s still not as fascinating as how in the world Clair can have Elizabeth’s baby, but it’s still pretty interesting.

Note: In case you missed our last episode, we’ll mention once again that we’re using IMAPI2 to create these scripts. The IMAPI2 API is available with Windows Vista and Windows Server 2008 (currently in Beta). It’s also available as a download for Windows XP and Windows Server 2003:

Image Mastering API v2.0 for Windows XP

Image Mastering API v2.0 for Windows Sever 2003

Image Mastering API v2.0 for Windows XP x64 Edition

Image Mastering API v2.0 for Windows Server 2003 x64 Edition

*
On This Page
Burning Data to a DiscBurning Data to a Disc
Burning ISO ImagesBurning ISO Images
Erasing a DiscErasing a Disc
Every Good Story Line Needs Some ConflictEvery Good Story Line Needs Some Conflict
And They All Lived Happily Ever AfterAnd They All Lived Happily Ever After

Burning Data to a Disc

Believe it or not, burning data to disc is a pretty involved story. You have to create six different objects and set all sorts of properties. But even though it’s a little complicated, don’t worry – we’ll walk you through it all and hope we wind up with a happy ending. For this script we’re assuming that you have a folder that contains all the files you want to burn to a CD or DVD. You could modify the script to pull content from different places, but it’s easier to get everything in one place first. Here’s the script:

Const FsiFileSystemISO9660 = 1
    
index = 1
strPath = "c:\writecd"

Set colDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")
Id = colDiscMaster.Item(index)

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder(Id)

Set objDataWriter = CreateObject("IMAPI2.MsftDiscFormat2Data")
objDataWriter.Recorder = objRecorder
objDatawriter.ClientName = "BurnTest"

Set objFSI = CreateObject("IMAPI2FS.MsftFileSystemImage")

objFSI.FileSystemsToCreate = FsiFileSystemISO9660
Set objDir = objFSI.Root
objDir.AddTree strPath, False
        
WScript.Echo "Writing to disc..."
Set objResult = objFSI.CreateResultImage()
objStream = objResult.ImageStream

objDataWriter.Write(objStream)

WScript.Echo "Done"

We start the script by declaring a constant:

Const FsiFileSystemISO9660 = 1

We’ll use this constant a little later to identify the type of image we’re burning (more on that later).

Next we initialize a couple of variables:

index = 1
strPath = "c:\writecd"

The first variable, index, is used to identify the device we’re going to use to burn our image. If you remember our previous article, we were able to iterate through all the disc devices connected to our computer. These devices are actually indexed, starting with zero. So if you read a list of devices, the first device returned will be index 0, the next will be index 1, and so on. Remember the output on our test machine:

Vendor: SAMSUNG
Product ID: CD-ROM SC-148A
Product Revision: B403
Mount Point: D:\
CD-ROM

Vendor: PLEXTOR
Product ID: DVDR   PX-740A
Product Revision: 1.01
Mount Point: E:\
DVD+RW
DVD-ROM
CD-R
CD-ROM

As you can see, two devices were returned. The first is a CD-ROM drive, meaning we can only read CDs, we can’t burn to this device; this is the device at index 0. The second, the Plextor device, is a CD-R/DVD+RW drive, meaning we can burn CDs and DVDs from this device; this device is at index 1. So in our script that burns a CD, we set the index variable to 1, indicating that we want to use the device at index 1 to burn our CD.

Note: Keep in mind that index numbers can change as you add and remove plug-and-play devices. It might be a good idea to combine the script in Part 1 that finds the devices with this script that writes to them.

The second variable, strPath, is simply the path to the files we want to burn to the CD. As with all our IMAPI2 scripts, we start out by creating an MsftDiscMaster2 object, which contains a list of all the device IDs of our disc devices:

Set colDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")

In our previous script, this was where we put in a For Each loop to loop through all the devices. But this time we don’t need to do that.We know which device we want to use: the device at index 1. So instead of a For Each loop we simply retrieve the ID from the device at index position 1. We do this by passing the index to the Item method of the MsftDiscMaster2 object:

Id = colDiscMaster.Item(index)

Now we can create our MsftDiscRecorder2 object and initialize it (by calling the InitializeDiscRecorder method), using the ID we just retrieved:

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder(Id)

We now have an object, objRecorder, that references our CD or DVD burner. That means that it’s time to create yet another object, the MsftDiscFormat2Data object:

Set objDataWriter = CreateObject("IMAPI2.MsftDiscFormat2Data")

This is the object we’ll use to actually write a data stream to the disc. However, we can’t use this object until we set a couple of its properties:

objDataWriter.Recorder = objRecorder
objDatawriter.ClientName = "BurnTest1"

The first property we set is the Recorder property. This property tells the MsftDiscFormat2Data object which device to write to, in this case the device we’re referencing with our objRecorder object. The next property, ClientName, is what’s known as the “friendly name.” The name might be friendly, but the explanation of what it’s used for isn’t; it’s actually a little complicated. The important thing to know is that you can use pretty much any name you want, but you have to give it a name. The reason has to do with locking the device while the data is being written. When a CD is in the process of burning, that process has a lock on the device. This name tells the system who has the device locked.

That’s the simplified version. For a more complicated explanation, see the SDK on MSDN.

Before we go on, let’s take a minute and recap where we are. We created a master object that gave us the unique ID of the recording device we want to use. We then used that ID to connect to that device. After that, we created an object that will allow us to write the data to the disc in the recorder, giving it the recorder it’s going to write to and a name identifying it. Now it’s time to get the data.

Burning to a CD isn’t as simple as copying files from one folder or one drive to another, it’s a more complicated than that (as you’re discovering). Rather than simply copying files you need to create an image that will be burned to the disc. We create this image using the MsftFileSystemImage object:

Set objFSI = CreateObject("IMAPI2FS.MsftFileSystemImage")

Another complicated thing about burning CDs and DVDs is the different file formats that can be created – certain devices can only read certain file formats. Disc file formats are way outside the kind of thing we want to talk about in a scripting article, so if you need to know more there’s a pretty good discussion of them on MSDN. We’ve decided to use the ISO 9660 format because it can be read on the most types of devices. We specify the file format by setting the FileSystemToCreate property:

objFSI.FileSystemsToCreate = FsiFileSystemISO9660

We’ve used the constant we set at the beginning of our script, FsiFileSystemISO9660, to assign a value to this property. There are three different file formats you can set your file system to; here are the formats and their values:

FsiFileSystemISO9660

1

FsiFileSystemJoliet

2

FsiFileSystemUDF

4

Now that we’ve set our file format we need to gather up the files themselves. We do this by creating yet another object, the FsiDirectoryItem:

Set objDir = objFSI.Root

You might have noticed that we didn’t use CreateObject to create our FsiDirectoryItem object. Instead we simply retrieved the value of the Root property of our MsftFilesSystemImage object (objFSI). The Root property returns an FsiDirectoryItem object, which we’ve stored in the variable objDir. This has us pointing to what is now the root of our directory structure for the image we’ll be burning to our CD. Now just need to add some files:

objDir.AddTree strPath, False

We add the files to our file system image by calling the AddTree method on our directory object. We pass AddTree two parameters:

strPath – This is a variable containing the path to the files we want burned to the CD (in our case, C:\WriteCD).

False – Passing False as the second parameter means that we want the files in strPath to be in the root of our CD image. If we pass True, that would mean that we want the root of our image to contain the WriteCD directory itself, with the files inside it. In other words, let’s say we have two files in our WriteCD folder, Test1.txt and Test2.txt. If we pass False as the second parameter of AddTree, the contents of our CD will look like this:

Test1.txt

Test2.txt

If we were to pass True, our CD would look like this:

WriteCD

WriteCD\Test1.txt

WriteCD\Test2.txt

We’re just about ready to start burning our image to CD (or DVD). This could take a little while, so at this point we display a message that the script is working and the CD is burning, so be patient:

WScript.Echo "Writing to disc..."

Want to take a guess at what we do now? Wrong! It’s still not time to write to the disc. But it is time to get yet another object, the FileSystemImageResult object:

Set objResult = objFSI.CreateResultImage()

We retrieve this object by calling the CreateImageResult method of the MsftFileSystemImage object. CreateImageResult returns an object that we’ll use to take the files and format information we put into our file system image object and create a data stream, which is what we’ll eventually burn to the disc. We get that data stream by calling the ImageStream property:

objStream = objResult.ImageStream

And yes, it’s finally time: we now have our data in a format that we can write to the disc:

objDataWriter.Write(objStream)

We simply (we know, “simply” may not be the right word at this point) call the Write method of the MsftDiscFormat2Data object – you remember, the object we said earlier we’d use to write the data to the disc – passing it the image stream we want to write.

Finally (whew!) we echo a message saying we’re done:

WScript.Echo "Done"

And yes, we really are done. But here’s some good news: you can use this script for a CD or a DVD. It will work with either type of device.

Note: We ran across an item in a forum not too long ago that pointed out one small thing about burning DVDs that we thought we’d pass along while we’re here. If you’re burning more than 2 GB of data to the DVD, you must set the FileSystemToCreate property to FsiFileSystemUDF (4).

All right, admit it, this was more interesting than the saga of Clair and Jeremy, right? No, we’re not going back to that story – not yet anyway. Instead we have a couple more things to tell you related to CD and DVD burning.

Top of pageTop of page

Burning ISO Images

In addition to burning a set of data files to disc, you can also burn an ISO image to disc. In case you don’t know (you probably do, but we don’t want to leave anyone out, just in case), an ISO image contains a set of data files as well as the information required to burn those files to a disc. Rather than burning a file with a .iso extension to a disc, you burn the files within that image to the disc.

Chances are if you’re burning ISOs you’re burning pretty large files. Be prepared to be patient. When we burned a 200 MB ISO to a CD it took very little time. However, we also burned a 4 GB ISO image to a DVD on a computer (one with 3.5 GB of memory) and it took 23 minutes. Not only can the script take a long time, but it can use of a lot of your system resources in the process, meaning you can’t do much else. So if you’re burning a large ISO to a DVD, you might want to kick off the script and go to lunch. That’s what we did.

Let’s take a look at the script, then we’ll explain more:

Const adFileTypeBinary = 1

index = 1
strISOFile = "c:\scripts\TestImage.iso"

Set objDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")
Id = objDiscMaster.Item(index)

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder(Id)

Set objDataWriter = CreateObject("IMAPI2.MsftDiscFormat2Data")
objDataWriter.Recorder = objRecorder
objDatawriter.ClientName = "ISOTest1"

Wscript.Echo "Writing to disc..."
Set objStream = CreateObject("ADODB.Stream")
objStream.Open
objStream.Type = adFileTypeBinary
objStream.LoadFromFile strISOFile

If objDataWriter.NextWritableAddress = 0 Then
    objDataWriter.Write(objStream)
    Wscript.Echo "Done"
Else
    Wscript.Echo "Cannot write to disc"
End If

We start by declaring a constant, adFileTypeBinary, and setting its value to 1. We’ll use this constant later to define the type of data we’re working with. Next we set a couple of variables, index and strISOFile. The index variable, just as in our previous script, identifies the index number of the device we’re going to use to burn the image. The strISOFile variable is the path to our ISO image:

Const adFileTypeBinary = 1

index = 1
strISOFile = "c:\scripts\TestImage.iso"

We’re not going to go over the next several lines in detail because they’re identical to the data burning script we just looked at:

Set objDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")
Id = objDiscMaster.Item(index)

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder(Id)

Set objDataWriter = CreateObject("IMAPI2.MsftDiscFormat2Data")
objDataWriter.Recorder = objRecorder
objDatawriter.ClientName = "ISOTest1"

We once again connect to the MsftDiscMaster2 object in order to retrieve the ID of the device we’re going to use (identified by the index variable we declared earlier). We then connect to the MsftDiscRecorder2 object and call InitializeDiscRecorder, passing it the ID we retrieved from the disc master. Next we connect to MsftDiscFormat2Data, where we set the Recorder property to the device we’re burning data to. Finally, we give it a ClientName to identify the process, in this case ISOTest1.

This is where this script starts to differ from the last one. First, keeping in mind that this process can take a little while, we display a message so that you know that you might need to wait a few minutes:

Wscript.Echo "Writing to disc..."

In our previous script this is where we connected to the MsftFileSystemImage object so we could start gathering together our data files into an image that can be burned to disc. However, in burning an ISO image we already have an image all ready for us; we simply need to connect to that image. We do that using the ADODB.Stream object:

Set objStream = CreateObject("ADODB.Stream")
objStream.Open
objStream.Type = adFileTypeBinary
objStream.LoadFromFile strISOFile

We start by creating the Stream object and then opening it. Now it’s time to use the constant we defined at the beginning of our script. Stream objects can be one of two types:

adTypeBinary

1

adTypeText

2

Even if your ISO image is full of text files, the image itself is still considered a binary file, which means we need to set our Stream Type to binary:

objStream.Type = adFileTypeBinary

Now that our Stream is all set up we simply call the LoadFromFile method, passing it the path to the ISO file, and load in the image:

objStream.LoadFromFile strISOFile

That’s almost all there is to it. All we have to do now is call the Write method to write our data to the disc. However, we need to make one small check first. Because an ISO is typically an image of a full CD or DVD, we must make sure that we’re going to start writing at the very beginning of the disc by checking the NextWriteableAddress property:

If objDataWriter.NextWritableAddress = 0 Then

If the next writeable address on the disc is 0, meaning we can start writing at the beginning of the disc, we go ahead and write the image and display a message when we’re done:

objDataWriter.Write(objStream)
    Wscript.Echo "Done"

If the next writeable address isn’t at the beginning, we display a message:

Else
    Wscript.Echo "Cannot write to disc"
End If

And that’s all there is to it.

Top of pageTop of page

Erasing a Disc

Let’s take a look at one more thing before we close out this episode. If you have writeable media, you might want to erase the contents of a disc and reuse it for something else. Here’s a script that erases a disc:

index = 1

Set objDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")

Id = objDiscMaster.Item(index)

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder Id

Set objEraser = CreateObject("IMAPI2.MsftDiscFormat2Erase")
objEraser.Recorder = objRecorder
objEraser.ClientName = "EraseDisc"
objEraser.EraseMedia

Wscript.Echo "Done"

Once again we declare our index variable, which identifies the device we’re working with. We connect to the MsftDiscMaster2 object to get the ID for that device, then connect to and initialize the device using that ID:

index = 1

Set objDiscMaster = CreateObject("IMAPI2.MsftDiscMaster2")

Id = objDiscMaster.Item(index)

Set objRecorder = CreateObject("IMAPI2.MsftDiscRecorder2")
objRecorder.InitializeDiscRecorder Id

Believe it or not, IMAPI2 has a class that’s made specifically for erasing – how convenient is that? We simply connect to this class – MsftDiscFormat2Erase – and set a few properties, and then we’re on our way:

Set objEraser = CreateObject("IMAPI2.MsftDiscFormat2Erase")
objEraser.Recorder = objRecorder
objEraser.ClientName = "EraseDisc"

The properties we’ve set shouldn’t be surprising to you at this point. We set the Recorder property to identify the device we’ll be using to erase the disc, and we’ve set the ClientName property to identify this particular process. Now all we have to do is erase the disc:

objEraser.EraseMedia

As you can see, erasing the contents of the disc is as simple as calling the EraseMedia method. After that we display a message that we’re done and, well, we’re done.

Top of pageTop of page

Every Good Story Line Needs Some Conflict

Let’s sum up what we’ve seen so far in this article. We’ve been able to burn data to a disc, either CD or DVD; we burned an ISO image to disc; and we erased the data from a disc. We realize some of this might have seemed a little complicated, but it’s doable, right?

Well, for the most part. We did run into a couple of snags here and there. For example, after running the script to erase a rewritable DVD (DVD+RW), we couldn’t get our script that writes data to write to that disc again; we could, however, write to that disc from the GUI. On the other hand, if we erased a disc through the GUI and ran our script that wrote to the disc everythingworked fine. We did some more testing and it turned out that the process of using scripts to write, then erase, then write again to a DVD+RW disc worked on two of the four machines we tested on. This tells us there could be hardware issues. And the fact that it worked fine from the GUI tells us that the methods available from scripts aren’t doing the exact same thing as the GUI.

On the plus side, when we tried this with a rewritable CD (CD+RW) the scripts worked fine on every machine we tested on. So with a CD+RW disc, we’re able to use scripts to write data to a CD, erase that CD, then write to it again with no problems.

Another difficulty we ran into was trying to append data to a DVD+RW. As it turns out, you can’t do that. Supposedly you can do that with a CD+RW, but we’ve decided to save that for another day. In the meantime, feel free to work on that one on your own.

If you’re wondering about burning audio CDs from a script – well, we are too. We played around with that one for a while and didn’t have much luck. We haven’t given up entirely, but we decided to go ahead and introduce you to the basics of CD and DVD burning using IMAPI2. Maybe someday we’ll get around to figuring out some of these other things.

And there are a lot of other things you can (or should be able to) do with the IMAPI2 API. For example, you can monitor events to check on the progress of your media. But – yes, you guessed it – we’re hoping to bring you more on that in the future. Like we said, this is all a bit on the complicated side for scripting, so with these articles we just wanted to give you enough to get you started; hopefully we succeeded at that. If not, well, it’s not the first time the Scripting Guys haven’t succeeded at something (just ask our manager…and our past managers…college professors…teachers…).

If you run into problems there’s a forum on MSDN that the optical platform group at Microsoft (the team that came up with all this stuff) keeps an eye on, so feel free to ask questions.

Top of pageTop of page

And They All Lived Happily Ever After

All in all, though, that wasn’t too bad. For the most part the IMAPI2 object model is pretty consistent; once you know how to do one thing it starts getting easier to figure out others (sort of). Now see, wasn’t that more interesting than the saga of Ken, Clair, and all the rest? Oh all right, if you insist…

As it turns out, Ken found out about the brain tumor and went running back to his old girlfriend, Elizabeth, who really wasn’t the father of Clair’s baby, Clair just said that because Jeremy said “yes” when she asked if she looked fat in her new jeans. And the doctors were mistaken, Delta didn’t really have a brain tumor, it was just a smudge on the MRI, and Agnes was so relieved she forgave her mother for stealing her boyfriend who’s now Elizabeth’s boyfriend…again.

The End…For Now


Top of pageTop of page