有没有一种相对简单的方法可以在 C# 或 PowerShell 中完成 CD 或 DVD?

Posted

技术标签:

【中文标题】有没有一种相对简单的方法可以在 C# 或 PowerShell 中完成 CD 或 DVD?【英文标题】:Is there a relatively straightforward way to finalize a CD or DVD in C# or PowerShell? 【发布时间】:2018-05-18 07:07:15 【问题描述】:

首先,对术语进行一些澄清。 finalize,我不是指结束会话;我的意思是,将信息写入 CD 或 DVD 的方式使信息无法再通过通常的方式(Roxio、Nero、Windows Explorer 等)添加到其中。

我已经对此进行了大量研究。有一些像InfraRecorder 这样的开源程序,我们可以从中汲取一些灵感,但它们似乎都涉及到相当复杂的大量使用 IMAPI 的 C++ 代码,这似乎是一种非常低级的处理方式。我们团队中的开发人员都没有 C++ 或 IMAPI 专业知识来支持这样的代码库。

互联网上最有前途的资源似乎是this one,但它似乎不包含finalize 函数。这是“写图像”的代码:

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);                
    

代码的关键部分似乎是这个:

discFormatData = new MsftDiscFormat2Data

    Recorder = recorder,
    ClientName = ClientName,
    ForceMediaToBeClosed = finalize // <-- Here
;

但这不是一个终结函数;它是将实际数据刻录到磁盘上的功能。您是否必须实际创建一个新会话才能在现有磁盘上执行终结?

【问题讨论】:

前段时间遇到了相反的问题,无法避免敲定。弹射媒体,总是引起引出。 SourceForge 的这个项目帮助了BwgBurn。有一个带有源代码 (c#) 的 zip 文件。希望对您有所帮助。 会话关闭称为导出。你想要的是在多会话媒体上写一个“磁盘导出”(或最终会话关闭),对吗?您实际上是否尝试过使用 IMAPI 的 ForceMediaToBeClosed 和 DisableConsumerDvdCompatibility? PS:MM 命令和术语在这里可用:13thmonkey.org/documentation/SCSI/mmc6r02g.pdf @SimonMourier:我不是 IMAPI2 API 方面的专家,而且我尽量不要成为专家。我确实看到需要几个月的工作才能胜任该 API,而我没有几个月的时间来编写单个 FinalizeDisk() 函数。您链接的那份工作草稿长达七百多页。 但是您是否尝试过使用这两个 IMAPI 属性(又名:您有问题吗?或者?) @SimonMourier:属性不是问题(除了我对它们的无知)。问题是,我有限的知识让我理解它,你不能简单地完成磁盘。 Finalize 不是操作;这是一面旗帜。因此,我认为需要使用此标志集创建一个新会话。但是因为我从来没有真正在代码中这样做过...... 【参考方案1】:

IDiscFormat2Datacontrols whether the IMAPI finalizes the disc 的ForceMediaToBeClosed 属性在下一个之后写:

设置为 VARIANT_TRUE 以将光盘标记为已关闭,以在下一次写入会话结束时禁止其他写入。

Image Mastering API 不提供专门用于完成光盘的抽象,因此我们需要执行写入操作。如果我们使用主映像写入器打开ForceMediaToBeClosed,API 将在初始刻录期间完成一张空白光盘。对于现有的多会话光盘,我们需要附加另一个会话。

这是一个简单的 PowerShell 示例,我们可以尝试一下,这样我们就不需要构建项目了。 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

这设置了一些样板文件,我们将在下面使用这些样板文件来刻录光盘。我们需要添加错误处理和功能检测以供实际使用,但它作为演示工作正常。如果我们将此代码粘贴或点源到 PowerShell 会话中,我们可以交互地使用 COM 对象。

此时,如果我们检查空白或打开光盘的状态,我们应该看到对应于“空白”或“可附加”位掩码的246 值( 6 两者都在IMAPI_FORMAT2_DATA_MEDIA_STATE 上枚举。

PS> $disc.CurrentMediaStatus  # 4 for an open, multi-session disc 

然后,我们可以添加一些文件。如果我们只想关闭多区段光盘,我们不需要向图像添加任何内容。 API 使用空数据轨道记录会话的导入和导出。

PS> $image.Root.AddTree('path\to\root\folder', $false)

最后,我们会将更改刻录到光盘上。因为我们将$disc.ForceMediaToBeClosed 设置为$true,所以这个操作完成了磁盘,不允许进一步的写操作:

PS> $disc.Write($image.CreateResultImage().ImageStream)

如果我们现在检查磁盘状态,应该表明该磁盘不可写:

PS> $disc.CurrentMediaStatus  # 16384 or 40960

对于单会话光盘,我们应该看到163840x4000,“finalized”)。我的系统报告 40960 用于包含位 0x2000(“写保护”)和 0x8000(“不支持的媒体”)的封闭的多会话光盘。我们可能需要弹出或重启某些硬件才能在刻录后看到准确的值。

备注:

通常,多区段光盘上的每个区段都以导入开头并以导出结尾。当我们最终确定光盘时,最后一个会话的导入会永久关闭媒体以进一步写入。这就是为什么即使我们没有更多数据要添加,我们也需要向未关闭的光盘附加一个额外的会话。

如果可用空间低于 2%,IMAPI 将自动完成光盘。

InfraRecorder(问题中提到的工具)不使用 IMAPI。此应用程序为cdrtools 提供了一个前端,它直接控制设备 IO。如果我们只需要最终确定未关闭的光盘,我们可能需要使用此软件包中包含的cdrecord CLI 程序以避免维护额外的代码库:

PS> cdrecord -scanbus          # Show <drive> IDs to choose from
PS> cdrecord -fix dev=<drive>  # Close an open session

作为一个简短的起点,以下是我们如何完成多区段光盘:

PS> $session = cdrecord -msinfo dev=<drive>
PS> mkisofs -rJ -C $session -M <drive> 'path\to\root' | cdrecord dev=<drive> -

这与我们使用 IMAPI 的 PowerShell 脚本实现了相同的结果:我们导入最后一个会话,创建映像,然后刻录一个新的会话来完成光盘。通过省略 cdrecord-multi 参数,该命令将不会以允许连续多区段光盘的方式写入前导。

虽然我们通常在类 Unix 系统上看到此工具集,但在 Windows 上看到 builds are available。

对于更高级的应用程序,我们可以使用较低级别的IDiscRecorderEx 的实现来查询并向记录设备发送命令。

【讨论】:

【参考方案2】:

IMAPI2.MsftDiscFormat2Data 对象上设置ForceMediaToBeClosed 标志,并在启用关闭标志的情况下写出光盘。

如果您已经知道上次会话,请设置标志,添加要写入的数据,然后将其写出,它将关闭。 如果您已经编写了最后一个会话,请导入最后一个会话,设置标志并写入关闭。

方法在这里描述:https://social.msdn.microsoft.com/Forums/en-US/ce1ff136-39a1-4442-bc5c-61c119b6f4f2/finalize-question?forum=windowsopticalplatform#2e968a94-7347-4d94-9332-00fe7cd0ba89

下面是一个不错的 Powershell 刻录脚本的链接,您只需在准备好结束写入时将Out-CD 更新为新的param 以设置$DiscFormatData.ForceMediaToBeClosed = true

链接:https://www.adamtheautomator.com/use-powershell-to-automate-burning-cds/

仅供参考:

# this fetches all the properties (as you probably already know)
$DiscFormatData  = New-Object -com IMAPI2.MsftDiscFormat2Data ;
$DiscFormatData | Get-Member ;

【讨论】:

以上是关于有没有一种相对简单的方法可以在 C# 或 PowerShell 中完成 CD 或 DVD?的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种简单有效的方法可以在 C# 或 C/C++ 中列出所有 USB 设备?

如何在 natTable 中对配置标签进行排序? (有没有一种简单的方法可以从 nattable 中获取相对坐标?)

有没有一种简单的方法可以将 C# 类转换为 PHP?

C# AppSettings:有没有一种简单的方法可以将集合放入 <appSetting>

c# foreach(对象中的属性)...有没有一种简单的方法可以做到这一点?

C# 中是不是存在一种方法来获取给定两个绝对路径输入的相对路径? [复制]