jeffkl / ManagedWimgApi

A managed wrapper for the native Windows Imaging API (WIMGAPI).
MIT License
36 stars 12 forks source link

handling cancellation during ApplyImage #34

Open PCAssistSoftware opened 5 years ago

PCAssistSoftware commented 5 years ago

hi

When creating an image I can handle cancel button and abort the process

But for ApplyImage I cannot get it to work, and even if I click Stop Debugging in Visual Studio it freezes up the system until I reboot or disconnect drive I am writing too

jeffkl commented 5 years ago

You'll need your progress callback to return WimMessageResult.Abort like this unit test. Depending on your application, you'll need to implement IDisposable which should get called when your process is killed.

PCAssistSoftware commented 5 years ago

Hi Jeff I have spent hours today using that exact unit test as an example and I just can't get it to work - had no problems with handling it for capture, it seems apply is different?

PCAssistSoftware commented 5 years ago

Okay, using example you posted here = https://github.com/jeffkl/ManagedWimgApi/wiki/Message-Callbacks

I did the same as I did for capture and added

case WimMessageType.QueryAbort
      Return WimMessageResult.Abort

but unlike for "capture" - it seems QueryAbort never gets called for "apply"

PCAssistSoftware commented 5 years ago

so instead I tried it under case WinMessageType.Process just as a test and at least here it does then Return WimMessageResult.Abort

I then catch the "OperationCanceledException" which now fires - but even using your example it doesn't stop the "applying" of the image, it just locks up the whole interface and Visual Studio gives following error if I click stop debugging

System.ComponentModel.Win32Exception: 'The process cannot access the file because it is being used by another process'

System.ComponentModel.Win32Exception HResult=0x80004005 Message=The process cannot access the file because it is being used by another process Source=Microsoft.Wim StackTrace: at Microsoft.Wim.WimHandle.ReleaseHandle() at System.Runtime.InteropServices.SafeHandle.InternalFinalize() at System.Runtime.InteropServices.SafeHandle.Finalize()

nothing will then bring system back to life unless I unplug drive I am writing too, or wait for it to finish the "apply"

jeffkl commented 5 years ago

I wrote a simple console application with cancellation and it appears to work with my simple test image. Debugging in Visual Studio worked if I stopped it as well.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Wim;

namespace ConsoleApplication
{
    internal  static class Program
    {
        private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();

        public static int Main(string[] args)
        {
            Console.CancelKeyPress += (sender, eventArgs) =>
            {
                Console.WriteLine("Cancelling...");
                CancellationTokenSource.Cancel();
                eventArgs.Cancel = true;
            };

            string wimPath = "test.wim";
            string applyPath = Directory.CreateDirectory("wimapply").FullName;

            using (WimHandle wimHandle = WimgApi.CreateFile(wimPath, WimFileAccess.Read, WimCreationDisposition.OpenExisting, WimCreateFileOptions.None, WimCompressionType.None))
            {
                WimgApi.SetTemporaryPath(wimHandle, Path.GetTempPath());

                WimMessageResult MessageCallback(WimMessageType messageType, object message, object userData)
                {
                    if (CancellationTokenSource.IsCancellationRequested)
                    {
                        Console.WriteLine("Aborting...");
                        return WimMessageResult.Abort;
                    }

                    Console.WriteLine("Sleeping...");
                    try
                    {
                        Task.Delay(1000, CancellationTokenSource.Token).Wait();
                    }
                    catch (TaskCanceledException)
                    {
                    }

                    return WimMessageResult.Done;
                }

                WimgApi.RegisterMessageCallback(wimHandle, MessageCallback);

                try
                {
                    using (WimHandle imageHandle = WimgApi.LoadImage(wimHandle, 1))
                    {
                        WimHandle imageHandleCopy = imageHandle;

                        try
                        {
                            Console.WriteLine("Applying...");
                            WimgApi.ApplyImage(imageHandleCopy, applyPath, WimApplyImageOptions.DisableDirectoryAcl | WimApplyImageOptions.DisableFileAcl | WimApplyImageOptions.Index);
                            Console.WriteLine("Applied");
                        }
                        catch (OperationCanceledException)
                        {
                            Console.WriteLine("Cancelled");
                            return 1;
                        }
                    }
                }
                finally
                {
                    WimgApi.UnregisterMessageCallback(wimHandle, MessageCallback);
                }

                return 0;
            }
        }
    }
}

Result

D:\ManagedWimgApi\src\ConsoleApplication\bin\Debug> ConsoleApplication.exe
Applying...
Sleeping...
Sleeping...
Cancelling...
Aborting...
Cancelled

If a particular file is very large I don't think the message callback would be called until later.

PCAssistSoftware commented 5 years ago

Hi Jeff

Thank you very much for the example which helped me prove that my interpretation of how to do it was correct

It turns out the problem is not the cancellation aspect, it is to do with destinations

In your example the destination is a folder on an internal hard drive I assume

Whereas during testing I was writing to a drive connected via USB

If I run your code it works fine as is, but if I change destination to be a external USB drive then I get same problem as reported

PCAssistSoftware commented 5 years ago

Can confirm from much more testing that problem is when writing (applying) to external USB devices, as even when you cancel API it still locks the drive causing app to hang in debugger or also explorer to hang as well - will find way of prompting or ejecting USB device to prevent this in future - problem doesn't occur when writing to internal drives

jeffkl commented 5 years ago

I'll try to find some time to test it out and see if there's anything I can get to work in regards to cancellation