How to pass progress back to UI from callback method - c#

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.

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(...);
}

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. I.e.:
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;
}
}

Related

C# variable is used before it's value is determined

This code should take a piece of information from a webpage. My problem is that it doesn't show correnctly and don't know why.
I want to make it somehow to wait for document completion without creating a function outside that one.
The actual code that I want to fix from a larger file:
public static string GetNews()
{
WebBrowser page = new WebBrowser();
string data = null;
page.Navigate(launcherScriptAddress);
page.DocumentCompleted += delegate {
data = page.Document.GetElementById("news").InnerText;
// can't return `data` from here
};
return data; // returns null because it doesn't wait for document to be completed
}
This won't work the way you try to do it. Your function returns way before the page.DocumentCompleted-delegate is being executed.
So the only thing you can do is pass a callback to your GetNews-function that gets executed within your delegate.
The old problem with sync <-> async.
Besides, you should swap the statements .Navigate and .DocumentCompleted +=
to make sure "page" has the callback set before it even starts to load anything.
[edit]
To do that, you need to create a delegate and change your function:
public delegate void NewsCallback( string dataReceived );
public static void GetNews( NewsCallback callback )
{
WebBrowser page = new WebBrowser();
string data = null;
page.Navigate(launcherScriptAddress);
page.DocumentCompleted += delegate {
data = page.Document.GetElementById("news").InnerText;
callback( data );
};
}
After that, you may want to call it this way:
void CallMyNews(){
GetNews( (dataReceived) => {
DoSomeStuffWith(dataReceived);
} );
}

Serial IO Async Issues

I've got serial data coming in to my application and by definition, it's async, so I'm running into troubles when trying to update a label to show what the incoming data is. Every now and then, I get an error on the lblRx.AsyncUpdate line, telling me the object is in use elsewhere.
At present, I use the following code;
private void IODataReceived(object sender, IODataEventArgs e)
{
lblRx.AsyncUpdate(() => lblRx.Text = string.Format("{0}:\t{1}", e.Timestamp, e.Data));
SetBackColors(false, eIODirection.In);
}
public static void AsyncUpdate(this Control ctrl, ActionCallback action)
{
if (ctrl != null)
{
if (!ctrl.IsHandleCreated && ctrl.IsDisposed)
ctrl.CreateControl(); // MSDN says CreateControl() is preferred over CreateHandle().
if (!ctrl.IsDisposed)
AsyncInvoke(ctrl, action);
}
}
The AsyncUpdate method isn't an issue (AFAIK...works well in other situations).
I think I need to put a lock on the control before calling AsyncUpdate. Or is there a better way to handle this situation?

Showing MessageBox in long running process

In an MVVM application I have a long running calculation that runs
in legacy code.
That legacy code shows a MessageBox to ask the user if it shall continue.
Now I want this code to stick to MVVM as easy as possible and thought
about handing in a callback to show the MessageBox and evaluating the
result inside.
How can this be done the easiest?
Have often seen Action for callbacks, but I have no idea how
to work with the bool inside the legacy code.
I want to pass the string to show in the MessageBox from the legacy code
and return the decision (a bool) to the legacy code.
Please note: I do not have to do a bigger refactoring right now, but want
to get rid of the MessageBox inside the legacy code right now.
Perhaps I can use a function like
private bool ShowMessageBox(string text)
{
var result = MessageBox.Show(text, "", MessageBoxButton.YesNo);
if (result.Equals(MessageBoxResult.Yes))
{
return true;
}
return false;
}
-edit-
Should I use some
Action<string, Action<bool>>
for the method signature?
How can I access the bool in the legacy code?
Maybe you can use a delegate?
For the method you showed, you can create a delegate like this:
public delegate bool ShowMessageBoxDelegate(string text);
Then let's say you have a property using the delegate as the type:
public ShowMessageBoxDelegate ShowMessageBoxDelegateProperty { get; set; }
Now if your ShowMessageBox method matches the signature of this delegate...
public bool ShowMessageBox(string text)
{
var result = MessageBox.Show(text, "", MessageBoxButton.YesNo);
if (result.Equals(MessageBoxResult.Yes))
{
return true;
}
return false;
}
... then you could set it as the value of the ShowMessageBoxDelegateProperty property:
ShowMessageBoxDelegateProperty = ShowMessageBox;
Note the missing parenthesis. A delegate can also be multicast, which simply means that they can have more than one method attached to them:
ShowMessageBoxDelegateProperty += ShowMessageBox;
You can also use them as parameters in methods:
public void ProxyShowMessageBox(ShowMessageBoxDelegate showMessageBoxDelegate)
{
if (showMessageBoxDelegate != null)
{
bool result = showMessageBoxDelegate("MessageBox message");
}
}
You would then call it like this:
ProxyShowMessageBox(ShowMessageBox);
You can find out more from the Delegates Tutorial page at MSDN.

Getting error when trying to handle ActiveX event

I have a two ActiveX servers I need to handle it's events.
the first one I got to work with no problems but with the second one I get a error once I try to assign a new event. The one that works the code is below:
public delegate void ICwGetXEvents_OnCommandExEventHandler(uint CommandW, uint CommandL, string CommandText);
public CwGet.CwGetXClass ax_CwGet;
//event
public void CwGetXEvents_OnCommandExEventHandler(uint CommandW, uint CommandL, string CommandText)
{
if (CommandL == 4)
{
//some code
}
}
//ok here is how I assign the controls and event:
ax_CwGet = new CwGetXClass();
ax_CwGet.OnCommandEx += CwGetXEvents_OnCommandExEventHandler;
Ok with the second control(by the way it was created by the same company) I try the same thing:
public delegate void ITrueTtyXEvents_OnCallsignEventHandler(string Call);
public truetty.TrueTtyXClass ax_truetty;
//event
public void TrueTtyXEvents_OnCallsignEventHandler(string Call)
{
//somecode
}
ax_truetty = new TrueTtyXClass();
ax_truetty.OnCallsign+= TrueTtyXEvents_OnCallsignEventHandler;
However when I create the new ActiveX object which works but when I go to assign the event I get this error:
"An outgoing call cannot be made since the application is dispatching an input-synchronous call. (Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_ININPUTSYNCCALL))"
was wondering if anyone could point me in the right direction..
Mike
This is a threading problem. You should ask the component vendor for help with this, sounds like they didn't set the ThreadingModel registry key properly. But the likely response you'll get is "do not use them from a worker thread, only from an STA thread". Which is very common for ActiveX controls.

update variable based upon results from .NET backgroundworker

I've got a C# program that talks to an instrument (spectrum analyzer) over a network. I need to be able to change a large number of parameters in the instrument and read them back into my program. I want to use backgroundworker to do the actual talking to the instrument so that UI performance doesn't suffer.
The way this works is - 1) send command to the instrument with new parameter value, 2) read parameter back from the instrument so I can see what actually happened (for example, I try to set the center frequency above the max that the instrument will handle and it tells me what it will actually handle), and 3) update a program variable with the actual value received from the instrument.
Because there are quite a few parameters to be updated I'd like to use a generic routine. The part I can't seem to get my brain around is updating the variable in my code with what comes back from the instrument via backgroundworker. If I used a separate RunWorkerCompleted event for each parameter I could hardwire the update directly to the variable. I'd like to come up with a way of using a single routine that's capable of updating any of the variables. All I can come up with is passing a reference number (different for each parameter) and using a switch statement in the RunWorkerCompleted handler to direct the result. There has to be a better way.
I think what I would do is pass a list of parameters, values, and delegates to the BackgroundWorker. That way you can write the assign-back code "synchronously" but have execution deferred until the values are actually retrieved.
Start with a "request" class that looks something like this:
class ParameterUpdate
{
public ParameterUpdate(string name, string value, Action<string> callback)
{
this.Name = name;
this.Value = value;
this.Callback = callback;
}
public string Name { get; private set; }
public string Value { get; set; }
public Action<string> Callback { get; private set; }
}
Then write your async code to use this:
private void bwUpdateParameters_DoWork(object sender, DoWorkEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
WriteDeviceParameter(update.Name, update.Value);
update.Value = ReadDeviceParameter(update.Name);
}
e.Result = updates;
}
private void bwUpdateParameters_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
if (update.Callback != null)
{
update.Callback(update.Value);
}
}
}
Here's how you would kick off the update. Let's say you've got a bunch of member fields that you want to update with the actual values of the parameters that were used:
// Members of the Form/Control class
private string bandwidth;
private string inputAttenuation;
private string averaging;
// Later on, in your "update" method
var updates = new List<ParameterUpdate>
{
new ParameterUpdate("Bandwidth", "3000", v => bandwidth = v),
new ParameterUpdate("InputAttenuation", "10", v => inputAttenuation = v),
new ParameterUpdate("Averaging", "Logarithmic", v => averaging = v)
};
bwUpdateParameters.RunWorkerAsync(updates);
That's all you have to do. All of the actual work is done in the background, but you're writing simple variable-assignment statements as if they were in the foreground. The code is short, simple, and completely thread-safe because the actual assignments are executed in the RunWorkerCompleted event.
If you need to do more than this, such as update controls in addition to variables, it's very simple, you can put anything you want for the callback, i.e.:
new ParameterUpdate("Bandwidth", "3000", v =>
{
bandwidth = v;
txtBandwidth.Text = v;
})
Again, this will work because it's not actually getting executed until the work is completed.
[Edit - look back at update history to see previous answer. Talk about not being able to see the wood for the trees]
Is there any reason that, rather than passing a reference number to the Background Worker, you can't pass the ID of the label that should be updated with any value passed back?
So the UI adds an item in the work queue containing:
Variable to change
Attempted change
UI ID
and the BackgroundWorker triggers an event with EventArgs containing
Attempted change
Actual value after attempt
UI ID
Error Message (null if successful)
which is all the information you need to update your UI without a switch or multiple event args and without your Background Worker ever being aware of UI detail.
How about something like this?
[TestFixture]
public class BGWorkerTest
{
string output1;
string output2;
[Test]
public void DoTest()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, args) =>
{
output1 = DoThing1();
output2 = DoThing2();
};
backgroundWorker.RunWorkerAsync();
//Wait for BG to finish
Thread.Sleep(3000);
Assert.AreEqual("Thing1",output1);
Assert.AreEqual("Thing2",output2);
}
public string DoThing1()
{
Thread.Sleep(1000);
return "Thing1";
}
public string DoThing2()
{
Thread.Sleep(1000);
return "Thing2";
}
}

Categories

Resources