У меня проблема с использованием 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 .