| /* | |
| * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include "jaccesswalker.h" | |
| #include "AccessInfo.h" | |
| HWND ourHwnd; | |
| HWND topLevelWindow; | |
| int depth = -1; | |
| FILE *logfile; | |
| HMENU popupMenu; | |
| char theJaccesswalkerClassName[] = "JaccesswalkerWin"; | |
| char theAccessInfoClassName[] = "AccessInfoWin"; | |
| HWND theJaccesswalkerWindow; | |
| HWND theTreeControlWindow; | |
| HINSTANCE theInstance; | |
| Jaccesswalker *theJaccesswalker; | |
| AccessibleNode *theSelectedNode; | |
| AccessibleNode *thePopupNode; | |
| AccessibleContext theSelectedAccessibleContext; | |
| HWND hwndTV; // handle of tree-view control | |
| int APIENTRY WinMain(HINSTANCE hInstance, | |
| HINSTANCE hPrevInstance, | |
| LPSTR lpCmdLine, | |
| int nCmdShow) | |
| { | |
| if (logfile == null) { | |
| logfile = fopen(JACCESSWALKER_LOG, "w"); // overwrite existing log file | |
| logString(logfile, "Starting jaccesswalker.exe %s\n", getTimeAndDate()); | |
| } | |
| theInstance = hInstance; | |
| // start Jaccesswalker | |
| theJaccesswalker = new Jaccesswalker(nCmdShow); | |
| return 0; | |
| } | |
| Jaccesswalker::Jaccesswalker(int nCmdShow) { | |
| HWND hwnd; | |
| static char szAppName[] = "jaccesswalker"; | |
| static char szMenuName[] = "JACCESSWALKERMENU"; | |
| MSG msg; | |
| WNDCLASSEX wc; | |
| // jaccesswalker window | |
| wc.cbSize = sizeof(wc); | |
| wc.style = CS_HREDRAW | CS_VREDRAW; | |
| wc.lpfnWndProc = WinProc; | |
| wc.cbClsExtra = 0; | |
| wc.cbWndExtra = 0; | |
| wc.hInstance = theInstance; | |
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
| wc.hCursor = LoadCursor(NULL, IDI_APPLICATION); | |
| wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); | |
| wc.lpszMenuName = szMenuName; | |
| wc.lpszClassName = szAppName; | |
| wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); | |
| RegisterClassEx(&wc); | |
| // AccessInfo Window | |
| wc.cbSize = sizeof(WNDCLASSEX); | |
| wc.hInstance = theInstance; | |
| wc.lpszClassName = theAccessInfoClassName; | |
| wc.lpfnWndProc = (WNDPROC)AccessInfoWindowProc; | |
| wc.style = 0; | |
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
| wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); | |
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |
| wc.lpszMenuName = ""; | |
| wc.cbClsExtra = 0; | |
| wc.cbWndExtra = 0; | |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); | |
| RegisterClassEx(&wc); | |
| // create the jaccesswalker window | |
| hwnd = CreateWindow(szAppName, | |
| szAppName, | |
| WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, | |
| CW_USEDEFAULT, | |
| CW_USEDEFAULT, | |
| CW_USEDEFAULT, | |
| NULL, | |
| NULL, | |
| theInstance, | |
| NULL); | |
| ourHwnd = hwnd; | |
| /* Initialize the common controls. */ | |
| INITCOMMONCONTROLSEX cc; | |
| cc.dwSize = sizeof(INITCOMMONCONTROLSEX); | |
| cc.dwICC = ICC_TREEVIEW_CLASSES; | |
| InitCommonControlsEx(&cc); | |
| ShowWindow(hwnd, nCmdShow); | |
| UpdateWindow(hwnd); | |
| BOOL result = initializeAccessBridge(); | |
| if (result != FALSE) { | |
| while (GetMessage(&msg, NULL, 0, 0)) { | |
| TranslateMessage(&msg); | |
| DispatchMessage(&msg); | |
| } | |
| shutdownAccessBridge(); | |
| } | |
| } | |
| /* | |
| * the jaccesswalker window proc | |
| */ | |
| LRESULT CALLBACK WinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { | |
| int command; | |
| short width, height; | |
| switch(iMsg) { | |
| case WM_CREATE: | |
| // create the accessibility tree view | |
| theTreeControlWindow = CreateATreeView(hwnd); | |
| // load the popup menu | |
| popupMenu = LoadMenu(theInstance, "PopupMenu"); | |
| popupMenu = GetSubMenu(popupMenu, 0); | |
| break; | |
| case WM_CLOSE: | |
| EndDialog(hwnd, TRUE); | |
| PostQuitMessage (0); | |
| break; | |
| case WM_SIZE: | |
| width = LOWORD(lParam); | |
| height = HIWORD(lParam); | |
| SetWindowPos(theTreeControlWindow, NULL, 0, 0, width, height, 0); | |
| return FALSE; // let windows finish handling this | |
| case WM_COMMAND: | |
| command = LOWORD(wParam); | |
| switch(command) { | |
| case cExitMenuItem: | |
| EndDialog(hwnd, TRUE); | |
| PostQuitMessage (0); | |
| break; | |
| case cRefreshTreeItem: | |
| // update the accessibility tree | |
| theJaccesswalker->buildAccessibilityTree(); | |
| break; | |
| case cAPIMenuItem: | |
| // open a new window with the Accessibility API in it for the | |
| // selected element in the tree | |
| if (theSelectedNode != (AccessibleNode *) 0) { | |
| theSelectedNode->displayAPIWindow(); | |
| } | |
| break; | |
| case cAPIPopupItem: | |
| // open a new window with the Accessibility API in it for the | |
| // element in the tree adjacent to the popup menu | |
| if (thePopupNode != (AccessibleNode *) 0) { | |
| thePopupNode->displayAPIWindow(); | |
| } | |
| break; | |
| } | |
| break; | |
| case WM_NOTIFY: // receive tree messages | |
| NMTREEVIEW *nmptr = (LPNMTREEVIEW) lParam; | |
| switch (nmptr->hdr.code) { | |
| case TVN_SELCHANGED: | |
| // get the selected tree node | |
| theSelectedNode = (AccessibleNode *) nmptr->itemNew.lParam; | |
| break; | |
| case NM_RCLICK: | |
| // display a popup menu over the tree node | |
| POINT p; | |
| GetCursorPos(&p); | |
| TrackPopupMenu(popupMenu, 0, p.x, p.y, 0, hwnd, NULL); | |
| // get the tree node under the popup menu | |
| TVHITTESTINFO hitinfo; | |
| ScreenToClient(theTreeControlWindow, &p); | |
| hitinfo.pt = p; | |
| HTREEITEM node = TreeView_HitTest(theTreeControlWindow, &hitinfo); | |
| if (node != null) { | |
| TVITEMEX tvItem; | |
| tvItem.hItem = node; | |
| if (TreeView_GetItem(hwndTV, &tvItem) == TRUE) { | |
| thePopupNode = (AccessibleNode *)tvItem.lParam; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| return DefWindowProc(hwnd, iMsg, wParam, lParam); | |
| } | |
| /* | |
| * Accessibility information window proc | |
| */ | |
| LRESULT CALLBACK AccessInfoWindowProc(HWND hWnd, UINT message, | |
| UINT wParam, LONG lParam) { | |
| short width, height; | |
| HWND dlgItem; | |
| switch (message) { | |
| case WM_CREATE: | |
| RECT rcClient; // dimensions of client area | |
| HWND hwndEdit; // handle of tree-view control | |
| // Get the dimensions of the parent window's client area, | |
| // and create the edit control. | |
| GetClientRect(hWnd, &rcClient); | |
| hwndEdit = CreateWindow("Edit", | |
| "", | |
| WS_VISIBLE | WS_TABSTOP | WS_CHILD | | |
| ES_MULTILINE | ES_AUTOVSCROLL | | |
| ES_READONLY | WS_VSCROLL, | |
| 0, 0, rcClient.right, rcClient.bottom, | |
| hWnd, | |
| (HMENU) cAccessInfoText, | |
| theInstance, | |
| NULL); | |
| break; | |
| case WM_CLOSE: | |
| DestroyWindow(hWnd); | |
| break; | |
| case WM_SIZE: | |
| width = LOWORD(lParam); | |
| height = HIWORD(lParam); | |
| dlgItem = GetDlgItem(hWnd, cAccessInfoText); | |
| SetWindowPos(dlgItem, NULL, 0, 0, width, height, 0); | |
| return FALSE; // let windows finish handling this | |
| break; | |
| default: | |
| return DefWindowProc(hWnd, message, wParam, lParam); | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Build a tree (and the treeview control) of all accessible Java components | |
| * | |
| */ | |
| void Jaccesswalker::buildAccessibilityTree() { | |
| TreeView_DeleteAllItems (theTreeControlWindow); | |
| // have MS-Windows call EnumWndProc() with all of the top-level windows | |
| EnumWindows((WNDENUMPROC) EnumWndProc, NULL); | |
| } | |
| /** | |
| * Create (and display) the accessible component nodes of a parent AccessibleContext | |
| * | |
| */ | |
| BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam) { | |
| if (IsJavaWindow(hwnd)) { | |
| long vmID; | |
| AccessibleContext ac; | |
| if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac) == TRUE) { | |
| theJaccesswalker->addComponentNodes(vmID, ac, (AccessibleNode *) NULL, | |
| hwnd, TVI_ROOT, theTreeControlWindow); | |
| } | |
| topLevelWindow = hwnd; | |
| } else { | |
| char szClass [MAX_PATH] = {0}; | |
| ::GetClassNameA(hwnd, szClass, sizeof(szClass) - 1); | |
| if ( ( 0 == ::strcmp(szClass, "IEFrame") ) | |
| || ( 0 == ::strcmp(szClass, "MozillaUIWindowClass") ) ) { | |
| EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /* | |
| Detects whether or not the specified Java window is one from which no useable | |
| information can be obtained. | |
| This function tests for various scenarios I have seen in Java applets where the | |
| Java applet had no meaningful accessible information. It does not detect all | |
| scenarios, just the most common ones. | |
| */ | |
| BOOL IsInaccessibleJavaWindow(const HWND hwnd) | |
| { | |
| BOOL ret_val ( FALSE ); | |
| { | |
| BOOL bT ( FALSE ); | |
| long vmIdWindow ( 0 ); | |
| AccessibleContext acWindow ( 0 ); | |
| bT = GetAccessibleContextFromHWND(hwnd, &vmIdWindow, &acWindow); | |
| if ( ( bT ) && ( 0 != vmIdWindow ) && ( 0 != acWindow ) ) { | |
| AccessibleContextInfo infoWindow = {0}; | |
| bT = GetAccessibleContextInfo(vmIdWindow, acWindow, &infoWindow); | |
| if ( ( bT ) | |
| && ( 0 == infoWindow.name [0] ) | |
| && ( 0 == infoWindow.description [0] ) | |
| && ( 0 == ::wcscmp(infoWindow.role_en_US, L"frame") ) ) { | |
| if ( 0 == infoWindow.childrenCount ) { | |
| ret_val = TRUE; | |
| } else if ( 1 == infoWindow.childrenCount ) { | |
| AccessibleContext acChild ( 0 ); | |
| acChild = | |
| GetAccessibleChildFromContext(vmIdWindow, acWindow, 0); | |
| if ( NULL != acChild ) { | |
| AccessibleContextInfo infoChild = {0}; | |
| bT = GetAccessibleContextInfo( vmIdWindow, acChild, | |
| &infoChild ); | |
| if ( ( bT ) | |
| && ( 0 == infoChild.name [0] ) | |
| && ( 0 == infoChild.description [0] ) | |
| && ( 0 == ::wcscmp(infoChild.role_en_US, L"panel") ) | |
| && ( 1 == infoChild.childrenCount ) ) { | |
| AccessibleContext acChild1 ( 0 ); | |
| acChild1 = GetAccessibleChildFromContext( vmIdWindow, | |
| acChild, 0); | |
| if ( NULL != acChild1 ) { | |
| AccessibleContextInfo infoChild1 = {0}; | |
| bT = GetAccessibleContextInfo( vmIdWindow, | |
| acChild1, &infoChild1 ); | |
| if ( ( bT ) | |
| && ( 0 == infoChild1.name [0] ) | |
| && ( 0 == infoChild1.description [0] ) | |
| && ( 0 == ::wcscmp(infoChild1.role_en_US, L"frame") ) | |
| && ( 0 == infoChild1.childrenCount ) ) { | |
| ret_val = TRUE; | |
| } else if ( ( bT ) | |
| && ( 0 == infoChild1.name [0] ) | |
| && ( 0 == infoChild1.description [0] ) | |
| && ( 0 == ::wcscmp( infoChild1.role_en_US, | |
| L"panel") ) | |
| && ( 1 == infoChild1.childrenCount ) ) { | |
| AccessibleContext acChild2 ( 0 ); | |
| acChild2 = GetAccessibleChildFromContext( | |
| vmIdWindow, acChild1, 0 ); | |
| if ( NULL != acChild2 ) { | |
| AccessibleContextInfo infoChild2 = {0}; | |
| bT = GetAccessibleContextInfo( | |
| vmIdWindow, acChild2, &infoChild2 ); | |
| if ( ( bT ) | |
| && ( 0 == infoChild2.name [0] ) | |
| && ( 0 == infoChild2.description [0] ) | |
| && ( 0 == ::wcscmp( infoChild2.role_en_US, | |
| L"frame") ) | |
| && ( 0 == infoChild2.childrenCount ) ) { | |
| ret_val = TRUE; | |
| } | |
| } | |
| } | |
| } | |
| } else if ( ( bT ) | |
| && ( 0 == infoChild.name [0] ) | |
| && ( 0 == infoChild.description [0] ) | |
| && ( 0 == ::wcscmp( infoChild.role_en_US, | |
| L"canvas") ) | |
| && ( 0 == infoChild.childrenCount ) ) { | |
| ret_val = TRUE; | |
| } | |
| } | |
| } | |
| } else if ( ( bT ) | |
| && ( 0 == infoWindow.name [0] ) | |
| && ( 0 == infoWindow.description [0] ) | |
| && ( 0 == ::wcscmp(infoWindow.role_en_US, L"panel") ) ) { | |
| if ( 1 == infoWindow.childrenCount ) { | |
| AccessibleContext acChild ( 0 ); | |
| acChild = GetAccessibleChildFromContext( vmIdWindow, | |
| acWindow, 0 ); | |
| if ( NULL != acChild ) { | |
| AccessibleContextInfo infoChild = {0}; | |
| bT = GetAccessibleContextInfo( vmIdWindow, | |
| acChild, &infoChild ); | |
| if ( ( bT ) | |
| && ( 0 == infoChild.name [0] ) | |
| && ( 0 == infoChild.description [0] ) | |
| && ( 0 == ::wcscmp(infoChild.role_en_US, L"frame") ) | |
| && ( 0 == infoChild.childrenCount ) ) { | |
| ret_val = TRUE; | |
| } else if ( ( bT ) | |
| && ( 0 == infoChild.name [0] ) | |
| && ( 0 == infoChild.description [0] ) | |
| && ( 0 == ::wcscmp( infoChild.role_en_US, | |
| L"panel") ) | |
| && ( 1 == infoChild.childrenCount ) ) { | |
| AccessibleContext acChild1 ( 0 ); | |
| acChild1 = GetAccessibleChildFromContext( vmIdWindow, | |
| acChild, 0); | |
| if ( NULL != acChild1 ) { | |
| AccessibleContextInfo infoChild1 = {0}; | |
| bT = GetAccessibleContextInfo( vmIdWindow, | |
| acChild1, | |
| &infoChild1 ); | |
| if ( ( bT ) | |
| && ( 0 == infoChild1.name [0] ) | |
| && ( 0 == infoChild1.description [0] ) | |
| && ( 0 == ::wcscmp( infoChild1.role_en_US, | |
| L"frame") ) | |
| && ( 0 == infoChild1.childrenCount ) ) { | |
| ret_val = TRUE; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } else if ( FALSE == bT ) { | |
| ret_val = TRUE; | |
| } | |
| } | |
| return ret_val; | |
| } | |
| BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) | |
| { | |
| if ( ( IsJavaWindow(hwnd) ) | |
| && ( FALSE == IsInaccessibleJavaWindow(hwnd) ) ) { | |
| long vmID ( 0 ); | |
| AccessibleContext ac ( 0 ); | |
| if ( TRUE == GetAccessibleContextFromHWND(hwnd, &vmID, &ac) ) { | |
| theJaccesswalker->addComponentNodes( | |
| vmID, ac, (AccessibleNode *) NULL, | |
| hwnd, TVI_ROOT, theTreeControlWindow); | |
| } | |
| topLevelWindow = hwnd; | |
| } else { | |
| EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); | |
| } | |
| return TRUE; | |
| } | |
| // CreateATreeView - creates a tree-view control. | |
| // Returns the handle of the new control if successful or NULL | |
| // otherwise. | |
| // hwndParent - handle of the control's parent window | |
| HWND CreateATreeView(HWND hwndParent) { | |
| RECT rcClient; // dimensions of client area | |
| // Get the dimensions of the parent window's client area, and create | |
| // the tree-view control. | |
| GetClientRect(hwndParent, &rcClient); | |
| hwndTV = CreateWindow(WC_TREEVIEW, | |
| "", | |
| WS_VISIBLE | WS_TABSTOP | WS_CHILD | | |
| TVS_HASLINES | TVS_HASBUTTONS | | |
| TVS_LINESATROOT, | |
| 0, 0, rcClient.right, rcClient.bottom, | |
| hwndParent, | |
| (HMENU) cTreeControl, | |
| theInstance, | |
| NULL); | |
| return hwndTV; | |
| } | |
| /** | |
| * Create (and display) the accessible component nodes of a parent AccessibleContext | |
| * | |
| */ | |
| void Jaccesswalker::addComponentNodes(long vmID, AccessibleContext context, | |
| AccessibleNode *parent, HWND hwnd, | |
| HTREEITEM treeNodeParent, HWND treeWnd) { | |
| AccessibleNode *newNode = new AccessibleNode( vmID, context, parent, hwnd, | |
| treeNodeParent ); | |
| AccessibleContextInfo info; | |
| if (GetAccessibleContextInfo(vmID, context, &info) != FALSE) { | |
| char s[LINE_BUFSIZE]; | |
| wsprintf(s, "%ls", info.name); | |
| newNode->setAccessibleName(s); | |
| wsprintf(s, "%ls", info.role); | |
| newNode->setAccessibleRole(s); | |
| wsprintf(s, "%ls [%ls]", info.name, info.role); | |
| TVITEM tvi; | |
| tvi.mask = TVIF_PARAM | TVIF_TEXT; | |
| tvi.pszText = (char *) s; // Accessible name and role | |
| tvi.cchTextMax = (int)strlen(s); | |
| tvi.lParam = (LPARAM) newNode; // Accessibility information | |
| TVINSERTSTRUCT tvis; | |
| tvis.hParent = treeNodeParent; | |
| tvis.hInsertAfter = TVI_LAST; | |
| tvis.item = tvi; | |
| HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); | |
| for (int i = 0; i < info.childrenCount; i++) { | |
| addComponentNodes(vmID, GetAccessibleChildFromContext(vmID, context, i), | |
| newNode, hwnd, treeNodeItem, treeWnd); | |
| } | |
| } else { | |
| char s[LINE_BUFSIZE]; | |
| snprintf( s, sizeof(s), | |
| "ERROR calling GetAccessibleContextInfo; vmID = %lX, context = %p", | |
| vmID, (void*)context ); | |
| TVITEM tvi; | |
| tvi.mask = TVIF_PARAM | TVIF_TEXT; // text and lParam are only valid parts | |
| tvi.pszText = (char *) s; | |
| tvi.cchTextMax = (int)strlen(s); | |
| tvi.lParam = (LPARAM) newNode; | |
| TVINSERTSTRUCT tvis; | |
| tvis.hParent = treeNodeParent; | |
| tvis.hInsertAfter = TVI_LAST; // make tree in order given | |
| tvis.item = tvi; | |
| HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); | |
| } | |
| } | |
| // ----------------------------- | |
| /** | |
| * Create an AccessibleNode | |
| * | |
| */ | |
| AccessibleNode::AccessibleNode(long JavaVMID, AccessibleContext context, | |
| AccessibleNode *parent, HWND hwnd, | |
| HTREEITEM parentTreeNodeItem) { | |
| vmID = JavaVMID; | |
| ac = context; | |
| parentNode = parent; | |
| baseHWND = hwnd; | |
| treeNodeParent = parentTreeNodeItem; | |
| // setting accessibleName and accessibleRole not done here, | |
| // in order to minimize calls to the AccessBridge | |
| // (since such a call is needed to enumerate children) | |
| } | |
| /** | |
| * Destroy an AccessibleNode | |
| * | |
| */ | |
| AccessibleNode::~AccessibleNode() { | |
| ReleaseJavaObject(vmID, ac); | |
| } | |
| /** | |
| * Set the accessibleName string | |
| * | |
| */ | |
| void AccessibleNode::setAccessibleName(char *name) { | |
| strncpy(accessibleName, name, MAX_STRING_SIZE); | |
| } | |
| /** | |
| * Set the accessibleRole string | |
| * | |
| */ | |
| void AccessibleNode::setAccessibleRole(char *role) { | |
| strncpy(accessibleRole, role, SHORT_STRING_SIZE); | |
| } | |
| /** | |
| * Create an API window to show off the info for this AccessibleContext | |
| */ | |
| BOOL AccessibleNode::displayAPIWindow() { | |
| HWND apiWindow = CreateWindow(theAccessInfoClassName, | |
| "Java Accessibility API view", | |
| WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, | |
| CW_USEDEFAULT, | |
| 600, | |
| 750, | |
| HWND_DESKTOP, | |
| NULL, | |
| theInstance, | |
| (void *) NULL); | |
| if (!apiWindow) { | |
| printError("cannot create API window"); | |
| return FALSE; | |
| } | |
| char buffer[HUGE_BUFSIZE]; | |
| buffer[0] = '\0'; | |
| getAccessibleInfo(vmID, ac, buffer, sizeof(buffer)); | |
| displayAndLog(apiWindow, cAccessInfoText, logfile, buffer); | |
| ShowWindow(apiWindow, SW_SHOWNORMAL); | |
| UpdateWindow(apiWindow); | |
| return TRUE; | |
| } | |