UI error display window with task async in WPF
This is a good fit for async await:

public async void RunProgramLogic()
{
    if (!IsRunning)
    {
        IsRunning = true;
        try
        {
            await Task.Run(() =&gt; ProgramLogic())
                      .ConfigureAwait(true); // schedules back to the synchronization context the method was called from.
        }
        catch (Exception e)
        {
            logger.Error(e); // Log or LogError feels like better names
            windowManager.ShowDialog(new ForceCloseViewModel(e.Message), null, null);
        }
        finally
        {
            IsRunning = false;
        }
    }
}


Regarding async await
As you can see using async and await removes quite a bit of noise from the code.
In general async void is smelly and should only be used for eventhavndlers.
In your case it feels like a decent fit since you handle the exceptions. Async void means it cannot be awaited.

Regarding command:
While I agree that this looks like a fit for a RelayCommand it is not trivial due to the async.

Here is a good read on commands for async scenarios.

Most RelayCommand implementations handle raising CanExecuteChanged poorly by calling CommandManager.InvalidateRequerySuggested which is very inefficient.

Here is a nice implementation.

Regarding scheduling
Always be explicit with .ConfigureAwait(true|false) when awaiting. The default is true which means that the code continues on the synchronizationcontext it came from. This is a strange default in my opinion and ConfigureAwait is a poorly named method.
In your case I think the logger and windowManager should be thread agnostic enabling .ConfigureAwait(false)
Suggested implementation:

public class WindowManager
{
    public void ShowDialog(object viewModel, ...)
    {
        if (Dispatcher.CurrentDispatcher.CheckAccess())
        {
            ShowDialogCore(viewModel, ...);
        }
        else
        {
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =&gt; ShowDialogCore(viewModel, ...)));
        }
    }

    private void ShowDialogCore(object viewModel, ...)
    {
        ...
    }
}


The upside with implementing it like this is that the caller will not need to remember to call it on the UI thread. It is the job of the windowmanager to schedule if needed. Hence fixing many potential bugs in one place.

Regarding the pattern Using a flag like this is fine and will work well if you refactor to relaycommand. A try-finally block makes it clear in my opinion.