Reporting Progress

Aug 23, 2013 at 11:12 PM
Can someone give an example on how to output progress applying a WIM? I figured out how to apply the image but I just can't figure out the progress reporting piece.
    Public Sub applyimage()
        Dim hwim = WimgApi.CreateFile("C:\temp\splitwim.swm", WimFileAccess.Read, WimCreationDisposition.OpenExisting, WimCreateFileOptions.None, WimCompressionType.None)
        WimgApi.SetTemporaryPath(hwim, "c:\temp\")
        WimgApi.SetReferenceFile(hwim, "c:\temp\splitwim2.swm", WimSetReferenceMode.Append, WimSetReferenceOptions.None)
        WimgApi.SetReferenceFile(hwim, "c:\temp\splitwim3.swm", WimSetReferenceMode.Append, WimSetReferenceOptions.None)
        Dim hwim2 = WimgApi.LoadImage(hwim, "1") '
        WimgApi.ApplyImage(hwim2, path:="c:\temp\split\", options:=WimApplyImageOptions.None)

    End Sub
thanks,
Coordinator
Aug 24, 2013 at 11:14 PM
I've typed up a quick sample for you at Using the message callback functionality for recieving progress and other information.

Let me know if you need more help.
Aug 25, 2013 at 11:49 AM
That example works great thank you so much. Exactly what I was looking for.

A couple questions though.
  1. Does the capture process allow for progress output? I thought I had read somewhere before that it wasn't built in to capture process.
  2. This I haven't really looked at but at the end of process does it return a success code that can be verified or is the lack of an error code good enough to assume the process was successful?
Again thank you so much this is perfect.
Aug 25, 2013 at 7:43 PM
I thought of one more thing. I saw your abort option but no matter what I try I can't make it abort the process. I must be doing something wrong.
Aug 26, 2013 at 3:31 PM
I think I figured it out. The cancel process didn't work because my test WIM file had a very large file in it that couldn't close until it completed. Once I used a wim file with smaller files it cancelled properly.

As for the other 2 questions. I got the capture process working and I think the lack of an error is good enough.
Coordinator
Aug 26, 2013 at 4:23 PM
The capture process receives a bunch of messages including Process (which lets you leave files out), Progress, etc. In your message callback you can log each message and see them all.

Each operation will throw a Win32Exception if anything fails. Generally you should do something like:
try
{
  // Do something
}
catch(Win32Exception exception)
{
    // An error occurred
}
finally
{
    // Clean up
}
You can only abort after any message is sent. Progress messages are sent periodically but you're applying a large file you might not get a message right away to abort. There is a message sent periodically querying for an abort. http://msdn.microsoft.com/en-us/library/windows/desktop/dd851919.aspx. Keep in mind if your WIM contains 1,000 files, your message callback is called more than 1,000 times. It gets called for every file, progress, query abort, info, warnings, etc. You can cancel during any message like this:
// Some other thread can set this to true and the message callback will check its value and abort a WIM operation
//
private static bool cancel = false;

private static WimMessageResult MyCallbackMethod(WimMessageType messageType, object message, object userData)
{
    switch (messageType)
    {
        // Check for specific messages and do stuff
    }

    // Depending on what this method returns, the WIMGAPI will continue or cancel.
    //
    // Return WimMessageResult.Abort to cancel.  In this case we return Success so WIMGAPI keeps going
        
    if(cancel)
    {
        return WimMessageResult.Abort;
    }

    return WimMessageResult.Success;
}
Aug 26, 2013 at 10:59 PM
Edited Aug 26, 2013 at 11:16 PM
Thanks I was able to get that piece working now.

Now I can't seem to get mount to work. It looks like it should use the same format as all the other functions but I always get the handle is invalid.

I get 2 error codes.

[2232] ImageStubMountDirectory:(296): The handle is invalid.
[2232] WIMMountImageHandle:(932): The handle is invalid.
    Public Sub MountImage()
        Try
            Using wimHandle As WimHandle = WimgApi.CreateFile("c:\temp\test.wim", WimFileAccess.Mount, _
        WimCreationDisposition.OpenExisting, WimCreateFileOptions.None, WimCompressionType.None)
                ' Always set a temporary path
                '
                WimgApi.SetTemporaryPath(wimHandle, Environment.GetEnvironmentVariable("TEMP"))
                WimgApi.RegisterLogFile("error.log")
                ' Register a method to be called while actions are performed by WIMGAPi for this .wim file
                '
                WimgApi.RegisterMessageCallback(wimHandle, AddressOf MyCallbackMethod)

                Try
                    ' Get a handle to the first image in the .wim file

                    Using imageHandle As WimHandle = WimgApi.LoadImage(wimHandle, 1)
              

                        WimgApi.MountImage(imageHandle, "c:\temp\mount3", WimMountImageOptions.ReadOnly)

                    End Using
                Finally
                    ' Be sure to unregister the callback method
                    '

                    WimgApi.UnregisterMessageCallback(wimHandle, AddressOf MyCallbackMethod)
                End Try
            End Using
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
Coordinator
Aug 26, 2013 at 11:53 PM
Yeah that is a common issue, you need to specify Read and Mount access to the WIM.
Using wimHandle As WimHandle = WimgApi.CreateFile("d:\temp\winpe.wim", _
                                                    WimFileAccess.Read Or WimFileAccess.Mount, _
                                                    WimCreationDisposition.OpenExisting, _
                                                    WimCreateFileOptions.None, _
                                                    WimCompressionType.None)
You can see a native example which is almost the same as using my wrapper here: Mount and Unmount an Image
Aug 27, 2013 at 12:28 AM
Wow thanks. I had seen that but I was thinking it was one or the other but now it works. So simple.
Aug 27, 2013 at 2:44 PM
Is there a trick to setting the image information as well?
 WimgApi.SetImageInformation(wimHandle, <IMAGE><NAME>new_name</NAME></IMAGE>)
This is what I though it should be but it comes back with an error.
Coordinator
Aug 27, 2013 at 7:28 PM
To ensure type safety, GetImageInformation and SetImageInformation use Xml objects like XmlDocument (or anything that implements IXPathNavigable).
Dim xmlDocument As New XmlDocument()

xmlDocument.LoadXml("<IMAGE><NAME>new_name</NAME></IMAGE>")

WimgApi.SetImageInformation(wimHandle, xmlDocument)
That way you can get the info as Xml objects, add elements, attributes etc, and set the info back. If both methods used strings, most people would just parse it as xml, do their magic and convert it back to a string so I decided to just encapsulate it in my wrapper.
Aug 27, 2013 at 10:44 PM
Thanks I was over complicating it. I also discovered I was putting in the wrong spot I needed to use ImageHandle not wimHandle.

I was looking over the native functions on MSDN and I see a function for WimGetMountedImages is this function not part of your wrapper?
Coordinator
Aug 28, 2013 at 6:41 AM
According to the MSDN documentation (http://msdn.microsoft.com/en-us/library/windows/desktop/dd851928.aspx)

WIMGetMountedImages

Returns a list of images that are currently mounted. This function has been superseded by WIMGetMountedImageInfo.
So I only implemented WIMGetMountedImageInfo
/// <summary>
/// Gets a <see cref="WimMountInfoCollection"/> containing <see cref="WimMountInfo"/> objects that represent a list of images that are currently mounted.
/// </summary>
/// <returns>A <see cref="WimMountInfoCollection"/> containing <see cref="WimMountInfo"/> objects.</returns>
/// <exception cref="Win32Exception">The Windows® Imaging API reported a failure.</exception>
public static WimMountInfoCollection GetMountedImageInfo()
Aug 28, 2013 at 9:22 PM
Ah I see that now. Looking over the GetMountedImageInfo what type of collection does it return. I'm not seeing how you parse the data it returns.
Coordinator
Aug 29, 2013 at 6:23 AM
Dim MountedImages = WimgApi.GetMountedImageInfo()

Console.WriteLine("There are {0} mounted images", MountedImages.Count)

For Each MountedImage As WimMountInfo In MountedImages
    Console.WriteLine("ImageIndex: {0}", MountedImage.ImageIndex)
    Console.WriteLine("MountPath: {0}", MountedImage.MountPath)
    Console.WriteLine("Path: {0}", MountedImage.Path)
    Console.WriteLine("ReadOnly: {0}", MountedImage.ReadOnly)
    Console.WriteLine("State: {0}", MountedImage.State)
    Console.WriteLine("-------------------------------------------------")
Next
Aug 29, 2013 at 1:44 PM
Edited Aug 29, 2013 at 3:49 PM
Thanks again.

I don't think the MountImage.ReadOnly is working correctly though. It always comes back true even if the the image was mounted read and write.


Update...

I worked around it by getting the MountImage.State which correctly show Read Write.
Coordinator
Aug 29, 2013 at 4:24 PM
Yeah sorry that is bug. I've fixed it, checked it in, and uploaded a new release.

Issue: WimMountInfo.ReadOnly property doesn't work properly

Changeset: 27056

Release: 1.0.01

Thanks for finding this, please feel free to open any other bugs if you find them.
Aug 29, 2013 at 5:55 PM
Thanks for the fix I just tested it and it looks good.