C# Managed API Sample

The following C# code example illustrates the following functionality: capture; apply; mount; and unmount.

Example

using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;

namespace Microsoft.WimgApi
{
    ///<summary>
    ///Public interface to a WindowsImage object.
    ///</summary>
    public
    interface IImage
    {
        ///<summary>
        ///Gets image information from within a .wim file.
        ///</summary>
        XmlTextReader ImageInformation
        {
            get;
        }

        ///<summary>
        ///Sets image information about an image within a .wim file.
        ///</summary>
        ///<param name="imageInformation">The string being passed in should be in the form of a unicode XML file.
        ///Calling this function replaces any customized image data. To preserve existing XML information, call ImageInformation
        ///and append or edit the desired data.
        ///</param>
        void SetImageInformation(
            string imageInformation
        );

        ///<summary>
        ///Mounts an image in a .wim file to the specified directory.
        ///</summary>
        void Mount(
            string pathToMountTo
        );

        ///<summary>
        ///Retrieves the path to which an image has been mounted.
        ///</summary>
        string MountedPath
        {
            get;
        }

        ///<summary>
        ///Unmounts a mounted image in a .wim file from the specified directory.
        ///</summary>
        ///<param name="commitChanges">Indicates whether changes (if any) to the .wim file should be committed 
        ///before unmounting the .wim file. This flag will have no effect if the .wim file was mounted not to allow edits.
        ///</param>
        void DismountImage(
            bool commitChanges
        );

        ///<summary>
        ///Applies an image to a drive root or to a directory path from a .wim file.
        ///</summary>
        void Apply(
            string pathToApplyTo
        );
    }

    ///<summary>
    ///Class representing a .wim file.
    ///</summary>
    public
    sealed
    class
    WindowsImageContainer : IDisposable
    {
        ///<summary>
        ///Specifies the type of access to the .wim file.
        ///</summary>
        public
        enum
        CreateFileAccess
        {
            ///<summary>
            ///Specifies read-only access to the .wim file.
            ///</summary>
            Read,

            ///<summary>
            ///Specifies write access to the .wim file.
            ///Includes WIM_GENERIC_READ access to enable the apply and append operations to be used with existing images.
            ///</summary>
            Write
        }

        ///<summary>
        ///Specifies which action to take on files that exist and 
        ///which action to take when files do not exist.
        ///</summary>
        public
        enum
        CreateFileMode
        {
            ///<summary>
            ///RESERVED, DO NOT USE!
            ///</summary>
            None = 0,

            ///<summary>
            ///Creates a new .wim file. The function fails if the specified file already exists.
            ///</summary>
            CreateNew = 1,

            ///<summary>
            ///Creates a new .wim file. If the file exists, the function overwrites the file.
            ///</summary>
            CreateAlways = 2,

            ///<summary>
            ///Opens the .wim file. The function fails if the file does not exist.
            ///</summary>
            OpenExisting = 3,

            ///<summary>
            ///Opens the .wim file if it exists. If the file does not exist and the caller requests WIM_GENERIC_WRITE access, the 
            ///function creates the file.
            ///</summary>
            OpenAlways = 4
        }

        ///<summary>
        ///Public constructor to create a WIM object
        ///</summary>
        ///<param name="imageFilePath">Path of .wim file to create or to open.</param>
        ///<param name="mode">Specifies Open, Create, Create/Open disposition of the .wim file.</param>
        ///<param name="access">Specifies access level of Read Only or Write.</param>
        //[CLSCompliant(false)]
        public WindowsImageContainer(string imageFilePath, CreateFileMode mode, CreateFileAccess access)
        {
            CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access);
            if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode))) {
                throw new System.UnauthorizedAccessException(string.Format(CultureInfo.CurrentCulture, 
                                 "Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist."));
            }

            //
            //Imaging DLLs must be in the same directory.
            //
            try {
                m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint) fileAccess, (uint) mode);
                m_WindowsImageFilePath = imageFilePath;
            }
            catch (System.DllNotFoundException ex) {
                throw new System.DllNotFoundException(string.Format(CultureInfo.CurrentCulture, 
                                  "Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException);
            }

            if (!m_ImageContainerHandle.Equals(IntPtr.Zero)) {
                //
                //Set the temporary path so that we can write to an image. This
                //cannot be %TEMP% as it does not exist on Windows PE
                //
                string tempDirectory = System.Environment.GetEnvironmentVariable("systemdrive");
                NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory);

            } else {
                //
                //Throw an exception
                //
                throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                                                                         "Unable to open  the .wim file {0}.", imageFilePath));
            }

            //
            //Finally, we must hook into the events.
            //
            m_MessageCallback = new NativeMethods.MessageCallback(ImageEventMessagePump);
            NativeMethods.RegisterCallback(m_MessageCallback);
        }
        
        ///<summary> Destructor to close the open handles.</summary>
        ~WindowsImageContainer()
        {
            DisposeInner();
        }

        ///<summary>
        ///Release all unmanaged resources
        ///</summary>
        public
        void
        Dispose()
        {
            foreach (WindowsImage wi in m_Images) {
                  if (wi != null) {
                    wi.Dispose();
                  }
            }
            DisposeInner();
            GC.SuppressFinalize(this);
        }

        private
        void
        DisposeInner()
        {
            if (m_ImageContainerHandle != IntPtr.Zero) {
                NativeMethods.CloseHandle(m_ImageContainerHandle);
                m_ImageContainerHandle = IntPtr.Zero;
            }

            if (m_MessageCallback != null) {
                NativeMethods.UnregisterMessageCallback(m_MessageCallback);
                m_MessageCallback = null;
            }
            GC.KeepAlive(this);
        }

        ///<summary>
        ///Used to enumerate the WindowsImage array
        ///</summary>
        public
        IEnumerator
        GetEnumerator(
            )
        {
            return m_Images.GetEnumerator();
        }

        ///<summary>
        ///[] overload, used to enumerate the WindowsImage array. 
        ///</summary>
        public
        IImage
        this[int imageIndex]
        {
            get
            {
                //
                //Delay the loading of images.
                //
                if (imageIndex >= ImageCount) {
                    return null;
                }
                
                if (m_Images == null ) {
                    m_Images = new WindowsImage[ImageCount];
                }
                if (m_Images[imageIndex] == null) {
                    m_Images[imageIndex] = new WindowsImage(m_ImageContainerHandle, m_WindowsImageFilePath, imageIndex + 1);
                }
                GC.KeepAlive(this);
                return m_Images[imageIndex];
            }
            set
            {
                m_Images[imageIndex] = (value as WindowsImage);
            }
        }

        ///<summary>
        ///Retrieve the number of images in the .wim file.
        ///</summary>
        public int ImageCount
        {
            get
            {
                //
                //Verify that there is an image count. If not, get an image count from the GetImageCount function.
                //
                if (m_ImageCount == 0) {  
                    m_ImageCount = NativeMethods.GetImageCount(m_ImageContainerHandle);
                }
                
                GC.KeepAlive(this);
                return m_ImageCount;
            }
        }

        ///<summary>
        ///Capture an image from the root of a drive or from an individual directory.
        ///</summary>
        public void CaptureImage(string pathToCapture)
        {
            //
            //Capture the image.
            //
            IntPtr windowsImageHandle = NativeMethods.CaptureImage(m_ImageContainerHandle, pathToCapture);
            NativeMethods.CloseHandle(windowsImageHandle);
            GC.KeepAlive(this);
        }

        ///<summary>
        ///Default event handler
        ///</summary>
        //[CLSCompliant(false)]
        public delegate void DefaultImageEventHandler(object sender, DefaultImageEventArgs e);
        //public delegate void DefaultImageEventHandler(IntPtr wParam, IntPtr lParam, IntPtr UserData);
        ///<summary>
        ///ProcessFileEvent handler
        ///</summary>
        //[CLSCompliant(false)]
        public delegate void ProcessFileEventHandler(object sender, ProcessFileEventArgs e);
        //public delegate void ProcessFileEventHandler(ProcessFile fileToProcess);

        
        ///<summary>
        ///Indicate an update in the progress of an image application.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler ProgressEvent;        
        ///<summary>
        ///Enable the caller to prevent a file or a directory from being captured or applied.
        ///</summary>
        //[CLSCompliant(false)]
        public event ProcessFileEventHandler ProcessFileEvent;
        ///<summary>
        ///Enable the caller to prevent a file resource from being compressed during a capture.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler CompressEvent;
        ///<summary>
        ///Alert the caller that an error has occurred while capturing or applying an image.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler ErrorEvent;
        ///<summary>
        ///Enable the caller to align a file resource on a particular alignment boundary.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler AlignmentEvent;
        ///<summary>
        ///Enable the caller to align a file resource on a particular alignment boundary.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler SplitEvent;
        ///<summary>
        ///Indicate that volume information is being gathered during an image capture.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler ScanningEvent;
        ///<summary>
        ///Indicate the number of files that will be captured or applied.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler SetRangeEvent;
        ///<summary>
        ///Indicate the number of files that have been captured or applied.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler SetPosEvent;
        ///<summary>
        ///Indicate that a file has been either captured or applied.
        ///</summary>
        //[CLSCompliant(false)]
        public event DefaultImageEventHandler StepItEvent;


        ///<summary>
        ///Event callback to the Wimgapi events
        ///</summary>
        private        
        uint
        ImageEventMessagePump(
            uint MessageId,
            IntPtr wParam,
            IntPtr lParam,
            IntPtr UserData
        )
        {
            uint status = (uint) NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
            DefaultImageEventArgs eventArgs = new DefaultImageEventArgs(wParam, lParam, UserData);

            switch ((ImageEventMessage)MessageId) {

                case ImageEventMessage.Progress:
                    ProgressEvent(this, eventArgs);
                    break;

                case ImageEventMessage.Process:
                    string fileToImage = Marshal.PtrToStringUni(wParam);
                    ProcessFileEventArgs fileToProcess = new ProcessFileEventArgs(fileToImage, lParam);
                    ProcessFileEvent(this, fileToProcess);
                    if (fileToProcess.Abort == true) {
                        status = (uint)ImageEventMessage.Abort;
                    }
                    break;

                case ImageEventMessage.Compress:
                    CompressEvent(this, eventArgs);
                    break;

                case ImageEventMessage.Error:
                    ErrorEvent(this, eventArgs);
                    break;

                case ImageEventMessage.Alignment:
                    AlignmentEvent(this, eventArgs);
                    break;

                case ImageEventMessage.Split:
                    SplitEvent(this, eventArgs);
                    break;

                case ImageEventMessage.Scanning:
                    ScanningEvent(this, eventArgs);
                    break;

                case ImageEventMessage.SetRange:
                    SetRangeEvent(this, eventArgs);
                    break;

                case ImageEventMessage.SetPos:
                    SetPosEvent(this, eventArgs);
                    break;

                case ImageEventMessage.StepIt:
                    StepItEvent(this, eventArgs);
                    break;

                default:
                    break;
            }

            return status;
        }

        ///<summary>
        ///Image inside of a .wim file
        ///</summary>
        private
        class
        WindowsImage : IImage, IDisposable
        {   
            ///<summary>
            ///Public constructor to create an image object from inside a .wim file
            ///</summary>
            public WindowsImage(IntPtr imageContainerHandle, string imageContainerFilePath, int imageIndex)
            {   
                m_ParentWindowsImageHandle = imageContainerHandle;
                m_ParentWindowsImageFilePath = imageContainerFilePath;
                m_Index = imageIndex;

                //
                //Load the image and store the handle.
                //
                m_ImageHandle = NativeMethods.LoadImage(imageContainerHandle, imageIndex);
            }

            ///<summary> Destructor to close open handles.</summary>
            ~WindowsImage()
            {
                DisposeInner();
            }

            ///<summary>
            ///Release all unmanaged resources.
            ///</summary>
            public
            void
            Dispose()
            {
                DisposeInner();
                GC.SuppressFinalize(this);
            }

            private
            void
            DisposeInner()
            {
                //
                //Do not leave any open handles or mounted images.
                //
                if (m_ImageHandle != IntPtr.Zero) {
                    NativeMethods.CloseHandle(m_ImageHandle);
                    m_ImageHandle = IntPtr.Zero;
                }

                if (m_Mounted == true) {
                    //
                    //Never commit changes when destroying this object.
                    //
                    this.DismountImage(false);
                }
                GC.KeepAlive(this);
            }

            ///<summary>
            ///Gets an image information header.
            ///</summary>
            ///<value></value>
            public XmlTextReader ImageInformation
            {
                get
                {
                    //
                    //Always get the image header (even if we have it already), and remove the unicode file marker.
                    //
                    string str = NativeMethods.GetImageInformation(m_ImageHandle).Remove(0, 1);                    
                    GC.KeepAlive(this);

                    return ImageInformationHelper(str);
                }
            }

            ///<summary>
            ///Returns an XmlTextReader for a given node name.
            ///</summary>
            private XmlTextReader ImageInformationHelper(string raw) {

                XmlTextReader xmlTextReader = null;
                
                if (raw != null) {                    
                    XmlDocument xmlDocument = new XmlDocument();
                    try {
                        xmlDocument.LoadXml(raw);
                        
                        //
                        //Look at all nodes.
                        //
                        foreach(XmlNode node in xmlDocument.ChildNodes) {
                            
                            //
                            //Now, find the specified node.
                            //
                            foreach(XmlNode childNode in node) {
                                if 
(String.Equals(childNode.Name, node.ToString(), StringComparison.InvariantCultureIgnoreCase)) {
                                    
                                    StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
                                    XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
                                    
                                    xmlTextWriter.WriteStartElement(childNode.Name);                                    
                                    //
                                    //We are in the specified node. Now, get all the attributes.
                                    //
                                    foreach(XmlAttribute attribute in childNode.Attributes) {
                                        xmlTextWriter.WriteAttributeString(attribute.Name, attribute.InnerXml);
                                    }
                                    xmlTextWriter.WriteEndElement();

                                    xmlTextReader = new XmlTextReader(null, new StringReader(stringWriter.ToString()));
                                }
                            }
                        }
                    }
                    catch (System.Xml.XmlException) {
                        throw new System.Xml.XmlException(string.Format(CultureInfo.CurrentCulture,
                                                                        "Unable to read XML header information from {0}", 
                                                                        this.m_ParentWindowsImageFilePath));
                    }
                }
                return xmlTextReader;
            }

            ///<summary>
            ///Retrieves the path to which an image has been mounted.
            /// </summary>
            public string MountedPath
            {
                get
                {
                    return (m_MountedPath != null) ? m_MountedPath : null;
                }
            }

            ///<summary>
            ///Sets the image information header
            ///</summary>
            ///<returns></returns>
            public void SetImageInformation(string imageInformation)
            {
                //
                //Format the incoming XML so that we can set the header. The XML must:
                //1. Begin with 0xFEFF
                //2. Be contained in <IMAGE></IMAGE> tags
                //
                string formattedXml = String.Format(CultureInfo.InvariantCulture, 
                                                    "{0}{1}{2}{3}",
                                                    UNICODE_FILE_MARKER,
                                                    "<IMAGE>", 
                                                    imageInformation, 
                                                    "</IMAGE>");
                
                NativeMethods.SetImageInformation(m_ImageHandle, formattedXml);
                GC.KeepAlive(this);
            }

            ///<summary>
            ///Mounts an image to a directory.
            ///</summary>
            ///<returns></returns>
            public void Mount(string pathToMountTo)
            {
                //
                //Mount the image
                //
                m_MountedPath = pathToMountTo;
                NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index);
                m_Mounted = true;
            }

            ///<summary>
            ///Unmounts an image from a directory.
            ///</summary>
            ///<returns></returns>
            public void DismountImage(bool commitChanges)
            {
                if (m_Mounted == true) {
                    NativeMethods.DismountImage(m_MountedPath, m_ParentWindowsImageFilePath, m_Index, commitChanges);
                }
            }

            ///<summary>
            ///Applies an image to a drive root or to a directory path.
            ///</summary>
            ///<returns></returns>
            public void Apply(string pathToApplyTo)
            {
                NativeMethods.ApplyImage(m_ImageHandle, pathToApplyTo);
                GC.KeepAlive(this);
            }

            private IntPtr m_ParentWindowsImageHandle = IntPtr.Zero;    //.wim file handle
            private string m_ParentWindowsImageFilePath;                //Path to .wim file

            private IntPtr m_ImageHandle = IntPtr.Zero;                 //Image handle
            private int m_Index;                                        //Index of image
            
            private string m_MountedPath;                               //Path to which the image has been mounted
            private bool m_Mounted;                                        //A Boolean set to true if image has been mounted

            //
            //DO NOT CHANGE! This controls the format of the image header
            //and it must be present.
            //
            private const string UNICODE_FILE_MARKER = "\uFEFF";
        }

        ///<summary>
        ///Interop to Wimgapi.dll
        ///</summary>
        private
        class
        NativeMethods
        {
            ///<summary>
            ///Private null constructor
            ///</summary>
            private
            NativeMethods() { }

            [DllImport("Wimgapi.dll", ExactSpelling = true, 
                       EntryPoint = "WIMCreateFile", 
                       CallingConvention = CallingConvention.StdCall, 
                       SetLastError = true)]
            private static extern
            IntPtr
            WimCreateFile(
                [MarshalAs(UnmanagedType.LPWStr)] string WimPath,
                uint DesiredAccess,
                uint CreationDisposition,
                uint FlagsAndAttributes,
                uint CompressionType,
                out IntPtr CreationResult
            );

            ///<summary>
            ///Creates a new .wim file or opens an existing .wim file.
            ///</summary>
            ///<param name="imageFile">Path to the .wim file to open or to create.</param>
            ///<param name="access">Specifies the file access to grant the file.</param>
            ///<param name="mode">Specifies the mode in which the file should be opened or created.</param>
            ///<returns>If the function succeeds, the return value is an open handle to the specified image file.
            ///If the function fails, the return value is NULL.</returns>
            public
            static
            IntPtr
            CreateFile(string imageFile, uint access, uint mode)
            {
                IntPtr creationResult = IntPtr.Zero;
                IntPtr windowsImageHandle = IntPtr.Zero;
                int rc = -1;

                windowsImageHandle = NativeMethods.WimCreateFile(imageFile, access, mode, 0, 0, out creationResult);
                rc = Marshal.GetLastWin32Error();
                if (windowsImageHandle == IntPtr.Zero) {
                    //
                    //Function failed. Throw an exception
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                                                                             "Unable to open/create .wim file {0}. Error = {1}", 
                                                                             imageFile, rc));
                }

                return windowsImageHandle;
            }

            [DllImport("Wimgapi.dll", 
                       ExactSpelling = true, 
                       EntryPoint = "WIMCloseHandle", 
                       CallingConvention = CallingConvention.StdCall, 
                       SetLastError = true)]
            private static extern
            bool
            WimCloseHandle(
                IntPtr Handle
            );

            ///<summary>
            ///Closes an open .wim file or an image handle.
            ///</summary>
            ///<param name="handle">Handle to an open imaging-based object.</param>
            ///<returns>If the function succeeds, the return value is nonzero.
            ///If the function fails, the return value is zero. </returns>
            public
            static
            void
            CloseHandle(IntPtr handle)
            {
                bool status = NativeMethods.WimCloseHandle(handle);
                int rc = Marshal.GetLastWin32Error();
                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                                                                         "Unable to close image handle. Error = {0}", rc));
                }
            }

            [DllImport("Wimgapi.dll", 
                       ExactSpelling = true,
                       EntryPoint = "WIMSetTemporaryPath",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimSetTemporaryPath(
                IntPtr Handle,
                [MarshalAs(UnmanagedType.LPWStr)] string TemporaryPath
            );

            ///<summary>
            ///Sets the location where temporary imaging files are stored.
            ///</summary>
            ///<param name="handle">Handle to a .wim file returned by CreateFile</param>
            ///<param name="temporaryPath">String value of path to set as a temporary location.</param>
            ///<returns>If the function succeeds, the return value is nonzero.
            ///If the function fails, the return value is NULL.</returns>
            public
            static
            void
            SetTemporaryPath(IntPtr handle, string temporaryPath)
            {
                bool status = NativeMethods.WimSetTemporaryPath(handle, temporaryPath);
                int rc = Marshal.GetLastWin32Error();
                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to set temporary path. Error = {0}", rc));
                }
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMLoadImage",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            IntPtr
            WimLoadImage(
                IntPtr Handle,
                uint ImageIndex
            );

            ///<summary>
            ///Loads a volume image from within a .wim file.
            ///</summary>
            ///<param name="handle">Wim handle.</param>
            ///<param name="imageIndex">Index of the image to load.</param>
            ///<returns>If the function succeeds, the return value is a handle to an object representing the volume image. 
            ///If the function fails, the return value is NULL. </returns>
            public
            static
            IntPtr
            LoadImage(IntPtr handle, int imageIndex)
            {
                //Load the image data based on the .wim handle
                //
                IntPtr hWim = NativeMethods.WimLoadImage(handle, (uint)imageIndex);
                int rc = Marshal.GetLastWin32Error();
                if (hWim == IntPtr.Zero) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to load image. Error = {0}", rc));
                }

                return hWim;

            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMCaptureImage",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            IntPtr
            WimCaptureImage(
                IntPtr Handle,
                [MarshalAs(UnmanagedType.LPWStr)] string Path,
                uint CaptureFlags
            );

            ///<summary>
            ///Captures an image from a drive root or from a directory path and stores it in an image file.
            ///</summary>
            ///<param name="handle">Handle to a .wim file returned by CreateFile.</param>
            ///<param name="path">Drive root or directory path from where the image data will be captured.</param>
            ///<returns>Handle to an object representing the volume image. If the function fails, the return value is NULL.</returns>
            public
            static
            IntPtr
            CaptureImage(IntPtr handle, string path)
            {   
                IntPtr hImage = NativeMethods.WimCaptureImage(handle, path, 0);
                int rc = Marshal.GetLastWin32Error();
                if (hImage == IntPtr.Zero) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Failed to capture image from {0}. Error = {1}", path, rc));
                }
                return hImage;
            }

            ///<summary>
            ///Gets the number of volume images stored in a .wim file.
            ///</summary>
            ///<param name="Handle">Handle to a .wim file returned by CreateFile.</param>
            ///<returns>The number of images within the .wim file. </returns>
            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMGetImageCount",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            int
            WimGetImageCount(
                IntPtr Handle
            );

            ///<summary>
            ///Returns the number of volume images stored in a .wim file.
            ///</summary>
            ///<param name="windowsImageHandle">Handle to a .wim file returned by WIMCreateFile.</param>
            ///<returns>The return value is the number of images within the .wim file.
            ///If this value is zero, then the .wim file is invalid or does not contain any images that can be applied.
            ///</returns>
            public
            static
            int
            GetImageCount(IntPtr windowsImageHandle)
            {
                int count = NativeMethods.WimGetImageCount(windowsImageHandle);
                int rc = Marshal.GetLastWin32Error();
                if (count == -1) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to get image count. Error = {0}", rc));
                }

                return count;
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMMountImage",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimMountImage(
                [MarshalAs(UnmanagedType.LPWStr)] string MountPath,
                [MarshalAs(UnmanagedType.LPWStr)] string WimFileName,
                uint ImageIndex,
                [MarshalAs(UnmanagedType.LPWStr)] string TemporaryPath

            );

            ///<summary>
            ///Mounts an image in a .wim file to the specified directory.
            ///</summary>
            ///<returns>Returns TRUE and sets the LastError to ERROR_SUCCESS.
            ///Returns FALSE in case of a failure and the LastError is set to the appropriate Win32 error value.
            ///</returns>
            public
            static
            void
            MountImage(string mountPath, string windowsImageFileName, int imageIndex)
            {
                bool status = false;
                int rc;

                try {
                    status = NativeMethods.WimMountImage(mountPath, 
                                                         windowsImageFileName, 
                                                         (uint)imageIndex, 
                                                         System.Environment.GetEnvironmentVariable("temp"));
                    rc = Marshal.GetLastWin32Error();
                }
                catch (System.StackOverflowException) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to mount image {0} to {1}.", windowsImageFileName, mountPath));
                }
                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to mount image {0} to {1}. Error = {2}", 
                                                                             windowsImageFileName, mountPath, rc));
                }
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMApplyImage",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimApplyImage(
                IntPtr Handle,
                [MarshalAs(UnmanagedType.LPWStr)] string Path,
                uint Flags
            );

            ///<summary>
            ///Applies an image to a drive root or to a directory path from a .wim file.
            ///</summary>
            ///<returns>If the function succeeds, the return value is nonzero.
            ///If the function fails, the return value is zero</returns>
            public
            static
            void
            ApplyImage(IntPtr imageHandle, string applicationPath)
            {
                //
                //Call WimApplyImage always with the Index flag for performance reasons.
                //
                bool status = NativeMethods.WimApplyImage(imageHandle, applicationPath, NativeMethods.WIM_FLAG_INDEX);
                int rc = Marshal.GetLastWin32Error();
                if (status == false) {
                    //
                    //Throw an exception
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to apply image to {0}. Error = {1}", applicationPath, rc));
                }
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMGetImageInformation",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimGetImageInformation(
                IntPtr Handle,
                out IntPtr ImageInfo,
                out IntPtr SizeOfImageInfo
            );

            ///<summary>
            ///Returns information about an image within the .wim file.
            ///</summary>
            ///<param name="handle">Handle returned by CreateImage, LoadImage</param>
            ///<returns>If the function succeeds, the return value is nonzero.
            ///If the function fails, the return value is zero.
            ///</returns>
            public
            static
            string
            GetImageInformation(IntPtr handle)
            {
                IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero;
                bool status;

                status = NativeMethods.WimGetImageInformation(handle, out info, out sizeOfInfo);
                int rc = Marshal.GetLastWin32Error();

                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to get image information. Error = {0}", rc));
                }
                string s = Marshal.PtrToStringUni(info);

                //If the function succeeds, return the pointer to the string. Otherwise, return NULL.
                //
                return s;
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMSetImageInformation",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimSetImageInformation(
                IntPtr Handle,
                IntPtr ImageInfo,
                uint SizeOfImageInfo
            );

            ///<summary>
            ///Stores information about an image within the .wim file.
            ///</summary>
            ///<param name="handle">Handle returned by CreateImage, LoadImage</param>
            ///<param name="imageInfo">String containing the unicode XML data to set.</param>
            ///<returns>If the function succeeds, the return value is nonzero.
            ///If the function fails, the return value is zero. </returns>
            public
            static
            void
            SetImageInformation(IntPtr handle, string imageInfo)
            {
                //Create a byte array for the stream, allocate some unmanaged memory, and then copy the bytes to the unmanaged memory.
                //
                byte[] byteBuffer = Encoding.Unicode.GetBytes(imageInfo);
                int byteBufferSize = byteBuffer.Length;
                IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize);
                Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize);

                bool status = NativeMethods.WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
                int rc = Marshal.GetLastWin32Error();
                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to set image information. Error = {0}", rc));
                }
            }

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMUnmountImage",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimUnmountImage(
                [MarshalAs(UnmanagedType.LPWStr)] string MountPath,
                [MarshalAs(UnmanagedType.LPWStr)] string WimFileName,
                uint ImageIndex,
                bool CommitChanges
            );

            ///<summary>
            ///Unmounts a mounted image in a .wim file from the specified directory.
            ///</summary>
            ///<returns>Returns TRUE and sets the LastError to ERROR_SUCCESS. Returns FALSE in case of a failure and the LastError is set
            ///to the appropriate Win32 error value.</returns>
            public
            static
            void
            DismountImage(string mountPath, string wimdowsImageFileName, int imageIndex, bool commitChanges)
            {
                bool status = false;
                int rc;

                try {
                    status = NativeMethods.WimUnmountImage(mountPath, wimdowsImageFileName, (uint) imageIndex, commitChanges);
                    rc = Marshal.GetLastWin32Error();
                }
                catch (System.StackOverflowException ex) {
                    //
                    //Throw an exception.
                    //
                    throw new System.StackOverflowException(string.Format(CultureInfo.CurrentCulture,
                                                                          "Unable to unmount image {0} from {1}.", wimdowsImageFileName, mountPath),
                                                                          ex.InnerException);
                }
                if (status == false) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, 
                                                                             "Unable to unmount image {0} from {1}. Error = {2}", 
                                                                             wimdowsImageFileName, mountPath, rc));
                }
            }

            ///<summary>
            ///User-defined function used with the RegisterMessageCallback or UnregisterMessageCallback function.
            ///</summary>
            ///<param name="MessageId">Specifies the message being sent.</param>
            ///<param name="wParam">Specifies additional message information. The contents of this parameter depend on the value of the
            ///MessageId parameter.</param>
            ///<param name="lParam">Specifies additional message information. The contents of this parameter depend on the value of the
            ///MessageId parameter.</param>
            ///<param name="UserData">Specifies the user-defined value passed to RegisterCallback.</param>
            ///<returns>
            ///To indicate success and to enable other subscribers to process the message return WIM_MSG_SUCCESS.
            ///To prevent other subscribers from receiving the message, return WIM_MSG_DONE.
            ///To cancel the application or capture of an image, return WIM_MSG_ABORT_IMAGE when handling the WIM_MSG_PROCESS message.
            ///</returns>
            public
            delegate
            uint
            MessageCallback(
                uint MessageId,
                IntPtr wParam,
                IntPtr lParam,
                IntPtr UserData
            );

            [DllImport("Wimgapi.dll",
                       ExactSpelling = true,
                       EntryPoint = "WIMRegisterMessageCallback",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            uint
            WimRegisterMessageCallback(
                IntPtr  hWim,
                MessageCallback MessageProc,
                IntPtr ImageInfo
            );

            ///<summary>
            ///Registers a function to be called with imaging-specific data.
            ///</summary>
            public
            static
            void
            RegisterCallback(MessageCallback callback)
            {
                uint callbackZeroBasedIndex = NativeMethods.WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
                int rc = Marshal.GetLastWin32Error();
                if (rc != 0) {
                    //
                    //Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to register message callback."));
                }
            }

            [DllImport("Wimgapi.dll", 
                       ExactSpelling = true,
                       EntryPoint = "WIMUnregisterMessageCallback",
                       CallingConvention = CallingConvention.StdCall,
                       SetLastError = true)]
            private static extern
            bool
            WimUnregisterMessageCallback(
                IntPtr  hWim,
                MessageCallback MessageProc
            );

            ///<summary>
            ///Unregisters a function from being called with imaging-specific data.
            ///</summary>
            ///<param name="registeredCallback">The Callback function to be unregistered.</param>
            public
            static
            void
            UnregisterMessageCallback(MessageCallback registeredCallback)
            {
                bool status = NativeMethods.WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
                int rc = Marshal.GetLastWin32Error();
                if (status != true) {
                    //
                    // Throw an exception.
                    //
                    throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to unregister message callback."));
                }
            }

            private const uint WM_APP = 0x8000;
            
            ///<summary>
            ///Imaging Messages
            ///</summary>
            public enum WIMMessage: uint
            {   
                WIM_MSG = WM_APP + 0x1476,                
                WIM_MSG_TEXT,
                ///<summary>
                ///Indicates an update in the progress of an image application.
                ///</summary>
                WIM_MSG_PROGRESS,
                ///<summary>
                ///Enables the caller to prevent a file or a directory from being captured or applied.
                ///</summary>
                WIM_MSG_PROCESS,
                ///<summary>
                ///Indicates that volume information is being gathered during an image capture.
                ///</summary>
                WIM_MSG_SCANNING,
                ///<summary>
                ///Indicates the number of files that will be captured or applied.
                ///</summary>
                WIM_MSG_SETRANGE,
                ///<summary>
                ///Indicates the number of files that have been captured or applied.
                ///</summary>
                WIM_MSG_SETPOS,
                ///<summary>
                ///Indicates that a file has been either captured or applied.
                ///</summary>
                WIM_MSG_STEPIT,
                ///<summary>
                ///Enables the caller to prevent a file resource from being compressed during a capture.
                ///</summary>
                WIM_MSG_COMPRESS,
                ///<summary>
                ///Alerts the caller that an error has occurred while capturing or applying an image.
                ///</summary>
                WIM_MSG_ERROR,
                ///<summary>
                ///Enables the caller to align a file resource on a particular alignment boundary.
                ///</summary>
                WIM_MSG_ALIGNMENT,
                WIM_MSG_RETRY,
                ///<summary>
                ///Enables the caller to align a file resource on a particular alignment boundary.
                ///</summary>
                WIM_MSG_SPLIT,
                WIM_MSG_SUCCESS = 0x00000000,                
                WIM_MSG_ABORT_IMAGE = 0xFFFFFFFF
            };

            ///<summary>
            ///The image capture will do a byte-by-byte verification of single instance files.
            ///</summary>
            public const uint WIM_FLAG_VERIFY = 0x00000002;
            ///<summary>
            ///Specifies that the image is to be sequentially read for caching or performance purposes.
            ///</summary>
            public const uint WIM_FLAG_INDEX = 0x00000004;

        }

        ///<summary>
        ///Maps CreateFileAccess to CreateFileAccessPrivate
        ///</summary>
        ///
        private
        CreateFileAccessPrivate
        GetMappedFileAccess(CreateFileAccess access)
        {
            //
            //Map the file access specified from an int to a uint.
            //
            CreateFileAccessPrivate fileAccess;
            switch (access)
            {
                case CreateFileAccess.Read:
                    fileAccess = CreateFileAccessPrivate.Read;
                    break;
                case CreateFileAccess.Write:
                    fileAccess = CreateFileAccessPrivate.Write;
                    break;
                default:
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No file access level specified."));
            }
            return fileAccess;
        }

        //[CLSCompliant(false)]
        [FlagsAttribute]
        private
        enum
        CreateFileAccessPrivate : uint
        {
            ///<summary>
            ///Mapping from CreateFileAccess.Read
            ///</summary>
            Read = 0x80000000,
            ///<summary>
            ///Mapping from CreateFileAccess.Write
            ///</summary>
            Write = 0x40000000
        }

        ///<summary>
        ///Image event messages.
        ///</summary>
        //[CLSCompliant(false)]
        private
        enum
        ImageEventMessage : uint
        {
            ///<summary>
            ///Enables the caller to prevent a file or a directory from being captured or applied.
            ///</summary>
            Progress = NativeMethods.WIMMessage.WIM_MSG_PROGRESS,
            ///<summary>
            ///Notification sent to enable the caller to prevent a file or a directory from being captured or applied.
            ///To prevent a file or a directory from being captured or applied, call WindowsImageContainer.SkipFile().
            ///</summary>
            Process = NativeMethods.WIMMessage.WIM_MSG_PROCESS,
            ///<summary>
            ///Enables the caller to prevent a file resource from being compressed during a capture.
            ///</summary>
            Compress = NativeMethods.WIMMessage.WIM_MSG_COMPRESS,
            ///<summary>
            ///Alerts the caller that an error has occurred while capturing or applying an image.
            ///</summary>
            Error = NativeMethods.WIMMessage.WIM_MSG_ERROR,
            ///<summary>
            ///Enables the caller to align a file resource on a particular alignment boundary.
            ///</summary>
            Alignment = NativeMethods.WIMMessage.WIM_MSG_ALIGNMENT,
            ///<summary>
            ///Enables the caller to align a file resource on a particular alignment boundary.
            ///</summary>
            Split = NativeMethods.WIMMessage.WIM_MSG_SPLIT,
            ///<summary>
            ///Indicates that volume information is being gathered during an image capture.
            ///</summary>
            Scanning = NativeMethods.WIMMessage.WIM_MSG_SCANNING,
            ///<summary>
            ///Indicates the number of files that will be captured or applied.
            ///</summary>
            SetRange = NativeMethods.WIMMessage.WIM_MSG_SETRANGE,
            ///<summary>
            ///Indicates the number of files that have been captured or applied.
            /// </summary>
            SetPos = NativeMethods.WIMMessage.WIM_MSG_SETPOS,
            ///<summary>
            ///Indicates that a file has been either captured or applied.
            ///</summary>
            StepIt = NativeMethods.WIMMessage.WIM_MSG_STEPIT,
            ///<summary>
            ///Success.
            ///</summary>
            Success = NativeMethods.WIMMessage.WIM_MSG_SUCCESS,
            ///<summary>
            ///Abort.
            ///</summary>
            Abort = NativeMethods.WIMMessage.WIM_MSG_ABORT_IMAGE
        }

        //
        //WindowsImageContainer Member Data
        //
        private IntPtr m_ImageContainerHandle;  //Handle to the .wim file
        private string m_WindowsImageFilePath;  //Path to the .wim file
        
        private WindowsImage[] m_Images;        //Array of image objects inside a .wim file
        private int m_ImageCount;               //Number of images inside a .wim file

        //
        //DO NOT CHANGE!
        //
        private static NativeMethods.MessageCallback m_MessageCallback;
    }

    ///<summary>
    ///Describes the file that is being processed for the ProcessFileEvent.
    ///</summary>
    public
    class
    DefaultImageEventArgs : EventArgs
    {
        ///<summary>
        ///Default constructor.
        ///</summary>
        public
        DefaultImageEventArgs(IntPtr wideParameter, IntPtr leftParameter, IntPtr userData)
        {
            m_wParam = wideParameter;
            m_lParam = leftParameter;
            m_UserData = userData;


        }
        ///<summary>
        ///wParam
        ///</summary>
        public IntPtr WideParameter
        {
            get
            {
                return m_wParam;
            }
        }
        ///<summary>
        ///lParam
        ///</summary>
        public IntPtr LeftParameter
        {
            get
            {
                return m_lParam;
            }
        }
        ///<summary>
        ///UserData
        ///</summary>
        public IntPtr UserData
        {
            get
            {
                return m_UserData;
            }
        }

        private IntPtr m_wParam;
        private IntPtr m_lParam;
        private IntPtr m_UserData;
    }

    ///<summary>
    ///Describes the file that is being processed for the ProcessFileEvent.
    ///</summary>
    public
    class
    ProcessFileEventArgs : EventArgs
    {
        ///<summary>
        ///Default constructor.
        ///</summary>
        ///<param name="file">Fully qualified path and file name. For example: c:\file.sys.</param>
        ///<param name="skipFileFlag">Default is false - skip file and continue.
        ///Set to true to abort the entire image capture.</param>
        public
        ProcessFileEventArgs(string file, IntPtr skipFileFlag)
        {
            m_FilePath = file;
            m_SkipFileFlag = skipFileFlag;
        }

        ///<summary>
        ///Skip file from being imaged.
        ///</summary>
        public
        void
        SkipFile()
        {
            byte[] byteBuffer = {
                    0
                };
            int byteBufferSize = byteBuffer.Length;
            Marshal.Copy(byteBuffer, 0, m_SkipFileFlag, byteBufferSize);
        }

        ///<summary>
        ///Fully qualified path and file name.
        ///</summary>
        public string FilePath
        {
            get
            {
                string stringToReturn = "";
                if (m_FilePath != null) {
                    stringToReturn = m_FilePath;
                }
                return stringToReturn;
            }
        }

        ///<summary>
        ///Flag that indicates if the entire image capture should be aborted.
        ///Default is false - skip file and continue. Setting to true will
        ///abort the entire image capture.
        ///</summary>
        public bool Abort
        {
            set
            {
                m_Abort = value;
            }

            get
            {
                return m_Abort;
            }
        }
        private string m_FilePath;
        private bool m_Abort;
        private IntPtr m_SkipFileFlag;

    }
}

See Also

Concepts

Imaging Reference