Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do we await for C# async delegate function in C++/CX

This is about the communication between the C++ (shared code of different platforms) with C# (Windows Universal App). We know that the following is how we make a function call from C++ to C#.

C#

class FooCS
{
    FooCS()
    {
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEvent;
        // Some other stuff ...
    }

    string GetSomeValueEvent()
    {
        // Some other stuff ... 
        return "Hello World"; 
    }
}

C++

public delegate Platform::String GetSomeValueEventHandler(); 

class FooC
{
    public:
    event GetSomeValueEventHandler^ m_GetSomeValueEvent; 
    void someFunction(){
        Platform::String^ str = m_GetSomeValueEvent();
        // Some other stuff ... 
    }
}

The above is very straight forward. But the problem is that this string GetSomeValueEvent() in C# do some heavy task such as reading data from the database, and I have to make it async.

    async Task<string> GetSomeValueEvent() {
        // Some other stuff ... 
        return "Hello World"; 
    }

Now here comes the question: How do I make my C++ code wait for the return of this delegate function? In another word, what should be the following signature be modified to?

    public delegate Platform::String GetSomeValueEventHandler(); 

I have been googling around and cannot find the right terminology for this problem. If you know for sure that it is impossible, would be at least nice to know.


Eventually, this is what we do:

    string GetSomeValueEvent() {
        // Convert a async method to non-async one
        var val = someAsyncMethod().Result; 
        // Some other stuff ... 
        return "Hello World"; 
    }

You have to make sure GetSomeValueEvent is not running on the main thread to prevent deadlock.

like image 418
Yuchen Avatar asked Sep 09 '15 20:09

Yuchen


1 Answers

UPD: Returning a value from an event is a really BAD idea. Consider the other options like some sort of the observable pattern but with a single observer.

As @RaymondChen mentioned, for async events it's better to use the deferral pattern. It's a commonly used pattern in Windows Runtime.

Add the GetDeferral() method to your event args which returns a special deferral object. This object must have the Complete() method which informs your code that an asynchronous operation has been completed.

===========================================================================

The async keyword actually doesn't change a function prototype. So you just need to alter the return value in your delegate to match the original one (Task<string>).

But there's a catch: Windows Runtime doesn't have the Task<> type, it's a part of TPL which is a .NET library. Instead, Windows Runtime uses IAsyncOperation<> and IAsyncAction to represent asynchronous operations.

So here's what you need to do:

  1. Alter the delegate's signature:

    public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler();
    
  2. Use the AsAsyncOperation() extension method to convert Task<> to IAsyncOperation<>:

    async Task<string> GetSomeValueEvent()
    {
        // Some other stuff ... 
        return "Hello World"; 
    }
    
    IAsyncOperation<string> GetSomeValueEventWrapper()
    {
        return GetSomeValueEvent().AsAsyncOperation();
    }
    
    FooCS()
    {
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEventWrapper;
        // Some other stuff ...
    }
    
like image 200
Eldar Dordzhiev Avatar answered Oct 18 '22 00:10

Eldar Dordzhiev