Wait inside method until event is captured
Asked Answered
G

3

11

I have this issue with a method in C#. I made a method that calls a function from a dll its called Phone.GetLampMode(); Now Phone.GetLampMode doesnt return anything. The data gets returned in a event the 'onGetLampModeResponse' event. Is there a way i can wait in my method until i get the data from the onGetLampModeResponse event?

public bool checkLamp(int iLamp)
{
    Phone.ButtonIDConstants btn = new Phone.ButtonIDConstants();
    btn = Phone.ButtonIDConstants.BUTTON_1;
    btn += iLamp;
    Phone.GetLampMode(btn, null);

    return true;
}

private void Phone_OnGetLampModeResponse(object sender, Phone.GetLampModeResponseArgs e)
{
    var test = e.getLampModeList[0].getLampMode.ToString();    
}
Guthrie answered 6/3, 2013 at 8:56 Comment(6)
What is the second parameter of GetLampMode? Let me guess: An object which you can define yourself? And is that object the sender in the event handler or inside the e? EDIT: Yeah it is, it's called userState. You can retrieve this userState inside the e variable in the callback function.Boote
I can think of one ugly solution by defining a global bool that is set to true by OnGetLampModeResponse and checked in a loop by checkLamp.Wuhu
@JohnWillemse that's quite ugly and shouldn't be done since the concept of handing state variables to events exists.Boote
This is what it says in the class browser. public int GetLampMode(Avaya.ApplicationEnablement.DMCC.Phone.ButtonIDConstants buttonNumber, object userState) Member of Avaya.ApplicationEnablement.DMCC.PhoneGuthrie
@JensMalfait then you can use the userState object to give the event handler a context...Boote
could u give me a small example on how i would actualy do this?Guthrie
W
10

One solution is to use AutoResetEvent:

public bool checkLamp(int iLamp)
{
    Phone.ButtonIDConstants btn = new Phone.ButtonIDConstants();
    btn = Phone.ButtonIDConstants.BUTTON_1;
    btn += iLamp;

    AutoResetEvent waitHandle = new AutoResetEvent(false); 

    // Pass waitHandle as user state
    Phone.GetLampMode(btn, waitHandle);

    // Wait for event completion
    waitHandle.WaitOne();

    return true;
}

private void Phone_OnGetLampModeResponse(object sender, Phone.GetLampModeResponseArgs e)
{
    var test = e.getLampModeList[0].getLampMode.ToString();

    // Event handler completed
    // I guess there is some UserState property in the GetLampModeResponseArgs class
    ((AutoResetEvent)e.UserState).Set();
}

NOTE: Ad you're using Phone as a static class/variable, one can think you're developing on Windows Phone... If it is the case, do note that the whole concept of WP and async programming is to not lock the UI thread in a such way.

Willwilla answered 6/3, 2013 at 9:5 Comment(8)
Did you just put the waitHandle into the global context? What if you execute it more than once and get your wait completely stuck? How can this answer even get upvotes?Boote
@Boote Good comment, but the OP didn't specified anything about multi-threading. I think he just want to make something sync from async. If it is actually multi-threaded, the wait handle should be passed as a parameter of the method.Willwilla
it should definitely be passed as userState, in ANY case. You risk getting a thread stuck infinitely because of such measly things as "race conditions".Boote
the Phone object is not from windows phone. Its actualy a component from the avaya SDK for .net I'm building a small phone server that listens to incomming registrations from terminals and sets up a thread for each client. Each client has a instance of the class where these methods are used in. So i dont think i will get any interference from using the autoresetevent? is my guesswork correct or is there a other path i need to take ?Guthrie
@JensMalfait "One thread for each client"? Then sinni800's comments apply to your case. You definitely want to use the userstate to get the wait handle. Editing the answer.Willwilla
is there a way i could pass e.getLampModeList[0].getLampMode.ToString(); to the checkLamp method ?Guthrie
@JensMalfait I knew something like that would happen. You will either just need to let the checkLamp method return and have the event handler set your values (= dont use the waithandle) or send a container (reference?) for that data with that waitHandle. I wouldn't know which kind of container would work for this... Pointers would be SO perfect in this case :)Boote
I would like to point out that the async solution proposed in my answer to easily be extended to pass a text value. (Just use a TaskCompletionSource<string>, await the task inside of checkLamp and do something with the result.)Homeopathist
H
3

You can wrap the handler in an asynchronous method, which should look something like this (untested):

public async Task<bool> checkLamp(int iLamp)
{
    Phone.ButtonIDConstants btn = new Phone.ButtonIDConstants();
    btn = Phone.ButtonIDConstants.BUTTON_1;
    btn += iLamp;

    var tcs = new TaskCompletionSource<bool>();
    var handler = (sender, e) => {
        Phone.OnGetLampModeResponse -= handler;
        var test = e.getLampModeList[0].getLampMode.ToString();
        tcs.SetResult(true);
    };
    Phone.OnGetLampModeResponse += handler;

    Phone.GetLampMode(btn, null);

    return tcs.Task;
}

In your calling method, you would write:

var returnValue = await checkLamp(iLamp);

This has the advantage that your user interface does not block while the process is waiting for the response.

Here's a blog entry on this issue. Note that Framework 4.5 is required.

Homeopathist answered 6/3, 2013 at 9:17 Comment(0)
G
0

It looks like the existing model is close to the Event-based Asynchronous Pattern (EAP). You might want to look at the article Interop with Other Asynchronous Patterns and Types which describes how to convert such a pattern to the newer Task-based Async Pattern (TAP).

Once you have a Task (or Task<T>, you can just Wait for it.

Giro answered 6/3, 2013 at 9:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.