gcdev
gcdev

Reputation: 1526

C# - Which interface is not registered and how do I register it?

I am trying to write a console app that will navigate the AutomationElement tree in any open program on Windows 10. My app actually works great with every open program I've tried... except for Chrome. Some installs of Chrome work, but some don't. There doesn't appear to be any discernable reason for the difference. I found this blog post where the author explains that Chrome's AutomationElement tree is inaccessible and offers C# code to make it accessible. Figured I would try it.

I had to rewrite a significant amount of the function to get it to compile and run, as you can see below (by comparing my code to the blog post).

public static void GetIAccessible2(int pid)
{
    Guid guid = new Guid("618736E0-3C3D-11CF-810C-00AA00389B71");
    Process proc = Process.GetProcessById(pid);
    IntPtr hwnd = proc.MainWindowHandle;
    IntPtr ptrToObj = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)));
    uint OBJECT_ID = 0xFFFFFFFC; // client: 0xFFFFFFFC window: 0x00000000
    object accessibleObject = new object();
    int retAcc = Win32.AccessibleObjectFromWindow(hwnd, OBJECT_ID, ref guid, ref accessibleObject);
    IntPtr accessibleObjectPointer = Marshal.GetIUnknownForObject(accessibleObject);
    Guid iAccessibleGuid = new Guid(0x618736e0, 0x3c3d, 0x11cf, 0x81, 0xc, 0x0, 0xaa, 0x0, 0x38, 0x9b, 0x71);
    IntPtr iAccessiblePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)));
    Guid iAccServiceProvider = new Guid("6d5140c1-7436-11ce-8034-00aa006009fa");
    int retQuery = Marshal.QueryInterface(
        accessibleObjectPointer,
        ref iAccServiceProvider,
        out iAccessiblePtr
    );
    Accessibility.IAccessible acc = (Accessibility.IAccessible)Marshal.GetTypedObjectForIUnknown(iAccessiblePtr, typeof(Accessibility.IAccessible));
    IntPtr ppvObjectPointer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
    Win32.IServiceProvider serviceProvider = (Win32.IServiceProvider)acc;
    Guid IID_IAccessible2 = new Guid(0xE89F726E, 0xC4F4, 0x4c19, 0xbb, 0x19, 0xb6, 0x47, 0xd7, 0xfa, 0x84, 0x78);
    try
    {
        // This throws an error
        // "interface not registered" error
        serviceProvider.QueryService(ref IID_IAccessible2, ref IID_IAccessible2, out ppvObjectPointer);
    }
    catch (Exception er)
    {
        // Console.WriteLine(er.Message);
        // "interface not registered" error
    }
    Marshal.FinalReleaseComObject(serviceProvider);
    Marshal.FreeCoTaskMem(ppvObjectPointer);
    Marshal.FreeCoTaskMem(accessibleObjectPointer);
    Marshal.FinalReleaseComObject(acc);
    proc = null;
    acc = null;
}

I also had to create the Win32 class he references from scratch, since the blog post doesn't explain what it is or where it came from. I found the following two links very helpful in recreating the Win32 class.

The signatures seemed correct, so I think I was on the right track. Here is my class.

public class Win32
{
    [DllImport("oleacc.dll")]
    internal static extern int AccessibleObjectFromWindow(
     IntPtr hwnd,
     uint id,
     ref Guid iid,
     [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);

    [ComImport]
    [Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IServiceProvider
    {
        void QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
    }
}

The problem I'm encountering is in the try-catch of my GetIAccessible2() function. Everything appears to run fine until that point. But on that line, where I call the QueryService() function, I get the error "interface not registered." It doesn't tell me what interface isn't registered, nor how to register it.

Frankly, most of this code is rather mysterious to me and I'm surprised that I've gotten as far as I have. However, I need it to actually work, and it doesn't yet.

What interface isn't registered, and how do I register it?

I would really appreciate any assistance (preferably in simple English, with whatever code is necessary) you can offer!

Thanks in advance!

[EDIT]

I fixed the bug I had. However, I'm not sure that it fixed Chrome at all. Here is my new code for interested parties.

// Moved this into the Program class with the function.
// https://github.com/jongund/wpt-atta-msaa-iaccessible2-csharp/blob/master/beizhang/TestAdapterConsole/TestAdapterConsole/Program.cs
[DllImport("oleacc.dll")]
public static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint dwObjectID, byte[] refID, out IntPtr pcObtained);


// Here is my new function
private static string GetChromeTitle()
{
    // Chrome must be already open when this runs.
    // https://github.com/jongund/wpt-atta-msaa-iaccessible2-csharp/blob/master/beizhang/TestAdapterConsole/TestAdapterConsole/Program.cs
    // https://lukecodesit.galamdring.com/2018/04/uiautomation-and-chrome-enabling-via.html
    Guid guid = new Guid("618736E0-3C3D-11CF-810C-00AA00389B71");
    Guid guidIDispatch = Guid.NewGuid();
    Process[] arrProcs = Process.GetProcessesByName("chrome");
    foreach (Process chromeProc in arrProcs)
    {
        try
        {
            IntPtr ptrHwnd = chromeProc.MainWindowHandle;
            IntPtr procbook = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)));
            IntPtr procbookp = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
            uint u = 0x00000000;
            AccessibleObjectFromWindow(ptrHwnd, u, guid.ToByteArray(), out procbookp);
            procbook = Marshal.ReadIntPtr(procbookp);
            IntPtr iAccessiblePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)));
            Guid iAccessibleGuid = new Guid("6d5140c1-7436-11ce-8034-00aa006009fa");
            Marshal.QueryInterface(procbookp, ref iAccessibleGuid, out iAccessiblePtr);
            Accessibility.IAccessible acc = (Accessibility.IAccessible)Marshal.GetTypedObjectForIUnknown(iAccessiblePtr, typeof(Accessibility.IAccessible));

            string sName = acc.get_accName();

            IntPtr ppvObjectPointer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
            // https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.ole.interop.iserviceprovider.queryservice?view=visualstudiosdk-2017
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider iProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)acc;
            Guid IID_IAccessible2 = new Guid(0xE89F726E, 0xC4F4, 0x4c19, 0xbb, 0x19, 0xb6, 0x47, 0xd7, 0xfa, 0x84, 0x78);
            iProvider.QueryService(ref IID_IAccessible2, ref IID_IAccessible2, out ppvObjectPointer);

            return sName;
        }
        catch(Exception er)
        {
            Console.WriteLine(er.Message);
        }
    }
    return string.Empty;
}

Upvotes: 3

Views: 1407

Answers (0)

Related Questions