dotnet / core

.NET news, announcements, release notes, and more!
https://dot.net
MIT License
20.88k stars 4.89k forks source link

MAC Mojave Accessibility apis #2132

Closed Terricide closed 5 years ago

Terricide commented 5 years ago

Issue Title

I'm not sure if this is a mac os issue or just the way .net core works on mac osx mojave. But essentially I'm using the api example here "https://forums.xamarin.com/discussion/21965/binding-accessibility-api-axisprocesstrustedwithoptions" to try and add my .net core application to accessibility because I've written a remote control application that needs to be able to send keyboard/mouse commands.

The api actually works and I'm able to get the prompt to appear however my application never shows up in the list. The strange thing is when I run it from the terminal it tries to add terminal and not my application. If I run it from a pkg installer it tries to add "installer". It is like it picks whatever is the parent process.

I'm using .net core 2.1.4

karelz commented 5 years ago

I am confused: If you're using Xamarin APIs, how come you run on .NET Core? That should not be possible AFAIK.

Terricide commented 5 years ago

Well I am using a port of MonoMac to the .net standard.

https://www.youtube.com/watch?v=edgosMqgcBc

I'm only using it for a few apis and it has been working well for what I've needed. It's been working well until mojave started locking things down.

Terricide commented 5 years ago

https://www.nuget.org/packages/MonoMac.NetStandard

karelz commented 5 years ago

Can you provide minimal repro without using that library? It will help to filter out if it is indeed problem in the platform, or problem in the library itself.

Terricide commented 5 years ago

Yup here it is MinimalRepo.zip

using System;
using System.Runtime.InteropServices;
using MonoMac.AppKit;
using MonoMac.Foundation;

namespace AddMeToAccessibility
{
    class Program
    {
        static void Main(string[] args)
        {
            NSApplication.Init();

            Console.WriteLine("AXTrusedCheckOptionPrompt");
            var key = new NSObject[] { NSObject.FromObject("AXTrustedCheckOptionPrompt") };
            Console.WriteLine("CFBoolean");
            var obj = new NSObject[] { NSObject.FromObject(true) };

            Console.WriteLine("FromObjectsAndKeys");
            var options = NSDictionary.FromObjectsAndKeys(obj, key);

            Console.WriteLine("IsProcessTrustedWithOptions");
            Console.WriteLine("Istrusted:" + IsProcessTrustedWithOptions(options));

            Console.WriteLine("Hello World!");
        }

        [DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
        private extern static bool AXIsProcessTrusted();

        [DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
        private extern static bool AXIsProcessTrustedWithOptions(IntPtr options);
        static bool IsProcessTrustedWithOptions(NSDictionary options)
        {
            if (options == null)
            {
                return AXIsProcessTrustedWithOptions(IntPtr.Zero);
            }
            using (NSDictionary d = options)
            {
                return AXIsProcessTrustedWithOptions(d.Handle);
            }
        }
    }
}
karelz commented 5 years ago

@Terricide I included your repro inline - it still references the library. We need your help to figure out if it is problem with the library, or with .NET Core itself.

cc @jkoritzinsky if he can help ...

jkoritzinsky commented 5 years ago

@Terricide the library you're using is really unreliable. The Avalonia project (which I'm also a part of) previously used that for our macOS windowing subsystem. We realized that it was unreliable, crash prone, and didn't actually work half of the time. With Avalonia 0.7.0, we deprecated our usage (and stopped supporting) the MonoMac.NetStandard package and switched to using a native component along with a managed wrapper.

It's highly likely the issue is in the MonoMac.NetStandard package. I highly doubt that this is an issue with .NET Core itself.

karelz commented 5 years ago

Thanks @jkoritzinsky for your insights. Closing for now, until there is specific .NET Core problem sign.

Terricide commented 5 years ago

I still think there is an issue here. If you take MonoMac out of the picture and just run

static void Main(string[] args)
        {
            Console.WriteLine("IsProcessTrustedWithOptions");
            Console.WriteLine("IsTrusted:" + IsProcessTrustedWithOptions());
        }

        [DllImport("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices")]
        private extern static bool AXIsProcessTrustedWithOptions(IntPtr options);
        static bool IsProcessTrustedWithOptions()
        {
            return AXIsProcessTrustedWithOptions(IntPtr.Zero);
        }

This will tell you if your process is trusted for accessibility. Take the sample I sent before remove MonoMac and publish a self contained binary.

Then open up the system preferences and manually add the binary to trusted accessibility programs when you run it the next time it will still show the application as untrusted.

karelz commented 5 years ago

@Terricide that sounds like OS problem though. Not something .NET can influence, right?

Terricide commented 5 years ago

I don't know I'm trying to reach out to apple as well. Programs like dropbox and teamviewer are using this mechanism on mojave to get the correct permissions.

Terricide commented 5 years ago

But I logged a ticket last week with apple as well and haven't heard back from them.

jkoritzinsky commented 5 years ago

@Terricide Try adding a [return: System.Runtime.InteropServices.MarshalAs(UnmanagedType.U1)] to your P/Invoke declaration. I think the native bool size for Objective-C and Swift is 1-byte, but we marshal Booleans to a 4-byte bool by default.

Terricide commented 5 years ago

I tried that and it didn't help. If I tell my mac to trust Terminal and run my sample app within the terminal then it works and comes back true. So I believe the pinvoke method is working correctly. I just need to figure out how to get the mac to trust my .net core binary regardless of how it is run.

It feels hacky but I'm going to try and write native binary executable, add it to accessibility and make it launch my .net core app because like I said earlier it seems to work if I add whatever is parent process.

Terricide commented 5 years ago

I just tried the hack and it didn't work :(

Terricide commented 5 years ago

I just tried the same sample just using swift and it has the same behavior. If I trust terminal it is okay but if I specify the binary it doesn't work. It is clearly not a .net core issue.