Почему соединение удаляется до завершения WaitAsyn c? - PullRequest
1 голос
/ 16 января 2020

У меня проблема с использованием DBus. Я пытаюсь получить уведомление с несколькими кнопками действий и работает.

До сих пор я был в состоянии заставить пузырь показываться с помощью

retValue = await proxy.NotifyAsync(

и получить кнопку, которая была нажата при обратном вызове, с

await proxy.WatchActionInvokedAsync

См. Код ниже.

Итак, теперь, поскольку уведомление возвращается до получения ответа, мне нужно дождаться ответа пользователя (ManualResetEventSlim), прежде чем закрывать соединение. Это создавало тупик, который я решил, переключившись на AsyncManualResetEvent.

Теперь это работает, вроде как - я получаю пузырь, я получаю ответ, если выбрано действие пузыря (если щелкнуть до истечения времени ожидания - время ожидания не обрабатывается в настоящее время), но я также получаю исключение, переданное в onError-обратный вызов в WatchActionInvokedAsyn c.

Ошибка обратного вызова: невозможно получить доступ к удаленному объекту. Название объекта: 'Tmds.DBus.Connection'

Почему я получаю эту ошибку? Разве await notifyResponseReceived.WaitAsync(); не должен вернуться до того, как соединение будет ликвидировано? Что может вызвать эту ошибку? В дополнение к этому, почему у исключения есть пустая трассировка стека?

namespace NotificationTest
{


    // https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-1-asyncmanualresetevent/
    public class AsyncManualResetEvent
    {
        private volatile System.Threading.Tasks.TaskCompletionSource<bool> m_tcs; 


        public AsyncManualResetEvent()
        {
            this.m_tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
        }


        public System.Threading.Tasks.Task WaitAsync() 
        { 
            return this.m_tcs.Task; 
        }


        //public void Set() 
        //{ 
        //    this.m_tcs.TrySetResult(true); 
        //}

        public void Set()
        {
            System.Threading.Tasks.TaskCompletionSource<bool> tcs = this.m_tcs;

            System.Threading.Tasks.Task.Factory.StartNew(s => ((System.Threading.Tasks.TaskCompletionSource<bool>)s).TrySetResult(true),
                tcs, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskCreationOptions.PreferFairness, System.Threading.Tasks.TaskScheduler.Default);

            tcs.Task.Wait();
        }


        public void Reset()
        {
            while (true)
            {
                System.Threading.Tasks.TaskCompletionSource<bool> tcs = this.m_tcs;
                if (!tcs.Task.IsCompleted ||
                    System.Threading.Interlocked.CompareExchange(ref this.m_tcs, new System.Threading.Tasks.TaskCompletionSource<bool>(), tcs) == tcs)
                    return;
            } // Whend 

        } // End Sub Reset 


    } // End Class AsyncManualResetEvent



    class Program
    {


        // https://wiki.debianforum.de/Desktop-Notification_von_Systemservice_mittels_dbus
        // https://cheesehead-techblog.blogspot.com/2009/02/five-ways-to-make-notification-pop-up.html
        // https://wiki.debianforum.de/Desktop-Notification_von_Systemservice_mittels_dbus
        // https://gist.github.com/ducin/6152106
        // https://cweiske.de/tagebuch/DBus%20notify-send%20over%20network.htm
        // dotnet dbus list services --bus system | grep NetworkManager org.freedesktop.NetworkManager
        // dotnet dbus list objects --bus system --service org.freedesktop.NetworkManager
        // dotnet dbus codegen --bus system --service org.freedesktop.NetworkManager

        // cd ~/gitlab/Projects/NotificationTest/Tmds.DBus.Tool
        // dotnet run codegen --bus system --service org.freedesktop.NetworkManager
        // dotnet run codegen --bus session --service org.freedesktop.Notifications
        private static async System.Threading.Tasks.Task<uint> SendNotification()
        {
            uint retValue = 666;
            // System.Threading.ManualResetEventSlim notifyResponseReceived = new System.Threading.ManualResetEventSlim();
            AsyncManualResetEvent notifyResponseReceived = new AsyncManualResetEvent();

            Tmds.DBus.ObjectPath objectPath = new Tmds.DBus.ObjectPath("/org/freedesktop/Notifications");
            string service = "org.freedesktop.Notifications";

            using (Tmds.DBus.Connection connection = new Tmds.DBus.Connection(Tmds.DBus.Address.Session))
            {
                await connection.ConnectAsync();

                Notifications.DBus.INotifications proxy = connection.CreateProxy<Notifications.DBus.INotifications>(service, objectPath);

                // Task<IDisposable> WatchActionInvokedAsync(Action<(uint id, string actionKey)> handler, Action<Exception> onError = null);
                await proxy.WatchActionInvokedAsync(
                    delegate ((uint id, string actionKey) id)
                    {
                        if (id.id != retValue)
                        {
                            System.Console.WriteLine("abort");
                            return;
                        }

                        System.Console.WriteLine("Dialog Id: {0}", id.id);
                        System.Console.WriteLine($"ActionKey: {id.actionKey}");
                        notifyResponseReceived.Set();
                    }, delegate (System.Exception ex)
                    {
                        System.Console.Write("Error callback: ");
                        System.Console.WriteLine(ex.Message);
                        System.Console.WriteLine(ex.StackTrace);
                    }
                );


                // string[] actions = new string[0];
                string[] actions = new string[] { "0", "Cancel", "1", "No", "266789", "default", "3", "test" };

                System.Collections.Generic.Dictionary<string, object> hints =
                    new System.Collections.Generic.Dictionary<string, object>();

                string icon = "insert-image"; // # Siehe https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html#names
                icon = "call-start";
                icon = "/root/Downloads/5d9ad698-8270-4141-b64e-736d3dbb9ecc.jpeg";
                icon = "dialog-information";
                icon = "dialog-error";
                icon = "dialog-warning";
                icon = "flag-ch";

                // https://developer.gnome.org/notification-spec/

                //await proxy.NotifyAsync("Notification", 0, icon, "summary", "body", actions, hints, 0);
                retValue = await proxy.NotifyAsync("Notifica1tion", 0, icon, "This is the summary", "This is the body", actions, hints, 5000);

                // notifyResponseReceived.Wait(); // blocks itselfs
                await notifyResponseReceived.WaitAsync();
            } // End Using connection 

            return retValue;
        } // End Task SendNotification 


        static void Main(string[] args)
        {
            System.Console.Write("Notification ID: ");
            System.Console.WriteLine(SendNotification().Result);
            System.Console.ReadKey();

            System.Console.WriteLine(" --- Press any key to continue --- ");
            System.Console.ReadKey();
        } // End Sub Main 


    } // End Class AsyncManualResetEvent 


} // End Namespace NotificationTest

Сгенерированный прокси-объект (Notifications.DBus.INotifications) находится здесь: https://pastebin.com/v40pyaFN на всякий случай, если кто-то захочет.

Этот код использует библиотеку Tmds.DBus .

...