|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThere are several free utilities that allow you to create file images, but the code is usually not available or available only in C++ like this CodeProject article or in other scripting languages. BackgroundInitially available only on Windows Vista, in 2007 Microsoft introduced IMAPIv2.0 for Windows XP and Windows 2003 server. Creating an Optical Image as a FileThere is no one API call that would do that, but doing so is not rocket science. using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using IMAPI2.Interop;
Import this legacy Win32API... [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
static internal extern uint SHCreateStreamOnFile
(string pszFile, uint grfMode, out IStream ppstm);
... and you are ready to go: IFileSystemImage ifsi = new MsftFileSystemImage();
ifsi.ChooseImageDefaultsForMediaType(IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK);
ifsi.FileSystemsToCreate =
FsiFileSystems.FsiFileSystemJoliet | FsiFileSystems.FsiFileSystemISO9660;
ifsi.VolumeName = "YourVolume";
ifsi.Root.AddTree("c:\\YourDirToArchive", true);//use a valid folder
//this will implement the Write method for the formatter
IStream imagestream = ifsi.CreateResultImage().ImageStream;
if (imagestream != null)
{
System.Runtime.InteropServices.ComTypes.STATSTG stat;
imagestream.Stat(out stat, 0x01);
IStream newStream;
if (0 == SHCreateStreamOnFile
("C:\\YourImage.iso", 0x00001001, out newStream) && newStream != null)
{
IntPtr inBytes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(long)));
IntPtr outBytes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(long)));
try
{
imagestream.CopyTo(newStream, stat.cbSize, inBytes, outBytes);
Marshal.ReleaseComObject(imagestream);
imagestream = null;
newStream.Commit(0);
}
finally
{
Marshal.ReleaseComObject(newStream);
Marshal.FreeHGlobal(inBytes);
Marshal.FreeHGlobal(outBytes);
if (imagestream != null)
Marshal.ReleaseComObject(imagestream);
}
}
}
Marshal.ReleaseComObject(ifsi);
As I promised it looks pretty simple, but with simplicity we also cut some corners that would make this solution unfit for a real world UI application. Adding an Individual File to the ImageAdding a file involves some multiple private void CreateFSIFile(FileInfo file, IFsiDirectoryItem diritem)
{
string realpath = UniqueListFileSystemInfo.GetPhysicalPath(file.FullName);
int index = realpath.IndexOf(":\\") + 1;
if (_sysImage.Exists(realpath.Substring(index)) == FsiItemType.FsiItemNotFound)
{
IFsiDirectoryItem crtdiritem = diritem as IFsiDirectoryItem;
string name = Path.GetFileName(realpath);
if (string.Compare(diritem.FullPath,
Path.GetDirectoryName(realpath).Substring(index), true) != 0)
{
string fsipath = Path.GetDirectoryName(realpath).Substring
(index + 1 + diritem.FullPath.Length);
string[] dirs = fsipath.Split('\\');
//create the subdirs one by one
foreach (string dir in dirs)
{
if (dir.Length == 0)
continue;//in the root like C:\
try
{
string newpath = string.Format("{0}\\{1}", crtdiritem.FullPath, dir);
if (_sysImage.Exists(newpath) != FsiItemType.FsiItemDirectory)
crtdiritem.AddDirectory(dir);
}
catch
{
Cancel = true;
throw;
}
crtdiritem = crtdiritem[dir] as IFsiDirectoryItem;
}
}
System.Runtime.InteropServices.ComTypes.IStream newStream = null;
try
{
newStream = LoadCOMStream(realpath);
crtdiritem.AddFile(name, newStream);
}
finally
{
Marshal.ReleaseComObject(newStream);
}
_actualSize += ((FileInfo)file).Length;
if (Update != null && Update.GetInvocationList().Length > 0)
Update(file.FullName, ((FileInfo)file).Length);
}
else
throw new IOException("invalid file or folder");
}
Adding Support for Updating and Cancelling the TaskThere are two time intensive tasks associated with an optical image creation. IFileSystemImageResult IFileSystemImage.CreateResultImage()
{
if (_sysImage == null)
_cancel = false;
#if DEBUG
System.Diagnostics.Stopwatch tm = new System.Diagnostics.Stopwatch();
tm.Start();
#endif
try
{
FileSysImage = new MsftFileSystemImage();
_sysImage.ChooseImageDefaultsForMediaType(_mediatype);
IFsiDirectoryItem root = _sysImage.Root;
_sysImage.FileSystemsToCreate = _fsifs;
_sysImage.VolumeName = _volumeName;
_actualSize = 0;
_items.ForEach(delegate(FileSystemInfo item)
{
if (!_cancel)
if ((item.Attributes & FileAttributes.Directory) == 0)
CreateFSIFile(item as FileInfo, root);
else
if (this.Update == null ||
Update.GetInvocationList().Length == 0)
root.AddTree(item.FullName, true);
else
CreateFSIFolder(item as DirectoryInfo, root);
});
return _cancel ? null : _sysImage.CreateResultImage();
}
catch
{
Cancel = true;
throw;
}
finally
{
if (_cancel)
FileSysImage = null;
#if DEBUG
tm.Stop();
Debug.WriteLine(string.Format("Preparing the image lasted {0} ms",
tm.Elapsed.TotalMilliseconds.ToString("#,#")));
#endif
}
}
Please notice that you would still use the quick approach using private void CreateProgressISOFile
(System.Runtime.InteropServices.ComTypes.IStream imagestream)
{
System.Runtime.InteropServices.ComTypes.STATSTG stat;
int bloksize = _fsres.BlockSize;
long totalblocks = _fsres.TotalBlocks;
#if DEBUG
System.Diagnostics.Stopwatch tm = new System.Diagnostics.Stopwatch();
tm.Start();
#endif
imagestream.Stat(out stat, 0x01);
if (stat.cbSize == totalblocks * bloksize)
{
byte[] buff = new byte[bloksize];
System.IO.BinaryWriter bw =
new BinaryWriter(new FileStream(_outputFileName, FileMode.Create));
IntPtr pcbRead = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)));
IProgressItems prg = _fsres.ProgressItems;
IEnumerator enm = prg.GetEnumerator();
enm.MoveNext();
IProgressItem crtitem = enm.Current as IProgressItem;
try
{
Marshal.WriteInt32(pcbRead, 0);
for (float i = 0; i < totalblocks; i++)
{
imagestream.Read(buff, bloksize, pcbRead);
if (Marshal.ReadInt32(pcbRead) != bloksize)
{
string err = string.Format
("Failed because Marshal.ReadInt32(pcbRead)
= {0} != bloksize = {1}",
Marshal.ReadInt32(pcbRead), bloksize);
Debug.WriteLine(err);
throw new ApplicationException(err);
}
bw.Write(buff);
if (crtitem.LastBlock <= i)
{
if (enm.MoveNext())
crtitem = enm.Current as IProgressItem;
if (_cancel)
return;
}
if(Update != null)
Update(crtitem, i / totalblocks);
}
return;
}
catch (Exception ex)
{
Debug.WriteLine(string.Format
("Exception in : CreateProgressISOFile {0}", ex.Message));
throw;
}
finally
{
bw.Flush();
bw.Close();
Marshal.FreeHGlobal(pcbRead);
#if DEBUG
tm.Stop();
Debug.WriteLine(string.Format
("Time spent in CreateProgressISOFile: {0} ms",
tm.Elapsed.TotalMilliseconds.ToString("#,#")));
#endif
}
}
else
{
Debug.WriteLine(string.Format("failed because stat.cbSize({0}) !=
totalblocks({1}) * bloksize({2}) ", stat.cbSize,
totalblocks, bloksize));
}
}
Notice the use of the Microsoft provided Other Important RemarksIf you were wondering what the /// Adds a new file only if it's not included
internal bool AddUniqueFile(string path)
{
path = GetPhysicalPath(path);
if (File.Exists(path))
{
bool bexists = false;
List folders = this.FindAll(delegate(FileSystemInfo it)
{ return it is DirectoryInfo; });
//don't add if it is already in an existing folder
if (folders.Count > 0)
{
string filedir = Path.GetDirectoryName(path);
bexists = folders.Exists(delegate(FileSystemInfo it)
{
string folder = it.FullName;
return string.Compare(filedir, 0, folder, 0, folder.Length, true) == 0;
});
if (bexists)
return false;
}
//don't add if it is already in the collection as a file
List files = this.FindAll(delegate(FileSystemInfo it)
{ return it is FileInfo; });
bexists = files.Exists(delegate(FileSystemInfo it)
{
string fname = it.FullName;
return string.Compare(path, 0, fname, 0, fname.Length, true) == 0;
});
if (!bexists)
this.Add(new FileInfo(path));
return !bexists;
}
return false;
}
/// Adds a new folder only if it's not included, strips files that are included in it
internal bool AddUniqueFolder(string path)
{
path = GetPhysicalPath(path);
if (Directory.Exists(path))
{
List folders = this.FindAll(delegate(FileSystemInfo it)
{ return it is DirectoryInfo; });
string dir = path.TrimEnd('\\');
//don't add if it is already in an existing folder
if (folders.Count > 0)
{
bool bexists = folders.Exists(delegate(FileSystemInfo it)
{
string folder = it.FullName;
return string.Compare(dir, 0, folder, 0, folder.Length, true) == 0;
});
if (bexists)
return false;
}
//remove the existing files that are contained in this current folder
List dupfiles = this.FindAll(delegate(FileSystemInfo it)
{
if (it is FileInfo)
{
string filedir = Path.GetDirectoryName(it.FullName);
return string.Compare(path, 0, filedir, 0, path.Length, true) == 0;
}
return false;
});
dupfiles.ForEach(delegate(FileSystemInfo it)
{
bool result = this.Remove(it);
});
this.Add(new DirectoryInfo(dir));
return true;
}
return false;
}
The methods above come in very handy when adding or dropping files and folders on the ‘Select Files’ tab of the application shown in the image below:
How to Use ItOnce the files are selected, on the ‘Build File’ tab, you have the option to create your file image specifying a number of parameters. void AsyncFormattingEvent(object o1, object o2)
{
Invoke(new DiscFormat2Data_EventsHandler(FormattingEvent), new Object[] { o1, o2 });
}
void FormattingEvent(object o1, object o2)
{
IMAPI2.Interop.IProgressItem it = o1 as IMAPI2.Interop.IProgressItem;
int i = (int)(Convert.ToSingle(o2) * 100);
this._progBar.Value = 100 + i;
if (it != null)
this._lblUpdate.Text = string.Format("Formatting {0}", it.Description);
if (!_ckWorker.Checked)
Application.DoEvents();
}
void AsyncRepositoryUpdate(object o1, object o2)
{
Invoke(new DiscFormat2Data_EventsHandler(RepositoryUpdate), new Object[] { o1, o2 });
}
void RepositoryUpdate(object o1, object o2)
{
string file = o1 as string;
long i = Convert.ToInt64(o2);
int pos = (int)((double)_repository.ActualSize /
_repository.SizeBeforeFormatting * 100);
_progBar.Value = pos;
_lblUpdate.Text = string.Format("Adding {0} size = {1}", file, i);
if (!_ckWorker.Checked)
Application.DoEvents();
}
Notice than even when using the Reusing the CodeThe solution has two projects to separate the UI from the classes that actually do the image creation. You can easily reuse this functionality if you add a reference to the ISOImage.dll or to the | ||||||||||||||||||||