简体   繁体   中英

How to pass progress back to UI from callback method

I'm working on an app that uses the WimgApi package by Jeff Kluge to apply a windows image to a disk.

I'm having issues getting the example callback method to update UI components, specifically a label on a form (ideally a progressbar).

I've tried to use a delegate to set the value but this does not seem to work as I cannot figure how to pass the delegate down to the callback method.

If I make the callback method non-static, I can access the form properties but then I get a deadlock that even if I disable deadlock breaking, it just locks up.

I've been told to look at using IProgress and async but whilst I can change the code to run the method asynchronously (this works and UI doesn't lock), I still cannot figure out how to get the MyCallbackMethod to send info back to the ui.

//Apply Image Method
public void ApplyImage()
        {
            using (WimHandle wimHandle = WimgApi.CreateFile(@"C:\osimages\test.wim",
                WimFileAccess.Read,
                WimCreationDisposition.OpenExisting,
                WimCreateFileOptions.None,
                WimCompressionType.None))
            {
                // Always set a temporary path
                WimgApi.SetTemporaryPath(wimHandle, Environment.GetEnvironmentVariable("TEMP"));

                // Register a method to be called while actions are performed by WIMGAPi for this .wim file
                WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod);

                try
                {
                    // Get a handle to the first image in the .wim file
                    using (WimHandle imageHandle = WimgApi.LoadImage(wimHandle, 1))
                    {
                        // Apply the image contents to C:\Apply
                        // This call is blocking but WIMGAPI will be calling MyCallbackMethod() during the process
                        WimgApi.ApplyImage(imageHandle, @"X:\", WimApplyImageOptions.None);
                    }
                }
                finally
                {
                    // Be sure to unregister the callback method
                    //
                    WimgApi.UnregisterMessageCallback(wimHandle, MyCallbackMethod);
                }
            }

private static WimMessageResult MyCallbackMethod(WimMessageType messageType, object message, object userData)
        {
            switch (messageType)
            {
                case WimMessageType.Progress:  // Some progress is being sent

                    // Get the message as a WimMessageProgress object
                    //
                    WimMessageProgress progressMessage = (WimMessageProgress)message;

                    // UPDATE UI
                    //THIS IS WHERE I WANT TO SEND BACK PROGRESS INFO

                    break;


                   //REMOVED OTHER MESSAGE CASE STATEMENTS TO CONDENSE CODE


            }

            // 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

            return WimMessageResult.Success;
        }

//full example code at Example code is https://github.com/jeffkl/ManagedWimgApi/wiki/Message-Callbacks

If I try and access the label property in the callback method, I receive an 'object reference is required for non static field, method or property form1.progressLabel.text. I've tried to create a delegate but seem to have issues accessing the method in the call back.

I've watched several videos and tried to understand the msdn documents for delegates, callbacks and things like async / backgroundworker but I just seem to come away more confused.

Really appreciate any pointers / things I should be focusing on.

Making some assumptions here but if you will only be showing one progress form at a time, you should be able to get away with storing a static reference to it. Ie:

class ProgressForm
{
    private static ProgressForm staticRef;

    private void Form_Loaded(object sender, EventArgs e)
    {
        staticRef = this;
    }

    private void InternalCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
    {
        // Ensure we're touching UI on the right thread
        if (Dispatcher.InvokeRequired)
        {
            Dispatcher.Invoke(() => InternalCallback(m, w, l, u));
            return;
        }

        // Update UI components
        // ....
    }

    private static uint StaticCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
    {
        staticRef?.InternalCallback(m, w, l, u);

        return 0;
    }
}

Dislaimer: I don't have any experience with the WimgApi package. But there is an overload of the WimgApi.RegisterMessageCallback method taking an arbitrary object that will be passed to the callback. So please try this:

WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod, this);

and in the callback:

var form = (MyForm)userData;
if (form.InvokeRequired)
{
    form.Invoke((MethodInvoker)(() => UpdateProgressUI(...)));
}
else
{
    form.UpdateProgressUI(...);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM