Я пытаюсь написать консольное приложение, которое будет перемещаться по дереву AutomationElement в любой открытой программе в Windows 10. Мое приложение отлично работает с любой открытой программой, которую я пробовал ... за исключением Chrome.Некоторые установки Chrome работают, а некоторые нет.Кажется, нет никакой видимой причины для разницы.Я нашел этот пост в блоге, где автор объясняет, что дерево Chrome AutomationElement недоступно, и предлагает код C #, чтобы сделать его доступным.Я решил попробовать.
Мне пришлось переписать значительное количество функции, чтобы она скомпилировалась и запустилась, как вы можете видеть ниже.(сравнивая мой код с постом в блоге).
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;
}
Мне также пришлось создать класс Win32
, на который он ссылается с нуля, так как пост в блоге не объясняет, что это такое и откуда оно пришлоот.Я нашел следующие две ссылки очень полезными при воссоздании класса Win32
.
Подписи казались правильнымиЯ думаю, что я был на правильном пути.Вот мой класс.
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);
}
}
Проблема, с которой я сталкиваюсь, заключается в try-catch
моей функции GetIAccessible2()
.Кажется, все работает нормально до этого момента.Но в той строке, где я вызываю функцию QueryService()
, я получаю сообщение об ошибке «интерфейс не зарегистрирован».Он не говорит мне, какой интерфейс не зарегистрирован, и как его зарегистрировать.
Честно говоря, большая часть этого кода довольно загадочна для меня, и я удивлен, что дошел до того, чтоиметь.Тем не менее, мне нужно, чтобы он действительно работал, а он пока не работает.
Какой интерфейс не зарегистрирован, и как мне его зарегистрировать?
Я бы хотелочень ценю любую помощь (желательно на простом английском языке, с любым необходимым кодом), которую вы можете предложить!
Заранее спасибо!
[EDIT]
Я исправил ошибку, которую яимел.Однако я не уверен, что это исправило Chrome вообще.Вот мой новый код для заинтересованных сторон.
// 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://docs.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;
}