Is there a relatively straightforward way to finalize a CD or DVD in C# or PowerShell?
Asked Answered
L

2

9

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?

Lucilius answered 4/12, 2017 at 18:34 Comment(6)
Some time ago I had the opposite problem, couldn't avoid the finalization. Ejecting the media, always caused a lead-out. This project from SourceForge helped BwgBurn. There's a zip file with the source code (c#). Hope it helps.Docket
A session closure is called a lead-out. What you want is to write a "disk lead-out" (or a final session closure) on a multi-session media, am I right? Have you in fact tried to play with IMAPI's ForceMediaToBeClosed and DisableConsumerDvdCompatibility? PS: MM Commands and terms are available here: 13thmonkey.org/documentation/SCSI/mmc6r02g.pdfHurlburt
@SimonMourier: I'm not an expert in the IMAPI2 API, and I'm trying not to be. I see literally months of work required to become competent in that API, months that I don't have available for writing a single FinalizeDisk() function. That working draft you linked is more than seven hundred pages long.Lucilius
But have you tried to play with the two IMAPI properties (aka: do you have a problem? or?)Hurlburt
@SimonMourier: The properties are not the problem (other than my ignorance of them). The problem, as my limited knowledge allows me to understand it, is that you can't simply finalize a disk. Finalize is not an operation; it's a flag. So I think creating a new session with this flag set is required. But since I've never actually done this in code ...Lucilius
You might find this article helpful. You are able to use IMAPI2 directly in powershell since it is written using COM; therefore, theoretically you could use MsftDiscFormat2Data's put_ForceMediaToBeClosed method to prevent further writing to the disc instead of writing your own. Hope this helps!Compiler
C
12

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.

Counterpressure answered 11/1, 2018 at 0:51 Comment(0)
A
8

Set the ForceMediaToBeClosed flag on the IMAPI2.MsftDiscFormat2Data object and write out the disc with the close flag enabled.

  • If you already know its your last session, set the flag, add your data to write and then write it out and it will close.
  • If you have already written your last session, import the last session, set the flag and write to close.

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 ;
Admission answered 11/1, 2018 at 0:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.