Adding a stream to an ISO as a File

1k Views Asked by At

I am using the IMAPI2FS Image Mastering API in Windows, and I'm trying to figure out how to add a stream as a file to the file system image before I generate the ISO.

var fsi = new MsftFileSystemImage();
fsi.ChooseImageDefaultsForMediaType(IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK);
fsi.FileSystemsToCreate = 
    FsiFileSystems.FsiFileSystemISO9660 | FsiFileSystems.FsiFileSystemJoliet;

using (var stream = new MemoryStream())
{
    stream.Write(buffer, 0, bufferSize);

    // Here is where I need to either instantiate an FsiStream and copy
    // stream to it, but I can't figure out how to do this.

    fsi.Root.AddFile(relativePathFromImageRoot, iStreamObject);
}
1

There are 1 best solutions below

0
On BEST ANSWER

You are trying to use the IMAPI2FS type library and are running into a common problem with that library. It was authored rather poorly and is quite difficult to use directly from a .NET program. Most programs that target the api were written in C++ and use the imap2fs.h SDK header file.

The specific issue you are running into here is that the type library importer converted the 2nd argument of AddFile() to FsiStream, a coclass type. It is a type you cannot create, it has the [noncreatable] type library attribute. The type library converter was lead astray, the method actually takes an IStream argument. What you are supposed to do is create your own implementation of IStream and pass an instance of it as the 2nd argument.

This can be worked around with the help of the C# version 4 dynamic keyword so the compiler isn't going to complain about FsiStream. Here is a sample implementation class that implements IStream:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;

[ComVisible(true)]
class FsiStreamImpl : IStream {
    private Stream source;
    private FsiStreamImpl() { }
    public static dynamic Create(Stream from) {
        var stream = new FsiStreamImpl();
        stream.source = from;
        return stream;
    }

    public void Read(byte[] pv, int cb, IntPtr pcbRead) {
        int read = source.Read(pv, 0, cb);
        Marshal.WriteInt32(pcbRead, read);
    }

    public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) {
        long pos = source.Seek(dlibMove, (SeekOrigin)dwOrigin);
        Marshal.WriteInt64(plibNewPosition, pos);
    }

    public void Stat(out STATSTG pstatstg, int grfStatFlag) {
        var stat = new STATSTG();
        stat.type = 2;
        stat.cbSize = source.Length;
        stat.grfMode = 2;
        pstatstg = stat;
    }

    // Methods that we don't have to implement:
    public void Write(byte[] pv, int cb, IntPtr pcbWritten) {
        throw new NotImplementedException();
    }
    public void Clone(out IStream ppstm) {
        throw new NotImplementedException();
    }
    public void Commit(int grfCommitFlags) {
        throw new NotImplementedException();
    }
    public void SetSize(long libNewSize) {
        throw new NotImplementedException();
    }
    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) {
        throw new NotImplementedException();
    }
    public void LockRegion(long libOffset, long cb, int dwLockType) {
        throw new NotImplementedException();
    }
    public void Revert() {
        throw new NotImplementedException();
    }
    public void UnlockRegion(long libOffset, long cb, int dwLockType) {
        throw new NotImplementedException();
    }
}

Now you can write your code like this:

using (var stream = new MemoryStream(buffer))
{
    stream.Write(buffer, 0, bufferSize);
    stream.Position = 0;
    fsi.Root.AddFile(relativePathFromImageRoot, FsiStreamImpl.Create(stream)));
}

Or code like this, what I tested:

using (var file = new FileStream(@"c:\temp\test.txt", FileMode.Open, FileAccess.Read)) {
    fsi.Root.AddFile("test.txt", FsiStreamImpl.Create(file));
}

You are liable to run into some more problems, I only tested the snippet you posted. I should point out this Codeproject.com project, written by a programmer that was battling IMAPI2 as well. He took a much broader approach, a fairly dangerous one, and rewrote the entire type library with hand-crafted C# declarations. He spent a lot of time on it and the support questions focus only on learning how to use IMAPI2 so you're probably okay relying on it.