First, some clarification of terms. By finalize, I don't mean closing a session; I mean writing a lead-out to a CD or DVD in such a way that information can no longer be added to it via the usual means (Roxio, Nero, Windows Explorer, etc.)
I've done a fair amount of research on this. There are some open-source programs like InfraRecorder from which we could draw some inspiration, but they all seem to involve rather elaborate reams of C++ code using IMAPI, which seems like a very low-level way to do things. None of the developers on our team have the C++ or IMAPI expertise to support such a code base.
The most promising resource on the internet appears to be this one, but it doesn't seem to include a finalize function. Here is the code that "writes an image":
public void WriteImage(BurnVerificationLevel verification, bool finalize, bool eject)
{
if (!_recorderLoaded)
throw new InvalidOperationException("LoadMedia must be called first.");
MsftDiscRecorder2 recorder = null;
MsftDiscFormat2Data discFormatData = null;
try
{
recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(_recorders.SelectedItem.InternalUniqueId);
discFormatData = new MsftDiscFormat2Data
{
Recorder = recorder,
ClientName = ClientName,
ForceMediaToBeClosed = finalize
};
//
// Set the verification level
//
var burnVerification = (IBurnVerification)discFormatData;
burnVerification.BurnVerificationLevel = IMAPI_BURN_VERIFICATION_LEVEL.IMAPI_BURN_VERIFICATION_NONE;
//
// Check if media is blank, (for RW media)
//
object[] multisessionInterfaces = null;
if (!discFormatData.MediaHeuristicallyBlank)
multisessionInterfaces = discFormatData.MultisessionInterfaces;
//
// Create the file system
//
IStream fileSystem;
_CreateImage(recorder, multisessionInterfaces, out fileSystem);
discFormatData.Update += _discFormatWrite_Update;
//
// Write the data
//
try
{
discFormatData.Write(fileSystem);
}
finally
{
if (fileSystem != null) Marshal.FinalReleaseComObject(fileSystem);
}
discFormatData.Update -= _discFormatWrite_Update;
if (eject) recorder.EjectMedia();
}
finally
{
_isWriting = false;
if (discFormatData != null) Marshal.ReleaseComObject(discFormatData);
if (recorder != null) Marshal.ReleaseComObject(recorder);
}
}
The critical section of code seems to be this one:
discFormatData = new MsftDiscFormat2Data
{
Recorder = recorder,
ClientName = ClientName,
ForceMediaToBeClosed = finalize // <-- Here
};
But this isn't a finalize function; it's a function that burns actual data onto a disk. Do you have to actually create a new session to perform a finalization on an existing disk?
As a general rule, finalization means that the disc cannot have any additional data written to it. It is the last step in the DVD authoring process. The term is also used as an alternative word for the "closing" of a CD-R, in which Table of Contents data and the like are written out to enable the computer to read a CD.
If the disc contains video as in a video DVD then the disc needs to be finished off so that the contents are readable by DVD playback devices and that those devices recognize it as a Video DVD.
The ForceMediaToBeClosed
property of IDiscFormat2Data
controls whether the IMAPI finalizes the disc after the next write:
Set to VARIANT_TRUE to mark the disc as closed to prohibit additional writes when the next write session ends.
The Image Mastering API does not provide an abstraction used specifically to finalize the disc, so we need to perform a write operation. The API will finalize a blank disc during the initial burn if we switch on ForceMediaToBeClosed
with the main image writer. For an existing multi-session disc, we need to append another session.
Here's a simple PowerShell example that we can try so we don't need to build the project. The concepts are similar in C#:
$drives = New-Object -ComObject 'IMAPI2.MsftDiscMaster2'
$recorder = New-Object -ComObject 'IMAPI2.MsftDiscRecorder2'
$recorder.InitializeDiscRecorder($drives[0]) # Choose a drive here
$disc = New-Object -ComObject 'IMAPI2.MsftDiscFormat2Data'
$disc.ClientName = 'PowerShell Recorder'
$disc.Recorder = $recorder
$disc.ForceMediaToBeClosed = $true # Finalize the next session
$image = New-Object -ComObject 'IMAPI2FS.MsftFileSystemImage'
if (!$disc.IsCurrentMediaSupported($recorder)) {
throw 'Disc is not writeable.'
} elseif ($disc.MediaHeuristicallyBlank) {
$image.ChooseImageDefaults($recorder)
} else {
$image.MultisessionInterfaces = $disc.MultisessionInterfaces
$image.ImportFileSystem() > $null
}
This sets up some boilerplate that we'll use below to burn a disc. We'll need to add error handling and capability detection for practical use, but it works fine as a demonstration. If we paste or dot-source this code into a PowerShell session, we can play with the COM objects interactively.
At this point, if we check the status of a blank or open disc, we should see a value of 2
, 4
, or 6
which correspond to the "blank" or "appendable" bitmasks (6
for both) enumerated on IMAPI_FORMAT2_DATA_MEDIA_STATE
.
PS> $disc.CurrentMediaStatus # 4 for an open, multi-session disc
Then, we can add some files. If we just want to close-off a multi-session disc, we don't need to add anything to the image. The API records the session's lead-in and lead-out with an empty data track.
PS> $image.Root.AddTree('path\to\root\folder', $false)
Finally, we'll burn our changes to the disc. Because we set $disc.ForceMediaToBeClosed
to $true
, this operation finalizes the disc, and no further write operations are allowed:
PS> $disc.Write($image.CreateResultImage().ImageStream)
If we inspect the disc status now, it should indicate that the disc is not writable:
PS> $disc.CurrentMediaStatus # 16384 or 40960
For a single-session disc, we should see 16384
(0x4000
, "finalized"). My system reports 40960
for closed, multi-session discs which contains the bits 0x2000
("write-protected") and 0x8000
("unsupported media"). We may need to eject or power-cycle some hardware to see accurate values after burning.
Remarks:
In general, each session on a multi-session disc starts with a lead-in and ends with a lead-out. The lead-in of the last session permanently closes the media to further writes when we finalize a disc. This is why we need to append an additional session to an unclosed disc even if we have no more data to add.
IMAPI will automatically finalize a disc if the free space drops below 2%.
InfraRecorder—the tool mentioned in the question—does not use the IMAPI. This application provides a frontend to cdrtools which controls the device IO directly. If we just need to finalize unclosed discs, we may want to use the cdrecord CLI program included with this package to avoid maintaining an extra codebase:
PS> cdrecord -scanbus # Show <drive> IDs to choose from
PS> cdrecord -fix dev=<drive> # Close an open session
As a brief starting point, here's how we can finalize a multi-session disc:
PS> $session = cdrecord -msinfo dev=<drive>
PS> mkisofs -rJ -C $session -M <drive> 'path\to\root' | cdrecord dev=<drive> -
This achieves the same result as our PowerShell script that uses the IMAPI: we import the last session, create the image, and then burn a new session which finalizes the disc. By omitting the -multi
argument to cdrecord, the command won't write the lead-in in a way that allows for continuation of a multi-session disc.
While we typically see this toolset on Unix-like systems, builds are available for Windows.
For more advanced applications, we can use an implementation of the lower-level IDiscRecorderEx
to query and send commands to the recording device.
Set the ForceMediaToBeClosed
flag on the IMAPI2.MsftDiscFormat2Data
object and write out the disc with the close flag enabled.
Approach is described here: https://social.msdn.microsoft.com/Forums/en-US/ce1ff136-39a1-4442-bc5c-61c119b6f4f2/finalize-question?forum=windowsopticalplatform#2e968a94-7347-4d94-9332-00fe7cd0ba89
Below is a link to a nice Powershell burning script, all you'd have to do is update Out-CD
with a new param
to set $DiscFormatData.ForceMediaToBeClosed = true
when you are ready for your closing write.
Link: https://www.adamtheautomator.com/use-powershell-to-automate-burning-cds/
FYI:
# this fetches all the properties (as you probably already know)
$DiscFormatData = New-Object -com IMAPI2.MsftDiscFormat2Data ;
$DiscFormatData | Get-Member ;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With