17.05.2022, 13:23 | #1 |
Участник
|
Dax 2012 получить путь к Treenode (Toolbar, tabax)
Привет всем.
А кто-нибудь делал такую штуку: Есть окно AOT с одним открытым элементом, например полученным через "Открыть новое окно". В ax4 его заголовок был например таким "\Data dictionary\Tables\SalesTable" а в 2012-й все скупо: "AOT - \Data dictionary\Tables" соответственно в 4-ке всякие самописные оконные менеджеры (DEV_ToolBar, Tabax, Sidax) корректно распознавали вид окна и в ярлычке окна было видно название объекта AOT (в данном примере стояло имя таблички SalesTable). А в 12-ке (и кажется в 2009-й то же самое) это не работает. Не очень удобно пользоваться. Ярлычки окон есть, имени объекта в тексте ярлыка нет. Кто-нибудь смог в окне AOT добраться до корневого узла и выцепить его имя ? Например, есть hwnd окошка AOT. Можно по нему как-то вытащить текст первого узла в дереве элементов ? Последний раз редактировалось Logger; 17.05.2022 в 13:47. |
|
17.05.2022, 14:01 | #2 |
Боец
|
А так не работает?
TreeNode treeNode = new LastAotSelection().first(); |
|
17.05.2022, 16:49 | #3 |
Участник
|
Работает.
Но проблема не в этом. Работает тулбар. Перебирает окошки в аксапте, чтоб обновить список ярлыков в панели. Открыто 5 окон AOT. В каждом своя табличка открыта. Раньше в ax4 в заголовке каждого окна был полный путь до таблички, в общем, было x++ имя таблички в названии окна. (например "\Data dictionary\Tables\SalesTable" "\Data dictionary\Tables\SalesTable_RU" "\Data dictionary\Tables\SalesTable_W" "\Data dictionary\Tables\PurchTable" ... ) И оно было доступно тулбару. Удобно. Видишь ярлыки в панели и видно какой ярлык к какому объекту X++ ведет. А теперь в заголовке все 5 разных окон имеют один и тот же текст: "AOT - \Data dictionary\Tables" Соответственно ярлычки в тулбаре все выглядят одинаково. Не различить их. Вот задача понять по окошку AOT - какой там объект спрятан. Хотя бы его название как строку вытащить. |
|
17.05.2022, 17:04 | #4 |
Участник
|
В случае 2012-й аксапты проблемные места обвел красной рамочкой.
Наверно так будет понятно в чем проблема. В случае ax2012 все заголовки окон AOT одинаковые. Поэтому и в тулбаре ярлыки для них одинаковые. Как бы вытащить текст из корня дерева с объектом AOT чтобы сделать в тулбаре нормальные ярлыки с информативным текстом как в ax4 Иногда при рефакторинге приходится много объектов смотреть и сверять. Начинается путаница. Последний раз редактировалось Logger; 17.05.2022 в 17:08. |
|
17.05.2022, 17:22 | #5 |
Участник
|
Посмотрел структуру в Spy++
Особой разницы не видно. Просто разные Caption у окон в случае ax4 и ax2012 |
|
18.05.2022, 00:59 | #6 |
Участник
|
Привет.
Есть там наименование, только смотреть его у ребёнка нужно: Ну и не новость, через простые WinAPI::findWindowEx и WinApi::getWindowText оно не выводится. Видно, что это структура, она реализует интерфейс IAccessible. Получение объекта реализующего данный интерфейс можно осуществить через функцию AccessibleObjectFromWindow библиотеки oleacc. Ну а там уже вызвать get_accName На шарпе проверил - работает. GUID интерфейса 618736E0-3C3D-11CF-810C-00AA00389B71 Последний раз редактировалось Товарищ ♂uatr; 18.05.2022 в 01:24. |
|
|
За это сообщение автора поблагодарили: Logger (93). |
25.10.2022, 13:23 | #7 |
Участник
|
Добрый день.
Получилось решить проблему? Тоже надоело в ToolBar видеть малоинформативные заголовки вкладок. |
|
25.10.2022, 17:06 | #8 |
Участник
|
У меня так и не дошли руки сделать. Как будет время - сделаю. Намного удобнее работать с заголовками.
Думаю что все должно получиться. Если из консольного приложения получилось, то почему в аксапте не получится ? Кстати в 2009-й та же проблема. С нее все и началось. Последний раз редактировалось Logger; 25.10.2022 в 17:08. |
|
27.11.2022, 20:05 | #9 |
Участник
|
Попробовал я набросать проект и не получилось.
Взял пример от Товарищ ♂uatr запихнул его в C# проект (консольное приложение) - все работает (правда запускать надо debug версию, ну не суть) Запускаем проект, он спрашивает hwnd окна Даем ему hwnd в десятичном виде (в моем примере 67552) в ответ текст Цитата:
Waiting for HWND input (in decimal format):
67552 HWND hex:107E0; Name:SalesTable; HWND:67552 HWND hex:107E0; Description:\Data Dictionary\Tables\SalesTable: 7; HWND:67552 Оговорюсь что нужно скармливать не hwnd окна АОТ (с ClassName AxMDIChildWindow) а hwnd дочернего к нему окна (с ClassName AxPaneWnd) Текст проекта X++: // [url=http://axforum.info/forums/showthread.php?p=434649#post434649]Dax 2012 получить путь к Treenode (Toolbar, tabax)[/url] // Товарищ ♂uatr using System; using System.Runtime.InteropServices; using System.Security; using Accessibility; namespace AotInspectWindows { class Program { [DllImport("oleacc.dll")] private static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject); static void Main(string[] args) { object accessibleObj = null; string name; string description; Guid guid = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}"); Console.WriteLine("Waiting for HWND input (in decimal format):"); string ptr = Console.ReadLine(); // вводить в десятичном формате IntPtr handle = new IntPtr(Convert.ToInt32(ptr)); // int result = AccessibleObjectFromWindow(handle, (uint)OBJID.OBJID_WINDOW, ref guid, ref accessibleObj); // для такого вызова будет пустым get_accDescription() но непустым get_accName() int result = AccessibleObjectFromWindow(handle, (uint)OBJID.OBJID_CLIENT, ref guid, ref accessibleObj); // а для такого все ок - и get_accDescription() и get_accName() - непустые name = ((IAccessible)accessibleObj).get_accName(0); Console.WriteLine($"HWND hex:{handle.ToString("X")}; Name:{name}; HWND:{handle}"); description = ((IAccessible)accessibleObj).get_accDescription(0); Console.WriteLine($"HWND hex:{handle.ToString("X")}; Description:{description}; HWND:{handle}"); Console.ReadKey(); } public enum OBJID : uint { OBJID_WINDOW = 0x00000000, OBJID_SYSMENU = 0xFFFFFFFF, OBJID_TITLEBAR = 0xFFFFFFFE, OBJID_MENU = 0xFFFFFFFD, OBJID_CLIENT = 0xFFFFFFFC, OBJID_VSCROLL = 0xFFFFFFFB, OBJID_HSCROLL = 0xFFFFFFFA, OBJID_SIZEGRIP = 0xFFFFFFF9, OBJID_CARET = 0xFFFFFFF8, OBJID_CURSOR = 0xFFFFFFF7, OBJID_ALERT = 0xFFFFFFF6, OBJID_SOUND = 0xFFFFFFF5, } } } Последний раз редактировалось Logger; 27.11.2022 в 20:07. |
|
27.11.2022, 20:13 | #10 |
Участник
|
т.е. в целом все работает.
А из аксапты у меня не получилось. Вот джоб X++: static void AotInspectWindows(Args _args) { HWND hwnd = 67552; // HWND hwnd = 0; DLL dllOleacc; DLLFunction dllFuncAccessibleObjectFromWindow; Binary guidBinary; // guid guidXpp; Binary ptrIAccessibleBinary; int hResult; COM comIAccessible; COMDispFunction comFunc; COMVariant varChild; COMVariant pszString; #define.OBJID_WINDOW (0x00000000) #define.OBJID_SYSMENU (0xFFFFFFFF) #define.OBJID_TITLEBAR (0xFFFFFFFE) #define.OBJID_MENU (0xFFFFFFFD) #define.OBJID_CLIENT (0xFFFFFFFC) #define.OBJID_VSCROLL (0xFFFFFFFB) #define.OBJID_HSCROLL (0xFFFFFFFA) #define.OBJID_SIZEGRIP (0xFFFFFFF9) #define.OBJID_CARET (0xFFFFFFF8) #define.OBJID_CURSOR (0xFFFFFFF7) #define.OBJID_ALERT (0xFFFFFFF6) #define.OBJID_SOUND (0xFFFFFFF5) Binary guid2Binary(str _guidStr) { guid guidVar; System.Guid guidNet; System.Byte[] buffer; System.IO.MemoryStream stream; Binary ret; ; guidVar = str2guid(_guidStr); stream = new System.IO.MemoryStream(); // stream.Write(guidVar); guidNet = guidVar; buffer = guidNet.ToByteArray(); // infoCon(["length", CLRInterop::getAnyTypeForObject(buffer.get_Length())]); stream.Write(buffer, 0, 16); // info(ClrMethod_MRC::toString(stream)); // info(ClrMethod_MRC::toString(stream.get_Length())); ret = Binary::constructFromMemoryStream(stream); /* // info(ret.xml()); info(int2Hex(ret.byte(0), 2)); info(int2Hex(ret.byte(1), 2)); info(int2Hex(ret.byte(2), 2)); info(int2Hex(ret.byte(3), 2)); info(int2Hex(ret.byte(4), 2)); info(int2Hex(ret.byte(5), 2)); info(int2Hex(ret.byte(6), 2)); info(int2Hex(ret.byte(7), 2)); info(int2Hex(ret.byte(8), 2)); info(int2Hex(ret.byte(9), 2)); info(int2Hex(ret.byte(10), 2)); info(int2Hex(ret.byte(11), 2)); info(int2Hex(ret.byte(12), 2)); info(int2Hex(ret.byte(13), 2)); info(int2Hex(ret.byte(14), 2)); info(int2Hex(ret.byte(15), 2)); info(''); */ stream.Close(); return ret; } ; dllOleacc = new DLL('oleacc.dll'); dllFuncAccessibleObjectFromWindow = new DLLFunction(dllOleacc, 'AccessibleObjectFromWindow'); // [url]https://learn.microsoft.com/en-us/windows/win32/api/oleacc/nf-oleacc-accessibleobjectfromwindow[/url] // HRESULT AccessibleObjectFromWindow( // [in] HWND hwnd, // [in] DWORD dwId, // [in] REFIID riid, // [out] void **ppvObject // ); dllFuncAccessibleObjectFromWindow.arg( ExtTypes::DWord, // [in] HWND hwnd, ExtTypes::DWord, // [in] DWORD dwId, ExtTypes::Pointer, // [in] REFIID riid, // TO DO ExtTypes::Pointer // [out] void **ppvObject // TO DO ); guidBinary = guid2Binary('{618736E0-3C3D-11CF-810C-00AA00389B71}'); // IAccessible Interface // [url]https://learn.microsoft.com/en-us/dotnet/api/accessibility.iaccessible?view=windowsdesktop-7.0[/url] // guidXpp = str2guid('{618736E0-3C3D-11CF-810C-00AA00389B71}'); /* ptrIAccessibleBinary = new Binary(8); ptrIAccessibleBinary.qWord(0, 0); */ ptrIAccessibleBinary = new Binary(4); ptrIAccessibleBinary.dWord(0, 0); infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.dWord(0)]); dllFuncAccessibleObjectFromWindow.returns(ExtTypes::DWord); // HRESULT hResult = dllFuncAccessibleObjectFromWindow.call( hwnd, #OBJID_WINDOW, guidBinary, // guidXpp, ptrIAccessibleBinary ); infoCon(["hResult = ", hResult]); if (hResult) { infoCon(["LastError = ", WinAPI::getLastError()]); } // infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.qWord(0)]); infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.dWord(0)]); comIAccessible = COM::createFromInterface(ptrIAccessibleBinary.dWord(0)); // comFunc = new COMDispFunction(comIAccessible, "get_accName", COMDispContext::PropertyGet); varChild = COMVariant::createFromInt(0, COMVariantInOut::In); // CHILDID_SELF info(any2str_MRC(varChild.variantType())); // pszString = COMVariant::createFromStr('', COMVariantInOut::Out_retVal); pszString = new COMVariant( COMVariantInOut::Out_retVal, COMVariantType::VT_BSTR); // comFunc = new COMDispFunction(comIAccessible, "accName", COMDispContext::PropertyGet); // hResult = comFunc.call(varChild, pszString); Метод "accName" в COM-объекте класса "IAccessible" возвратил код ошибки 0x8002000E (DISP_E_BADPARAMCOUNT), который означает: Число аргументов, указанных в вызове функции, отличается от числа аргументов в объявлении метода. // comFunc = new COMDispFunction(comIAccessible, "get_accName", COMDispContext::Method); // Метод "get_accName" не поддерживается интерфейсом Automation COM-объекта класса "IAccessible". // hResult = comFunc.call(varChild, pszString); comFunc = new COMDispFunction(comIAccessible, "accName", COMDispContext::Method); hResult = comFunc.call(varChild, pszString); // Метод "accName" в COM-объекте класса "IAccessible" возвратил код ошибки 0x80020003 (DISP_E_MEMBERNOTFOUND), который означает: Запрошенный метод не существует или во время вызова была осуществлена попытка присвоить некоторое значение свойству, доступному только для чтения. // comIAccessible.accName(varChild, pszString); // пустая строка // comIAccessible.get_accName(varChild, pszString); // ошибка info(pszString.toString()); info(pszString.bStr()); } получаю интерфейс IAccessible дальше непонятно как из аксапты вызвать на нем метод. Попробовал через Com - он даже его видит, но что-то с числом параметров ругается. Натравил на oleacc.dll мастер оберток com объектов в аксапте. А он видит интерфейс IAccessible но обертку делает пустую - т.е. не видит ничего в нем, нет свойств и методов. И это странно. Последний раз редактировалось Logger; 27.11.2022 в 20:28. |
|
28.11.2022, 02:48 | #11 |
Участник
|
Привет.
X++: CLRObject IAccessibleObject = System.Runtime.InteropServices.Marshal::GetObjectForIUnknown(new System.IntPtr(ptrIAccessibleBinary.dWord(0)));
System.String accName = IAccessibleObject.get_accName(); Последний раз редактировалось Товарищ ♂uatr; 28.11.2022 в 03:08. |
|
|
За это сообщение автора поблагодарили: Logger (10). |
28.11.2022, 12:13 | #12 |
Участник
|
Цитата:
Но почему то не всегда. Подправил джоб Основная проблема вот в чем : 1. При первых запусках метод iAccessibleObject.get_accName(); почему-то возвращает null А потом когда и так и сяк покрутил, вдруг раз, - его "пробило" и стал не null возвращать. Как-то это стремно. То работает то нет. словно изначально не все подгрузилось и проинициализировалось. 2. Если при вызове dllFuncAccessibleObjectFromWindow.call() передавать не #OBJID_WINDOW, а #OBJID_CLIENT то возвращаются null и для iAccessibleObject.get_accName() и для iAccessibleObject.get_accDescription(). В то время как в консольном приложении на C# возвращаются непустые значения. Для get_accName - имя AOT объекта а для get_accDescription полный путь в AOT. И именно этот вариант и хотелось задействовать. X++: static void AotInspectWindows(Args _args) { HWND hwnd = 67552; // HWND hwnd = 0; DLL dllOleacc; DLLFunction dllFuncAccessibleObjectFromWindow; Binary guidBinary; // guid guidXpp; Binary ptrIAccessibleBinary; int hResult; COM comIAccessible; COMDispFunction comFunc; COMVariant varChild; COMVariant pszString; CLRObject iAccessibleObject; System.String accName; System.String accDescription; int64 obj_clientUint = 0xFFFFFFFC; #define.OBJID_WINDOW (0x00000000) #define.OBJID_SYSMENU (0xFFFFFFFF) #define.OBJID_TITLEBAR (0xFFFFFFFE) #define.OBJID_MENU (0xFFFFFFFD) #define.OBJID_CLIENT (0xFFFFFFFC) #define.OBJID_VSCROLL (0xFFFFFFFB) #define.OBJID_HSCROLL (0xFFFFFFFA) #define.OBJID_SIZEGRIP (0xFFFFFFF9) #define.OBJID_CARET (0xFFFFFFF8) #define.OBJID_CURSOR (0xFFFFFFF7) #define.OBJID_ALERT (0xFFFFFFF6) #define.OBJID_SOUND (0xFFFFFFF5) Exception infoCon(container _con, str _delimiter = " ", URL helpUrl = '', SysInfoAction _sysInfoAction = null) { SysInfoLogStr txt = con2Str(_con, _delimiter); ; return infolog.add(Exception::Info, getPrefix()+txt, helpUrl, _sysInfoAction, false); } Binary guid2Binary(str _guidStr) { guid guidVar; System.Guid guidNet; System.Byte[] buffer; System.IO.MemoryStream stream; Binary ret; ; guidVar = str2guid(_guidStr); stream = new System.IO.MemoryStream(); // stream.Write(guidVar); guidNet = guidVar; buffer = guidNet.ToByteArray(); // infoCon(["length", CLRInterop::getAnyTypeForObject(buffer.get_Length())]); stream.Write(buffer, 0, 16); // info(ClrMethod_MRC::toString(stream)); // info(ClrMethod_MRC::toString(stream.get_Length())); ret = Binary::constructFromMemoryStream(stream); /* // info(ret.xml()); info(int2Hex(ret.byte(0), 2)); info(int2Hex(ret.byte(1), 2)); info(int2Hex(ret.byte(2), 2)); info(int2Hex(ret.byte(3), 2)); info(int2Hex(ret.byte(4), 2)); info(int2Hex(ret.byte(5), 2)); info(int2Hex(ret.byte(6), 2)); info(int2Hex(ret.byte(7), 2)); info(int2Hex(ret.byte(8), 2)); info(int2Hex(ret.byte(9), 2)); info(int2Hex(ret.byte(10), 2)); info(int2Hex(ret.byte(11), 2)); info(int2Hex(ret.byte(12), 2)); info(int2Hex(ret.byte(13), 2)); info(int2Hex(ret.byte(14), 2)); info(int2Hex(ret.byte(15), 2)); info(''); */ stream.Close(); return ret; } ; dllOleacc = new DLL('oleacc.dll'); dllFuncAccessibleObjectFromWindow = new DLLFunction(dllOleacc, 'AccessibleObjectFromWindow'); // [url]https://learn.microsoft.com/en-us/windows/win32/api/oleacc/nf-oleacc-accessibleobjectfromwindow[/url] // HRESULT AccessibleObjectFromWindow( // [in] HWND hwnd, // [in] DWORD dwId, // [in] REFIID riid, // [out] void **ppvObject // ); dllFuncAccessibleObjectFromWindow.arg( ExtTypes::DWord, // [in] HWND hwnd, ExtTypes::DWord, // [in] DWORD dwId, ExtTypes::Pointer, // [in] REFIID riid, // TO DO ExtTypes::Pointer // [out] void **ppvObject // TO DO ); guidBinary = guid2Binary('{618736E0-3C3D-11CF-810C-00AA00389B71}'); // IAccessible Interface // [url]https://learn.microsoft.com/en-us/dotnet/api/accessibility.iaccessible?view=windowsdesktop-7.0[/url] // guidXpp = str2guid('{618736E0-3C3D-11CF-810C-00AA00389B71}'); /* ptrIAccessibleBinary = new Binary(8); ptrIAccessibleBinary.qWord(0, 0); */ ptrIAccessibleBinary = new Binary(4); ptrIAccessibleBinary.dWord(0, 0); infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.dWord(0)]); dllFuncAccessibleObjectFromWindow.returns(ExtTypes::DWord); // HRESULT hResult = dllFuncAccessibleObjectFromWindow.call( hwnd, #OBJID_WINDOW, // #OBJID_CLIENT, // obj_clientUint, guidBinary, // guidXpp, ptrIAccessibleBinary ); infoCon(["hResult = ", hResult]); if (hResult) { infoCon(["LastError = ", WinAPI::getLastError()]); } // infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.qWord(0)]); infoCon(["ptrIAccessibleBinary = ", ptrIAccessibleBinary.dWord(0)]); iAccessibleObject = System.Runtime.InteropServices.Marshal::GetObjectForIUnknown(new System.IntPtr(ptrIAccessibleBinary.dWord(0))); accName = iAccessibleObject.get_accName(); // accDescription = iAccessibleObject.get_accDescription(); // accName = iAccessibleObject.accName(); // accDescription = iAccessibleObject.accDescription(); // System.Runtime.InteropServices.Marshal::GetComInterfaceForObject( /* try { info(ClrMethod_MRC::toString(iAccessibleObject)); } catch { info(AifUtil::getClrErrorMessage()); return; } */ if (CLRInterop::isNull(accName)) { infoCon(["accName = ", "null"]); } else { infoCon(["accName = ", CLRInterop::getAnyTypeForObject(accName)]); } if (CLRInterop::isNull(accDescription)) { infoCon(["accDescription = ", "null"]); } else infoCon(["accDescription = ", CLRInterop::getAnyTypeForObject(accDescription)]); return; /* comIAccessible = COM::createFromInterface(ptrIAccessibleBinary.dWord(0)); // comIAccessible = new COM(); // comIAccessible.attach(ptrIAccessibleBinary.dWord(0)); // comFunc = new COMDispFunction(comIAccessible, "get_accName", COMDispContext::PropertyGet); varChild = COMVariant::createFromInt(0, COMVariantInOut::In); // CHILDID_SELF info(any2str_MRC(varChild.variantType())); // pszString = COMVariant::createFromStr('', COMVariantInOut::Out_retVal); pszString = new COMVariant( COMVariantInOut::Out_retVal, COMVariantType::VT_BSTR); // comFunc = new COMDispFunction(comIAccessible, "accName", COMDispContext::PropertyGet); // hResult = comFunc.call(varChild, pszString); Метод "accName" в COM-объекте класса "IAccessible" возвратил код ошибки 0x8002000E (DISP_E_BADPARAMCOUNT), который означает: Число аргументов, указанных в вызове функции, отличается от числа аргументов в объявлении метода. // comFunc = new COMDispFunction(comIAccessible, "get_accName", COMDispContext::Method); // Метод "get_accName" не поддерживается интерфейсом Automation COM-объекта класса "IAccessible". // hResult = comFunc.call(varChild, pszString); comFunc = new COMDispFunction(comIAccessible, "accName", COMDispContext::Method); hResult = comFunc.call(varChild, pszString); // Метод "accName" в COM-объекте класса "IAccessible" возвратил код ошибки 0x80020003 (DISP_E_MEMBERNOTFOUND), который означает: Запрошенный метод не существует или во время вызова была осуществлена попытка присвоить некоторое значение свойству, доступному только для чтения. // comIAccessible.accName(varChild, pszString); // пустая строка // comIAccessible.get_accName(varChild, pszString); // ошибка info(pszString.toString()); info(pszString.bStr()); */ } |
|
28.11.2022, 15:42 | #13 |
Участник
|
Попробуй так значения получать:
X++: accName = iAccessibleObject.get_accName(0); accDescription = iAccessibleObject.get_accDescription(0); |
|
|
За это сообщение автора поблагодарили: Logger (30). |
28.11.2022, 15:47 | #14 |
Участник
|
да, так работает.
Круто. Как же я про индекс то не заметил ) |
|
22.01.2023, 14:32 | #15 |
Участник
|
Оформил в нормальном виде.
Сделан класс AxWindowInfo В нем метод getWindowText нужно использовать этот метод вместо WinApi::getWindowText (для получения заголовка окна) в Tabax Sidax Dev_Toolbar итп утилитах. Тогда заголовки в тулбаре ведут себя также как в ax3-ax4, имеют информативный вид. Проверено в 2012 и 2009 версиях. Последний раз редактировалось Logger; 22.01.2023 в 14:40. |
|
|
За это сообщение автора поблагодарили: Raven Melancholic (10), S.Kuskov (10), sukhanchik (20), Товарищ ♂uatr (4), gl00mie (10), DarkSpirit22 (3). |
Теги |
aot, sidax, tabax, toolbar |
|
|