Running C++ code asynchronously in a C# program

648 views Asked by At

I wrote some backend code in C++ and I wrote a frontend for it in C#. What I want it to do is run the backend code in the background so I can do other things like update a progress bar, but when I click the "Start" button, the program hangs until it's finished running the backend code.

C# code:

    [DllImport("backend.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int executeBackend();

    private async void startButton_Click(object sender, EventArgs e)
    {
        startButton.Enabled = false;
        await StartProgram();
    }

    private async Task StartProgram()
    {
        int status = 0;
        status = executeBackend(); //This is causing the UI to deadlock
        startButton.Enabled = true;
    }

backend.dll C++ code:

extern "C" {
    __declspec(dllexport) int executeBackend()
    {
        int statusCode = 0;
        //Do stuff in the background
        return statusCode;
    }
}

If I comment out the call to run executeBackend and replace it with await Task.Delay(5000);, the UI doesn't deadlock. How would I fix this?

2

There are 2 answers

1
WBuck On BEST ANSWER

You can wrap the call to executeBackend in a Task to prevent the UI from locking up.

var status = await Task.Run(() => executeBacked());

I also think you're confused about what the async keyword actually does. I think it might be prudent for you to read up on how Asynchronous Programming works in dotnet.

0
Charlieface On

You need to rethink how your C++ code handles asynchronicity.

You should be able to pass the C++ side a callback function, so that you can hand off the whole operation and return a Task.

Something like this (note the StartProgram function is using TaskCompletionSource and is not marked async.)

[DllImport("backend.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void executeBackend(BackendCallback func);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void BackendCallback(int result);

private Task<int> StartProgram()
{
    var tcs = new TaskCompletionSource<int>();
    BackendCallback callback = result =>
    {
        tcs.SetResult(result);
        GC.KeepAlive(callback);  // prevent GC from disposing unmanaged callback
    };
    executeBackend(callback);
    return tcs.Task;
}

I'm not familiar enough with C++ to show you that side.

But essentially you need to be able to take a parameter of type void (*) (int) function pointer, hand off the operation to another thread, and then on completion you call back the C# side using the function pointer.