|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionWindows introduced the new IMAPIv2.0 with the release of the Vista Operating System which was a big improvement over the original IMAPI. The original IMAPI is great for CDROMs, but it has some huge limitations like not being able to write to DVD media. I am sure this limitation is due to almost nobody having a DVD writer when Windows XP was released back in 2001. IMAPIv2 allows you to write to CD, DVD, and even Blu-ray media, as well as read and write ISO files. IMAPIv2.0 had a problem since it was only available with Windows Vista. But in June of 2007, Microsoft released update packages for Windows XP and Windows 2003. You can download the updates here. I wrote this article as a sequel to my C++ article Burning CD/DVD Media with the Image Mastering API Version 2.0. Most of the IMAPI2 samples seem to be in scripting languages. The only C# sample I found was the This article was more difficult than I had thought it would be. Normally .NET applications are supposed to be easier, but there were a number of issues that I needed to figure out to get this to work. If you're not interested in hearing me rant and rave, you can skip over the next section. The ProblemsIMAPI2 was implemented using two separate COM DLLs: imapi2.dll and imapi2fs.dll. imapi2.dll handles most of the device and recording APIs and imapi2fs.dll handles all of the file system and Unable to cast object of type 'IMAPI2FS.FsiStreamClass' to type 'IMAPI2.IStream'
Microsoft realized this problem and created a project called Unfortunately, their implementation did not completely fix my problems. I was having other COM problems, like having the application throw exceptions whenever I would try to use any method that returned an So off I went on my journey to create a complete solution. I created two .NET Interop assemblies from the COM DLLs by using the Microsoft Type Library to Assembly Converter tlbimp.exe. I created the two assemblies by using the following commands: tlbimp imapi2.dll /out:imapi2int.dll
tlbimp imapi2fs.dll /out:imapi2fsint.dll
Once I had the .NET assemblies, I used the Lutz Reflector to disassemble the interops and create C# source code. I combined the two files and made many modifications to the interfaces and helper classes, and added support for all interfaces of IMAPI2. That sounds a lot easier than it really was. One of the biggest issues I had that seemed to take forever to figure out was the reversing of the I also chose not to implement the Lastly, in order to receive notifications for all events, I had to go through the SDK and find all of the Dispatch IDs for all of the events. Without these values, the event handlers are unable to receive notifications. New IMAPI2 Interop - Imapi2interop.csMy replacement for Interop.cs is Imapi2Interop.cs included in the source code. It defines the following classes and interfaces:
It also defines the following events:
Using the CodeMake sure that XP and 2003 have the IMAPI2 updates mentioned at the top of the article. Do not add the imapi2.dll and imapi2fs.dll COM DLLs to your project. That will cause the problems listed above. Add the file imapi2interop.cs to your project and define the namespace in your app: using IMAPI2.Interop;
In order to receive notification to your event handler from COM, you need to open up the file AssemblyInfo.cs and change the [assembly: ComVisible(true)]
Determining Media TypeTo determine the media type and available space on the hard drive, you create a Once you have the media type, create a To determine if any sessions have already been recorded on the media, check the If it is Then, get the free media blocks by multiplying the private void buttonDetectMedia_Click(object sender, EventArgs e)
{
IDiscRecorder2 discRecorder =
(IDiscRecorder2)devicesComboBox.Items[devicesComboBox.SelectedIndex];
//
// Create and initialize the IDiscFormat2Data
//
MsftDiscFormat2Data discFormatData = new MsftDiscFormat2Data();
if (!discFormatData.IsCurrentMediaSupported(discRecorder))
{
labelMediaType.Text = "Media not supported!";
totalDiscSize = 0;
return;
}
else
{
//
// Get the media type in the recorder
//
discFormatData.Recorder = discRecorder;
IMAPI_MEDIA_PHYSICAL_TYPE mediaType = discFormatData.CurrentPhysicalMediaType;
labelMediaType.Text = GetMediaTypeString(mediaType);
//
// Create a file system and select the media type
//
MsftFileSystemImage fileSystemImage = new MsftFileSystemImage();
fileSystemImage.ChooseImageDefaultsForMediaType(mediaType);
//
// See if there are other recorded sessions on the disc
//
if (!discFormatData.MediaHeuristicallyBlank)
{
fileSystemImage.MultisessionInterfaces =
discFormatData.MultisessionInterfaces;
fileSystemImage.ImportFileSystem();
}
int freeMediaBlocks = fileSystemImage.FreeMediaBlocks;
totalDiscSize = 2048 * freeMediaBlocks;
}
UpdateCapacity();
}
Adding Files and Directories to the ListboxI created a generic interface IMediaItem
{
/// <summary>
/// Returns the full path of the file or directory
/// </summary>
string Path { get; }
/// <summary>
/// Returns the size of the file or directory to the next largest sector
/// </summary>
Int64 SizeOnDisc { get; }
/// <summary>
/// Returns the Icon of the file or directory
/// </summary>
System.Drawing.Icon FileIcon { get; }
/// <summary>
/// Adds the file or directory to the directory item, usually the root.
/// </summary>
bool AddToFileSystem(IFsiDirectoryItem rootItem);
}
For file items, I created the For directory items, I created the These classes and interfaces are located in the IMediaItem.cs file. Creating the ImageI use the private bool CreateMediaFileSystem(IDiscRecorder2 discRecorder, out IStream dataStream)
{
MsftFileSystemImage fileSystemImage = new MsftFileSystemImage();
fileSystemImage.ChooseImageDefaults(discRecorder);
fileSystemImage.FileSystemsToCreate =
FsiFileSystems.FsiFileSystemJoliet | FsiFileSystems.FsiFileSystemISO9660;
fileSystemImage.VolumeName = textBoxLabel.Text;
fileSystemImage.Update += new DFileSystemImage_EventHandler(fileSystemImage_Update);
//
// Get the image root
//
IFsiDirectoryItem rootItem = fileSystemImage.Root;
//
// Add Files and Directories to File System Image
//
foreach (IMediaItem mediaItem in listBoxFiles.Items)
{
//
// Check if we've cancelled
//
if (backgroundBurnWorker.CancellationPending)
{
break;
}
//
// Add to File System
//
mediaItem.AddToFileSystem(rootItem);
}
fileSystemImage.Update -= new DFileSystemImage_EventHandler(fileSystemImage_Update);
//
// did we cancel?
//
if (backgroundBurnWorker.CancellationPending)
{
dataStream = null;
return false;
}
dataStream = fileSystemImage.CreateResultImage().ImageStream;
return true;
}
MultithreadingBurning or formatting media can take some time so we do not want to perform these actions on the main UI thread. I use the Writing Data to MediaI write the data in the private void backgroundBurnWorker_DoWork(object sender, DoWorkEventArgs e)
{
//
// Create and initialize the IDiscRecorder2 object
//
MsftDiscRecorder2 discRecorder = new MsftDiscRecorder2();
BurnData burnData = (BurnData)e.Argument;
discRecorder.InitializeDiscRecorder(burnData.uniqueRecorderId);
discRecorder.AcquireExclusiveAccess(true, m_clientName);
//
// Create the file system
//
IStream fileSystem = null;
if (!CreateMediaFileSystem(discRecorder, out fileSystem))
{
e.Result = -1;
return;
}
//
// Create and initialize the IDiscFormat2Data
//
MsftDiscFormat2Data discFormatData = new MsftDiscFormat2Data();
discFormatData.Recorder = discRecorder;
discFormatData.ClientName = m_clientName;
discFormatData.ForceMediaToBeClosed = checkBoxCloseMedia.Checked;
//
// add the Update event handler
//
discFormatData.Update += new DiscFormat2Data_EventHandler(discFormatData_Update);
//
// Write the data here
//
try
{
discFormatData.Write(fileSystem);
e.Result = 0;
}
catch (COMException ex)
{
e.Result = ex.ErrorCode;
MessageBox.Show(ex.Message, "IDiscFormat2Data.Write failed",
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
//
// remove the Update event handler
//
discFormatData.Update -= new DiscFormat2Data_EventHandler(discFormatData_Update);
if (this.checkBoxEject.Checked)
{
discRecorder.EjectMedia();
}
discRecorder.ReleaseExclusiveAccess();
}
Progress Update EventsThe void discFormatData_Update([In, MarshalAs(UnmanagedType.IDispatch)] object sender,
[In, MarshalAs(UnmanagedType.IDispatch)] objectprogress)
{
//
// Check if we've cancelled
//
if (backgroundBurnWorker.CancellationPending)
{
IDiscFormat2Data format2Data = (IDiscFormat2Data)sender;
format2Data.CancelWrite();
return;
}
IDiscFormat2DataEventArgs eventArgs = (IDiscFormat2DataEventArgs)progress;
m_burnData.task = BURN_MEDIA_TASK.BURN_MEDIA_TASK_WRITING;
// IDiscFormat2DataEventArgs Interface
m_burnData.elapsedTime = eventArgs.ElapsedTime;
m_burnData.remainingTime = eventArgs.RemainingTime;
m_burnData.totalTime = eventArgs.TotalTime;
// IWriteEngine2EventArgs Interface
m_burnData.currentAction = eventArgs.CurrentAction;
m_burnData.startLba = eventArgs.StartLba;
m_burnData.sectorCount = eventArgs.SectorCount;
m_burnData.lastReadLba = eventArgs.LastReadLba;
m_burnData.lastWrittenLba = eventArgs.LastWrittenLba;
m_burnData.totalSystemBuffer = eventArgs.TotalSystemBuffer;
m_burnData.usedSystemBuffer = eventArgs.UsedSystemBuffer;
m_burnData.freeSystemBuffer = eventArgs.FreeSystemBuffer;
//
// Report back to the UI
//
backgroundBurnWorker.ReportProgress(0, m_burnData);
}
Formatting/Erasing RW Discs
I format the disc in the private void backgroundFormatWorker_DoWork(object sender, DoWorkEventArgs e)
{
//
// Create and initialize the IDiscRecorder2
//
MsftDiscRecorder2 discRecorder = new MsftDiscRecorder2();
string activeDiscRecorder = (string)e.Argument;
discRecorder.InitializeDiscRecorder(activeDiscRecorder);
discRecorder.AcquireExclusiveAccess(true, m_clientName);
//
// Create the IDiscFormat2Erase and set properties
//
MsftDiscFormat2Erase discFormatErase = new MsftDiscFormat2Erase();
discFormatErase.Recorder = discRecorder;
discFormatErase.ClientName = m_clientName;
discFormatErase.FullErase = !checkBoxQuickFormat.Checked;
//
// Setup the Update progress event handler
//
discFormatErase.Update += new DiscFormat2Erase_EventHandler(discFormatErase_Update);
//
// Erase the media here
//
try
{
discFormatErase.EraseMedia();
e.Result = 0;
}
catch (COMException ex)
{
e.Result = ex.ErrorCode;
MessageBox.Show(ex.Message, "IDiscFormat2.EraseMedia failed",
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
//
// Remove the Update progress event handler
//
discFormatErase.Update -= new DiscFormat2Erase_EventHandler(discFormatErase_Update);
if (checkBoxEjectFormat.Checked)
{
discRecorder.EjectMedia();
}
discRecorder.ReleaseExclusiveAccess();
}
History
| ||||||||||||||||||||||