From acf215fe000b3c59bdd60158cc8e3a7619396807 Mon Sep 17 00:00:00 2001 From: Edward Jakubowski Date: Tue, 29 Apr 2014 23:33:10 -0400 Subject: [PATCH] Update native bridge to UI Automation for speed and reliability Native UI Automation bridge redesigned for speed and reliability. The bridge library has been renamed to UiaBridge. Native components use VS 2010 C++ and .Net 4. --- .classpath | 2 +- build.xml | 12 +- native/WpfBridge/WpfAutomation.cpp | 351 --------------- native/WpfBridge/WpfAutomation.h | 47 -- native/WpfBridge/bin/wpfbridge32.dll | Bin 78848 -> 0 bytes native/WpfBridge/bin/wpfbridge64.dll | Bin 93184 -> 0 bytes native/WpfBridge/org_synthuse_WpfBridge.cpp | 300 ------------- native/WpfBridge/org_synthuse_WpfBridge.h | 141 ------ native/uiabridge.sln | 36 ++ native/uiabridge.suo | Bin 0 -> 14336 bytes .../{WpfBridge => uiabridge}/AssemblyInfo.cpp | 16 +- native/{WpfBridge => uiabridge}/Global.cpp | 6 +- native/{WpfBridge => uiabridge}/Global.h | 5 +- native/{WpfBridge => uiabridge}/ReadMe.txt | 5 +- native/uiabridge/Stdafx.cpp | 6 + native/{WpfBridge => uiabridge}/Stdafx.h | 0 native/{WpfBridge => uiabridge}/app.ico | Bin native/{WpfBridge => uiabridge}/app.rc | Bin native/{WpfBridge => uiabridge}/build.bat | 0 native/uiabridge/org_synthuse_UiaBridge.cpp | 157 +++++++ native/uiabridge/org_synthuse_UiaBridge.h | 85 ++++ native/{WpfBridge => uiabridge}/resource.h | 0 native/uiabridge/uiabridge.cpp | 422 ++++++++++++++++++ native/uiabridge/uiabridge.h | 46 ++ .../uiabridge.vcxproj} | 19 +- .../uiabridge.vcxproj.filters} | 16 +- .../uiabridge.vcxproj.user} | 0 native/uiabtest/AssemblyInfo.cpp | 40 ++ native/uiabtest/ReadMe.txt | 35 ++ native/uiabtest/app.ico | Bin 0 -> 1078 bytes native/uiabtest/app.rc | Bin 0 -> 2558 bytes native/uiabtest/resource.h | 3 + .../Stdafx.cpp => uiabtest/stdafx.cpp} | 4 +- native/uiabtest/stdafx.h | 11 + native/uiabtest/uiabtest.cpp | 71 +++ native/uiabtest/uiabtest.vcxproj | 169 +++++++ native/uiabtest/uiabtest.vcxproj.filters | 47 ++ native/uiabtest/uiabtest.vcxproj.user | 3 + src/org/synthuse/RobotMacro.java | 132 +++++- src/org/synthuse/SynthuseDlg.java | 9 +- .../{WpfBridge.java => UiaBridge.java} | 65 ++- src/org/synthuse/WindowInfo.java | 54 ++- src/org/synthuse/WindowsEnumeratedXml.java | 48 +- src/org/synthuse/XpathManager.java | 57 +-- src/org/synthuse/commands/BaseCommand.java | 5 +- src/org/synthuse/test/UnitTestHelper.java | 2 +- src/org/synthuse/test/WpfBridgeTest.java | 125 ------ synthuse.properties | 6 +- 48 files changed, 1453 insertions(+), 1105 deletions(-) delete mode 100644 native/WpfBridge/WpfAutomation.cpp delete mode 100644 native/WpfBridge/WpfAutomation.h delete mode 100644 native/WpfBridge/bin/wpfbridge32.dll delete mode 100644 native/WpfBridge/bin/wpfbridge64.dll delete mode 100644 native/WpfBridge/org_synthuse_WpfBridge.cpp delete mode 100644 native/WpfBridge/org_synthuse_WpfBridge.h create mode 100644 native/uiabridge.sln create mode 100644 native/uiabridge.suo rename native/{WpfBridge => uiabridge}/AssemblyInfo.cpp (72%) rename native/{WpfBridge => uiabridge}/Global.cpp (68%) rename native/{WpfBridge => uiabridge}/Global.h (70%) rename native/{WpfBridge => uiabridge}/ReadMe.txt (67%) create mode 100644 native/uiabridge/Stdafx.cpp rename native/{WpfBridge => uiabridge}/Stdafx.h (100%) rename native/{WpfBridge => uiabridge}/app.ico (100%) rename native/{WpfBridge => uiabridge}/app.rc (100%) rename native/{WpfBridge => uiabridge}/build.bat (100%) create mode 100644 native/uiabridge/org_synthuse_UiaBridge.cpp create mode 100644 native/uiabridge/org_synthuse_UiaBridge.h rename native/{WpfBridge => uiabridge}/resource.h (100%) create mode 100644 native/uiabridge/uiabridge.cpp create mode 100644 native/uiabridge/uiabridge.h rename native/{WpfBridge/WpfBridge.vcxproj => uiabridge/uiabridge.vcxproj} (93%) rename native/{WpfBridge/WpfBridge.vcxproj.filters => uiabridge/uiabridge.vcxproj.filters} (90%) rename native/{WpfBridge/WpfBridge.vcxproj.user => uiabridge/uiabridge.vcxproj.user} (100%) create mode 100644 native/uiabtest/AssemblyInfo.cpp create mode 100644 native/uiabtest/ReadMe.txt create mode 100644 native/uiabtest/app.ico create mode 100644 native/uiabtest/app.rc create mode 100644 native/uiabtest/resource.h rename native/{WpfBridge/Stdafx.cpp => uiabtest/stdafx.cpp} (75%) create mode 100644 native/uiabtest/stdafx.h create mode 100644 native/uiabtest/uiabtest.cpp create mode 100644 native/uiabtest/uiabtest.vcxproj create mode 100644 native/uiabtest/uiabtest.vcxproj.filters create mode 100644 native/uiabtest/uiabtest.vcxproj.user rename src/org/synthuse/{WpfBridge.java => UiaBridge.java} (72%) delete mode 100644 src/org/synthuse/test/WpfBridgeTest.java diff --git a/.classpath b/.classpath index 3c497e1..a4a6148 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,7 @@ - + diff --git a/build.xml b/build.xml index a4edace..2a69e30 100644 --- a/build.xml +++ b/build.xml @@ -58,7 +58,7 @@ - + @@ -69,7 +69,13 @@ - + + + + + + + + - \ No newline at end of file diff --git a/native/WpfBridge/WpfAutomation.cpp b/native/WpfBridge/WpfAutomation.cpp deleted file mode 100644 index f8e4a9f..0000000 --- a/native/WpfBridge/WpfAutomation.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 2014, Synthuse.org - * Released under the Apache Version 2.0 License. - * - * last modified by ejakubowski7@gmail.com -*/ -#include "StdAfx.h" -#include "WpfAutomation.h" - -using namespace System; -using namespace System::Windows::Automation; - -WpfAutomation::WpfAutomation(void) -{ - this->frameworkId = WpfAutomation::DEFAULT_FRAMEWORK; - this->touchableOnly = true; -} - -void WpfAutomation::setFrameworkId(System::String ^propertyValue) -{ - this->frameworkId = propertyValue; -} - -void WpfAutomation::setTouchableOnly(System::Boolean val) -{ - this->touchableOnly = val; -} - -System::Windows::Automation::Condition ^ WpfAutomation::getSearchConditions() -{ - array ^cons = nullptr; - if (this->touchableOnly) - { - cons = gcnew array(3); - cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework - cons[1] = gcnew PropertyCondition(AutomationElement::IsEnabledProperty, true);//is enabled - cons[2] = gcnew PropertyCondition(AutomationElement::IsOffscreenProperty, false);// is off screen - } - else - { - cons = gcnew array(1); - cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework - } - System::Windows::Automation::Condition ^result = gcnew System::Windows::Automation::AndCondition(cons); - return result; -} - -array ^ WpfAutomation::convertRuntimeIdString(System::String ^runtimeIdValue) -{ - System::String ^delim = L"-"; - array ^idStrArray = runtimeIdValue->Split(delim->ToCharArray()); - array ^idArray = gcnew array(idStrArray->Length); - for(System::Int32 i = 0 ; i < idStrArray->Length ; i++) - { - idArray[i] = System::Int32::Parse(idStrArray[i]); - } - return idArray; -} - -AutomationElement ^ WpfAutomation::findAutomationElementById(System::String ^runtimeIdValue, System::Boolean unfiltered) -{ - if (runtimeIdValue == nullptr || runtimeIdValue->Equals(L"")) - return AutomationElement::RootElement; - array ^idArray = this->convertRuntimeIdString(runtimeIdValue); - if (unfiltered) - return AutomationElement::RootElement->FindFirst(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::RuntimeIdProperty, idArray)); - //Condition ^pcFramework = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId); - Condition ^pcRunId = gcnew PropertyCondition(AutomationElement::RuntimeIdProperty, idArray); - Condition ^frameworkAndRuntimeId = gcnew AndCondition(getSearchConditions(), pcRunId); - return AutomationElement::RootElement->FindFirst(TreeScope::Descendants, frameworkAndRuntimeId); -} - -array ^ WpfAutomation::getRuntimeIdsFromCollection(System::Windows::Automation::AutomationElementCollection ^collection) -{ - array ^idStrArray = gcnew array(collection->Count); - System::Int32 count = 0; - for each(AutomationElement ^child in collection) - { - idStrArray[count] = getRuntimeIdFromElement(child); - ++count; - } - return idStrArray; -} - -System::String ^ WpfAutomation::getRuntimeIdFromElement(System::Windows::Automation::AutomationElement ^element) -{ - System::String ^result = L""; - System::Object ^currentVal = element->GetCurrentPropertyValue(AutomationElement::RuntimeIdProperty); - if (currentVal != nullptr) - { - array ^idArray = (array ^)currentVal; - for each(System::Int32 val in idArray) - { - result += System::Convert::ToString(val) + L"-"; - } - result = result->TrimEnd('-'); - //System::Console::WriteLine("id: {0}", result); - } - return result; -} - -//Descendants will walk the full tree of windows, NOT just one level of children -System::Int32 WpfAutomation::countDescendantWindows() -{ - //AutomationElementCollection ^aec = rootElem->FindAll(TreeScope::Children, Condition::TrueCondition); - AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Descendants, getSearchConditions()); - if (aec == nullptr) - return 0; - System::Int32 result = aec->Count; - //delete aec; - return result; -} - -System::Int32 WpfAutomation::countDescendantWindows(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); - if (aec == nullptr) - return 0; - System::Int32 result = aec->Count; - //delete aec; - //delete frameworkAndRuntimeId; - return result; -} - -System::Int32 WpfAutomation::countChildrenWindows() -{ - //AutomationElementCollection ^aec = rootElem->FindAll(TreeScope::Children, Condition::TrueCondition); - AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Children, getSearchConditions()); - if (aec == nullptr) - return 0; - System::Int32 result = aec->Count; - //delete aec; - return result; -} - -System::Int32 WpfAutomation::countChildrenWindows(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, getSearchConditions()); - if (aec == nullptr) - return 0; - System::Int32 result = aec->Count; - //delete aec; - //delete frameworkAndRuntimeId; - return result; -} - -array ^ WpfAutomation::enumChildrenWindowIds(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, getSearchConditions()); - if (aec == nullptr) - return nullptr; - return getRuntimeIdsFromCollection(aec); -} - -array ^ WpfAutomation::enumDescendantWindowIds(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); - if (aec == nullptr) - return nullptr; - return getRuntimeIdsFromCollection(aec); -} - -array ^ WpfAutomation::enumDescendantWindowIds(System::Int32 processId) -{ - Condition ^frameworkAndProcessId = gcnew AndCondition( - gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId), - gcnew PropertyCondition(AutomationElement::ProcessIdProperty, processId)); - AutomationElement ^parent = AutomationElement::RootElement->FindFirst(TreeScope::Descendants, frameworkAndProcessId); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); - if (aec == nullptr) - return nullptr; - return getRuntimeIdsFromCollection(aec); -} - -System::String ^ WpfAutomation::getRuntimeIdFromHandle(System::IntPtr windowHandle) -{ - //AutomationElement test = AutomationElement.FromHandle(new System.IntPtr(123123)); - AutomationElement ^target = AutomationElement::FromHandle(windowHandle); - if (target == nullptr) - return nullptr; - return getRuntimeIdFromElement(target); -} - -array ^ WpfAutomation::enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - if (parent == nullptr) - return nullptr; - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); - - //when wildcard is enabled it will pull all property names & values - System::Boolean wildcardEnabled = false; - if (properties->Equals(L"*")) - wildcardEnabled = true; - - //create array for keeping order of properties - System::String ^delim = L","; - array ^propSpltArray = properties->Split(delim->ToCharArray()); - TreeWalker ^tw = TreeWalker::ControlViewWalker; - - array ^winInfoList = gcnew array(aec->Count); - System::Int32 count = 0; - for each(AutomationElement ^child in aec) //loop through all descendants - { - array ^aps = child->GetSupportedProperties(); - array ^propValues = gcnew array(propSpltArray->Length);//keep order - System::String ^wildcardProperties = L""; - if (wildcardEnabled) { - wildcardProperties += "ParentRuntimeIdProperty:" + getRuntimeIdFromElement(tw->GetParent(child)) + ","; - //propValues = gcnew array(aps->Length +1 );//add one for parent property since it doesn't exist - } - for(int i=0 ; i < propValues->Length ; i++) - { - propValues[i] = L""; - if (propSpltArray[i]->Equals("ParentRuntimeIdProperty"))//custom property for getting parent - { - propValues[i] = getRuntimeIdFromElement(tw->GetParent(child)); - } - } - for each(AutomationProperty ^ap in aps) //loop through all supported Properties for a child - { - System::String ^currentPropertyStr = L""; //current property values - //System::Console::WriteLine("property: {0}", ap->ProgrammaticName); - System::String ^shortPropName = L" null "; - if (ap->ProgrammaticName->Contains(L".")) - shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1); - if (properties->Contains(shortPropName) || properties->Contains(ap->ProgrammaticName) || ap->ProgrammaticName->Equals(properties) || wildcardEnabled) - { - //System::Console::WriteLine("shortPropName: {0}", shortPropName); - System::Object ^currentVal = child->GetCurrentPropertyValue(ap); - if (currentVal == nullptr) - continue; - if (ap->ProgrammaticName->Equals(L"AutomationElementIdentifiers.RuntimeIdProperty")) - { - array ^idArray = (array ^)currentVal; - for each(System::Int32 val in idArray) - { - currentPropertyStr += System::Convert::ToString(val) + L"-"; - } - currentPropertyStr = currentPropertyStr->TrimEnd('-'); - //System::Console::WriteLine("id: {0}", result); - } - else//not runtimeId which is an Int32[] - { - currentPropertyStr = currentVal->ToString(); - currentPropertyStr = currentPropertyStr->Replace(",",","); - } - } - if (currentPropertyStr->Equals(L"")) //if there isn't a value skip - continue; - if (wildcardEnabled) { - wildcardProperties += shortPropName + ":" +currentPropertyStr + ","; - continue; - } - //System::Console::WriteLine("currentPropertyStr: {0}", currentPropertyStr); - //find the correct order to return this property - for(int i=0 ; i < propSpltArray->Length ; i++) - { - if (propSpltArray[i]->Equals(shortPropName) || propSpltArray[i]->Equals(ap->ProgrammaticName)) - propValues[i] = currentPropertyStr; - } - } - //output properties in the correct order - for(int i=0 ; i < propSpltArray->Length ; i++) - winInfoList[count] += propValues[i] + L","; - if (wildcardEnabled) - winInfoList[count] += wildcardProperties; - ++count; - } - return winInfoList; -} - -System::String ^ WpfAutomation::getRuntimeIdFromPoint(System::Int32 x, System::Int32 y) -{ - AutomationElement ^target = AutomationElement::FromPoint(System::Windows::Point(x,y)); - if (target == nullptr) - return nullptr; - return getRuntimeIdFromElement(target); -} - -System::String ^ WpfAutomation::getParentRuntimeId(System::String ^runtimeIdValue) -{ - AutomationElement ^target = findAutomationElementById(runtimeIdValue, true); - if (target == nullptr) - return nullptr; - TreeWalker ^tw = TreeWalker::ControlViewWalker; - AutomationElement ^parent = tw->GetParent(target); - return getRuntimeIdFromElement(parent); -} - -System::String ^ WpfAutomation::getProperty(System::String ^propertyName, System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - if (parent == nullptr) - return nullptr; - //System::Object ^currentVal = parent->GetCurrentPropertyValue(AutomationElement::RuntimeIdProperty); - array ^aps = parent->GetSupportedProperties(); - for each(AutomationProperty ^ap in aps) - { - //System::Console::WriteLine("property: {0}", ap->ProgrammaticName); - if (ap->ProgrammaticName->Contains(L"." + propertyName) || ap->ProgrammaticName->Equals(propertyName)) - { - System::Object ^currentVal = parent->GetCurrentPropertyValue(ap); - return currentVal->ToString(); - } - } - return nullptr; -} - - -array ^ WpfAutomation::getProperties(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - if (parent == nullptr) - return nullptr; - array ^aps = parent->GetSupportedProperties(); - array ^propStrArray = gcnew array(aps->Length); - System::Int32 count = 0; - for each(AutomationProperty ^ap in aps) - { - System::Object ^currentVal = parent->GetCurrentPropertyValue(ap); - if (currentVal == nullptr) - continue; - propStrArray[count] = ap->ProgrammaticName; - ++count; - } - return propStrArray; -} - -array ^ WpfAutomation::getPropertiesAndValues(System::String ^runtimeIdValue) -{ - AutomationElement ^parent = findAutomationElementById(runtimeIdValue, true); - if (parent == nullptr) - return nullptr; - array ^aps = parent->GetSupportedProperties(); - array ^propStrArray = gcnew array(aps->Length); - System::Int32 count = 0; - for each(AutomationProperty ^ap in aps) - { - System::Object ^currentVal = parent->GetCurrentPropertyValue(ap); - if (currentVal == nullptr) - continue; - propStrArray[count] = ap->ProgrammaticName + ":" + currentVal->ToString(); - ++count; - } - return propStrArray; -} diff --git a/native/WpfBridge/WpfAutomation.h b/native/WpfBridge/WpfAutomation.h deleted file mode 100644 index fe8adb9..0000000 --- a/native/WpfBridge/WpfAutomation.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014, Synthuse.org - * Released under the Apache Version 2.0 License. - * - * last modified by ejakubowski7@gmail.com -*/ -#pragma once - -public ref class WpfAutomation -{ -public: - WpfAutomation(void); - void setFrameworkId(System::String ^propertyValue); //default is WPF, but also accepts Silverlight, Win32 - void setTouchableOnly(System::Boolean val); //default is true - - //Descendants will walk the full tree of windows, NOT just one level of children - System::Int32 countDescendantWindows(); - System::Int32 countDescendantWindows(System::String ^runtimeIdValue); - - System::Int32 countChildrenWindows(); - System::Int32 countChildrenWindows(System::String ^runtimeIdValue); - - array ^ enumChildrenWindowIds(System::String ^runtimeIdValue); //if runtimeIdValue is null will start at desktop - array ^ enumDescendantWindowIds(System::String ^runtimeIdValue); //if runtimeIdValue is null will start at desktop - array ^ enumDescendantWindowIds(System::Int32 processId); - //In all the above Enumerate methods will return a list of Runtime Ids for all related windows. - array ^ enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties); - - System::String ^ getRuntimeIdFromHandle(System::IntPtr windowHandle); - System::String ^ getRuntimeIdFromPoint(System::Int32 x, System::Int32 y); - System::String ^ getParentRuntimeId(System::String ^runtimeIdValue); - System::String ^ getProperty(System::String ^propertyName, System::String ^runtimeIdValue); - array ^ getProperties(System::String ^runtimeIdValue); - array ^ getPropertiesAndValues(System::String ^runtimeIdValue); -private: - array ^ convertRuntimeIdString(System::String ^runtimeIdValue); - System::Windows::Automation::AutomationElement ^ findAutomationElementById(System::String ^runtimeIdValue, System::Boolean unfiltered); - System::String ^ getRuntimeIdFromElement(System::Windows::Automation::AutomationElement ^element); - array ^ getRuntimeIdsFromCollection(System::Windows::Automation::AutomationElementCollection ^collection); - System::Windows::Automation::Condition ^ getSearchConditions(); - - static System::String ^DEFAULT_FRAMEWORK = L"WPF"; - static System::Boolean ^DEFAULT_TOUCHABLE = true; - System::String ^frameworkId; - System::Boolean ^touchableOnly; -}; - diff --git a/native/WpfBridge/bin/wpfbridge32.dll b/native/WpfBridge/bin/wpfbridge32.dll deleted file mode 100644 index b6dc92c758f197b62410554042c45517bad762b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78848 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 NEWzIU`(5F{{{dz@{c!*Q diff --git a/native/WpfBridge/bin/wpfbridge64.dll b/native/WpfBridge/bin/wpfbridge64.dll deleted file mode 100644 index 29affed7647486760ba4f239526eb8f3dbb7300d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93184 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 P`gyA7Kl=OsDF^-!+SScM diff --git a/native/WpfBridge/org_synthuse_WpfBridge.cpp b/native/WpfBridge/org_synthuse_WpfBridge.cpp deleted file mode 100644 index 85e6d0e..0000000 --- a/native/WpfBridge/org_synthuse_WpfBridge.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2014, Synthuse.org - * Released under the Apache Version 2.0 License. - * - * last modified by ejakubowski7@gmail.com -*/ -#include "StdAfx.h" -#include "org_synthuse_WpfBridge.h" -#include "WpfAutomation.h" -#include "Global.h" -#include //using namespace msclr::interop; - -using namespace System; -using namespace System::Windows::Automation; -using namespace msclr::interop; -using namespace Globals; - -JNIEXPORT void JNICALL Java_org_synthuse_WpfBridge_setFrameworkId(JNIEnv *env, jobject obj, jstring jpropertyValue) -{ - const char *propertyValue = env->GetStringUTFChars(jpropertyValue, 0);//convert string - Global::WPF_AUTO->setFrameworkId(marshal_as(propertyValue)); - env->ReleaseStringUTFChars(jpropertyValue, propertyValue); //release string -} - -/* - * Class: org_synthuse_WpfBridge - * Method: setTouchableOnly - * Signature: (Z)V - */ -JNIEXPORT void JNICALL Java_org_synthuse_WpfBridge_setTouchableOnly(JNIEnv *env, jobject obj, jboolean jval) -{ - Global::WPF_AUTO->setTouchableOnly((bool)(jval == JNI_TRUE)); -} - -/* - * Class: org_synthuse_WpfBridge - * Method: CountDescendantWindows - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countDescendantWindows__(JNIEnv *env, jobject obj) -{ - return Global::WPF_AUTO->countDescendantWindows(); -} - -/* - * Class: org_synthuse_WpfBridge - * Method: CountDescendantWindows - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countDescendantWindows__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - jint result = Global::WPF_AUTO->countDescendantWindows(marshal_as(runtimeIdValue)); - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return result; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: CountChildrenWindows - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countChildrenWindows__(JNIEnv *env, jobject obj) -{ - return Global::WPF_AUTO->countChildrenWindows(); -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: CountChildrenWindows - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countChildrenWindows__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - jint result = Global::WPF_AUTO->countChildrenWindows(marshal_as(runtimeIdValue)); - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return result; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: EnumChildrenWindowIds - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumChildrenWindowIds(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - array ^mchildrenIds = Global::WPF_AUTO->enumChildrenWindowIds(marshal_as(runtimeIdValue)); - if (mchildrenIds == nullptr) - return NULL; - //create result object array to the same size as the managed children Ids string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mchildrenIds->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - //char **childrenIds = new char *[mchildrenIds->Length]; - for(int i = 0 ; i < mchildrenIds->Length ; i++) - { - //childrenIds[i] = (char *)context.marshal_as(mchildrenIds[i]); - //env->SetObjectArrayElement(results, i, env->GetStringUTFChars(childrenIds[i], 0) - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mchildrenIds[i]))); - } - //delete[] childrenIds; - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return results; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: EnumDescendantWindowIds - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIds__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - array ^mchildrenIds = Global::WPF_AUTO->enumDescendantWindowIds(marshal_as(runtimeIdValue)); - if (mchildrenIds == nullptr) - return NULL; - //create result object array to the same size as the managed children Ids string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mchildrenIds->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - //char **childrenIds = new char *[mchildrenIds->Length]; - for(int i = 0 ; i < mchildrenIds->Length ; i++) - { - //childrenIds[i] = (char *)context.marshal_as(mchildrenIds[i]); - //env->SetObjectArrayElement(results, i, env->GetStringUTFChars(childrenIds[i], 0) - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mchildrenIds[i]))); - } - //delete[] childrenIds; - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return results; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: EnumDescendantWindowIds - * Signature: (J)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIds__J(JNIEnv *env, jobject obj, jlong jprocessId) -{ - array ^mchildrenIds = Global::WPF_AUTO->enumDescendantWindowIds((System::Int32)jprocessId); - if (mchildrenIds == nullptr) - return NULL; - //create result object array to the same size as the managed children Ids string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mchildrenIds->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - for(int i = 0 ; i < mchildrenIds->Length ; i++) - { - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mchildrenIds[i]))); - } - return results; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: getRuntimeIdFromHandle - * Signature: (J)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getRuntimeIdFromHandle(JNIEnv *env, jobject obj, jlong jwindowHandle) -{ - System::String ^mrunId = Global::WPF_AUTO->getRuntimeIdFromHandle(System::IntPtr(jwindowHandle)); - if (mrunId == nullptr) - return NULL; - marshal_context context; //lets you marshal managed classes to unmanaged types - jstring result = env->NewStringUTF(context.marshal_as(mrunId)); - return result; -} - -/* - * Class: org_synthuse_WpfBridge - * Method: enumDescendantWindowInfo - * Signature: (Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowInfo(JNIEnv *env, jobject obj, jstring jruntimeIdValue, jstring jproperties) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string - array ^mwinInfo = Global::WPF_AUTO->enumDescendantWindowInfo(marshal_as(runtimeIdValue), marshal_as(properties)); - if (mwinInfo == nullptr) - return NULL; - //create result object array to the same size as the managed window info string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mwinInfo->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - for(int i = 0 ; i < mwinInfo->Length ; i++) - { - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mwinInfo[i]))); - } - env->ReleaseStringUTFChars(jproperties, properties); //release string - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return results; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: getRuntimeIdFromPoint - * Signature: (II)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getRuntimeIdFromPoint(JNIEnv *env, jobject obj, jint x, jint y) -{ - System::String ^mresult = Global::WPF_AUTO->getRuntimeIdFromPoint(x, y); - if (mresult == nullptr) - return NULL; - marshal_context context; //lets you marshal managed classes to unmanaged types - jstring result = env->NewStringUTF(context.marshal_as(mresult)); - return result; -} - -/* - * Class: org_synthuse_WpfBridge - * Method: getParentRuntimeId - * Signature: (Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getParentRuntimeId(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - System::String ^mresult = Global::WPF_AUTO->getParentRuntimeId(marshal_as(runtimeIdValue)); - if (mresult == nullptr) - return NULL; - marshal_context context; //lets you marshal managed classes to unmanaged types - jstring result = env->NewStringUTF(context.marshal_as(mresult)); - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return result; -} - -/* - * Class: org_synthuse_WpfBridge - * Method: GetProperty - * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getProperty(JNIEnv *env, jobject obj, jstring jpropertyName, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - const char *propertyName = env->GetStringUTFChars(jpropertyName, 0);//convert string - System::String ^mresult = Global::WPF_AUTO->getProperty(marshal_as(propertyName), marshal_as(runtimeIdValue)); - if (mresult == nullptr) - return NULL; - marshal_context context; //lets you marshal managed classes to unmanaged types - jstring result = env->NewStringUTF(context.marshal_as(mresult)); - env->ReleaseStringUTFChars(jpropertyName, propertyName); //release string - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return result; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: GetProperties - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getProperties(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - array ^mprops = Global::WPF_AUTO->getProperties(marshal_as(runtimeIdValue)); - if (mprops == nullptr) - return NULL; - //create result object array to the same size as the managed children Ids string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mprops->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - for(int i = 0 ; i < mprops->Length ; i++) - { - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mprops[i]))); - } - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return results; -} - - -/* - * Class: org_synthuse_WpfBridge - * Method: GetPropertiesAndValues - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getPropertiesAndValues(JNIEnv *env, jobject obj, jstring jruntimeIdValue) -{ - const char *runtimeIdValue = env->GetStringUTFChars(jruntimeIdValue, 0);//convert string - array ^mprops = Global::WPF_AUTO->getPropertiesAndValues(marshal_as(runtimeIdValue)); - if (mprops == nullptr) - return NULL; - //create result object array to the same size as the managed children Ids string array - jclass stringClass = env->FindClass("java/lang/String"); - jobjectArray results = env->NewObjectArray(mprops->Length, stringClass, 0); - marshal_context context; //lets you marshal managed classes to unmanaged types - for(int i = 0 ; i < mprops->Length ; i++) - { - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mprops[i]))); - } - env->ReleaseStringUTFChars(jruntimeIdValue, runtimeIdValue); //release string - return results; -} diff --git a/native/WpfBridge/org_synthuse_WpfBridge.h b/native/WpfBridge/org_synthuse_WpfBridge.h deleted file mode 100644 index 6a546b9..0000000 --- a/native/WpfBridge/org_synthuse_WpfBridge.h +++ /dev/null @@ -1,141 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_synthuse_WpfBridge */ - -#ifndef _Included_org_synthuse_WpfBridge -#define _Included_org_synthuse_WpfBridge -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_synthuse_WpfBridge - * Method: setFrameworkId - * Signature: (Ljava/lang/String;)V - */ -JNIEXPORT void JNICALL Java_org_synthuse_WpfBridge_setFrameworkId - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: setTouchableOnly - * Signature: (Z)V - */ -JNIEXPORT void JNICALL Java_org_synthuse_WpfBridge_setTouchableOnly - (JNIEnv *, jobject, jboolean); - -/* - * Class: org_synthuse_WpfBridge - * Method: countDescendantWindows - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countDescendantWindows__ - (JNIEnv *, jobject); - -/* - * Class: org_synthuse_WpfBridge - * Method: countDescendantWindows - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countDescendantWindows__Ljava_lang_String_2 - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: countChildrenWindows - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countChildrenWindows__ - (JNIEnv *, jobject); - -/* - * Class: org_synthuse_WpfBridge - * Method: countChildrenWindows - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_org_synthuse_WpfBridge_countChildrenWindows__Ljava_lang_String_2 - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: enumChildrenWindowIds - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumChildrenWindowIds - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: enumDescendantWindowIds - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIds__Ljava_lang_String_2 - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: enumDescendantWindowIds - * Signature: (J)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIds__J - (JNIEnv *, jobject, jlong); - -/* - * Class: org_synthuse_WpfBridge - * Method: enumDescendantWindowInfo - * Signature: (Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowInfo - (JNIEnv *, jobject, jstring, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: getRuntimeIdFromHandle - * Signature: (J)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getRuntimeIdFromHandle - (JNIEnv *, jobject, jlong); - -/* - * Class: org_synthuse_WpfBridge - * Method: getRuntimeIdFromPoint - * Signature: (II)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getRuntimeIdFromPoint - (JNIEnv *, jobject, jint, jint); - -/* - * Class: org_synthuse_WpfBridge - * Method: getParentRuntimeId - * Signature: (Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getParentRuntimeId - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: getProperty - * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getProperty - (JNIEnv *, jobject, jstring, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: getProperties - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getProperties - (JNIEnv *, jobject, jstring); - -/* - * Class: org_synthuse_WpfBridge - * Method: getPropertiesAndValues - * Signature: (Ljava/lang/String;)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getPropertiesAndValues - (JNIEnv *, jobject, jstring); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/native/uiabridge.sln b/native/uiabridge.sln new file mode 100644 index 0000000..88b543c --- /dev/null +++ b/native/uiabridge.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uiabridge", "uiabridge\uiabridge.vcxproj", "{BAC1B079-7B87-4396-B17F-91A86DF1AE29}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uiabtest", "uiabtest\uiabtest.vcxproj", "{7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Debug|Win32.ActiveCfg = Debug|Win32 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Debug|Win32.Build.0 = Debug|Win32 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Debug|x64.ActiveCfg = Debug|x64 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Debug|x64.Build.0 = Debug|x64 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Release|Win32.ActiveCfg = Release|Win32 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Release|Win32.Build.0 = Release|Win32 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Release|x64.ActiveCfg = Release|x64 + {BAC1B079-7B87-4396-B17F-91A86DF1AE29}.Release|x64.Build.0 = Release|x64 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Debug|Win32.ActiveCfg = Debug|Win32 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Debug|Win32.Build.0 = Debug|Win32 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Debug|x64.ActiveCfg = Debug|x64 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Debug|x64.Build.0 = Debug|x64 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Release|Win32.ActiveCfg = Release|Win32 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Release|Win32.Build.0 = Release|Win32 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Release|x64.ActiveCfg = Release|x64 + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/native/uiabridge.suo b/native/uiabridge.suo new file mode 100644 index 0000000000000000000000000000000000000000..0c2f744a2c91bbe8b8fc4400524c0394dc21e4c3 GIT binary patch literal 14336 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 F@Lz`cVaxyk literal 0 HcmV?d00001 diff --git a/native/WpfBridge/AssemblyInfo.cpp b/native/uiabridge/AssemblyInfo.cpp similarity index 72% rename from native/WpfBridge/AssemblyInfo.cpp rename to native/uiabridge/AssemblyInfo.cpp index 8d9e333..5ef1b63 100644 --- a/native/WpfBridge/AssemblyInfo.cpp +++ b/native/uiabridge/AssemblyInfo.cpp @@ -1,9 +1,3 @@ -/* - * Copyright 2014, Synthuse.org - * Released under the Apache Version 2.0 License. - * - * last modified by ejakubowski7@gmail.com -*/ #include "stdafx.h" using namespace System; @@ -17,12 +11,12 @@ using namespace System::Security::Permissions; // set of attributes. Change these attribute values to modify the information // associated with an assembly. // -[assembly:AssemblyTitleAttribute("WpfBridge")]; +[assembly:AssemblyTitleAttribute("uiabridge")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; -[assembly:AssemblyCompanyAttribute("Synthuse")]; -[assembly:AssemblyProductAttribute("WpfBridge")]; -[assembly:AssemblyCopyrightAttribute("Copyright (c) Synthuse 2014")]; +[assembly:AssemblyCompanyAttribute("na")]; +[assembly:AssemblyProductAttribute("uiabridge")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) na 2014")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; @@ -37,7 +31,7 @@ using namespace System::Security::Permissions; // You can specify all the value or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly:AssemblyVersionAttribute("1.1.*")]; +[assembly:AssemblyVersionAttribute("1.0.*")]; [assembly:ComVisible(false)]; diff --git a/native/WpfBridge/Global.cpp b/native/uiabridge/Global.cpp similarity index 68% rename from native/WpfBridge/Global.cpp rename to native/uiabridge/Global.cpp index 6832972..2523535 100644 --- a/native/WpfBridge/Global.cpp +++ b/native/uiabridge/Global.cpp @@ -4,7 +4,7 @@ * * last modified by ejakubowski7@gmail.com */ -#include "StdAfx.h" +#include "stdafx.h" #include "Global.h" -#include "WpfAutomation.h" - +#include "uiabridge.h" +using namespace uiabridge; diff --git a/native/WpfBridge/Global.h b/native/uiabridge/Global.h similarity index 70% rename from native/WpfBridge/Global.h rename to native/uiabridge/Global.h index d7b5a56..c486cd8 100644 --- a/native/WpfBridge/Global.h +++ b/native/uiabridge/Global.h @@ -5,7 +5,8 @@ * last modified by ejakubowski7@gmail.com */ #pragma once -#include "WpfAutomation.h" +#include "uiabridge.h" +using namespace uiabridge; namespace Globals { using namespace System; @@ -13,7 +14,7 @@ namespace Globals public ref class Global { public: - static WpfAutomation ^WPF_AUTO = gcnew WpfAutomation(); + static AutomationBridge ^AUTO_BRIDGE = nullptr; }; } diff --git a/native/WpfBridge/ReadMe.txt b/native/uiabridge/ReadMe.txt similarity index 67% rename from native/WpfBridge/ReadMe.txt rename to native/uiabridge/ReadMe.txt index 4d5f4e6..bba57e9 100644 --- a/native/WpfBridge/ReadMe.txt +++ b/native/uiabridge/ReadMe.txt @@ -1,9 +1,8 @@ ======================================================================== - DYNAMIC LINK LIBRARY : WpfBridge Project Overview + DYNAMIC LINK LIBRARY : uiabridge Project Overview ======================================================================== - Created By Edward Jakubowski ejakubowski7@gmail.com Description: This is a bridge for java to access .net 4 UI Automation libraries. This -library enables Synthuse to access and automation WPF and Silverlight apps. +library enables Synthuse to access and automate WinForms, WPF and Silverlight apps. \ No newline at end of file diff --git a/native/uiabridge/Stdafx.cpp b/native/uiabridge/Stdafx.cpp new file mode 100644 index 0000000..c445b97 --- /dev/null +++ b/native/uiabridge/Stdafx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// uiabridge.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" +#include \ No newline at end of file diff --git a/native/WpfBridge/Stdafx.h b/native/uiabridge/Stdafx.h similarity index 100% rename from native/WpfBridge/Stdafx.h rename to native/uiabridge/Stdafx.h diff --git a/native/WpfBridge/app.ico b/native/uiabridge/app.ico similarity index 100% rename from native/WpfBridge/app.ico rename to native/uiabridge/app.ico diff --git a/native/WpfBridge/app.rc b/native/uiabridge/app.rc similarity index 100% rename from native/WpfBridge/app.rc rename to native/uiabridge/app.rc diff --git a/native/WpfBridge/build.bat b/native/uiabridge/build.bat similarity index 100% rename from native/WpfBridge/build.bat rename to native/uiabridge/build.bat diff --git a/native/uiabridge/org_synthuse_UiaBridge.cpp b/native/uiabridge/org_synthuse_UiaBridge.cpp new file mode 100644 index 0000000..6bd5bc9 --- /dev/null +++ b/native/uiabridge/org_synthuse_UiaBridge.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ +#include "stdafx.h" +#include //using namespace msclr::interop; +#include "org_synthuse_UiaBridge.h" +#include "uiabridge.h" +#include "Global.h" + +using namespace System; +using namespace System::Windows::Automation; +using namespace msclr::interop; +using namespace Globals; +using namespace uiabridge; + + +/* + * Class: org_synthuse_UiaBridge + * Method: initialize + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_initialize(JNIEnv *env, jobject obj, jstring jproperties) +{ + Global::AUTO_BRIDGE = gcnew AutomationBridge(); +} + +/* + * Class: org_synthuse_UiaBridge + * Method: shutdown + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_shutdown(JNIEnv *env, jobject obj) +{ +} + +/* + * Class: org_synthuse_UiaBridge + * Method: addEnumFilter + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_synthuse_UiaBridge_addEnumFilter(JNIEnv *env, jobject obj, jstring jpropertyName, jstring jpropertyValue) +{ + const char *propertyName = env->GetStringUTFChars(jpropertyValue, 0);//convert string + const char *propertyValue = env->GetStringUTFChars(jpropertyValue, 0);//convert string + return (jint)Global::AUTO_BRIDGE->addEnumFilter(marshal_as(propertyName), marshal_as(propertyValue)); +} + +/* + * Class: org_synthuse_UiaBridge + * Method: clearEnumFilters + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_clearEnumFilters(JNIEnv *env, jobject obj) +{ + Global::AUTO_BRIDGE->clearEnumFilters(); +} + +/* + * Class: org_synthuse_UiaBridge + * Method: enumWindowInfo + * Signature: (Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_synthuse_UiaBridge_enumWindowInfo__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jproperties) +{ + const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string + array ^mwinInfo = Global::AUTO_BRIDGE->enumWindowInfo(marshal_as(properties)); + if (mwinInfo == nullptr) + return NULL; + //create result object array to the same size as the managed children Ids string array + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray results = env->NewObjectArray(mwinInfo->Length, stringClass, 0); + marshal_context context; //lets you marshal managed classes to unmanaged types + //char **childrenIds = new char *[mchildrenIds->Length]; + for(int i = 0 ; i < mwinInfo->Length ; i++) + { + //childrenIds[i] = (char *)context.marshal_as(mchildrenIds[i]); + //env->SetObjectArrayElement(results, i, env->GetStringUTFChars(childrenIds[i], 0) + env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mwinInfo[i]))); + } + //delete[] childrenIds; + env->ReleaseStringUTFChars(jproperties, properties); //release string + return results; +} + +/* + * Class: org_synthuse_UiaBridge + * Method: enumWindowInfo + * Signature: (ILjava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_synthuse_UiaBridge_enumWindowInfo__ILjava_lang_String_2(JNIEnv *env, jobject obj, jint jwindowHandle, jstring jproperties) +{ + const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string + array ^mwinInfo = Global::AUTO_BRIDGE->enumWindowInfo(System::IntPtr(jwindowHandle), marshal_as(properties)); + if (mwinInfo == nullptr) + return NULL; + //create result object array to the same size as the managed children Ids string array + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray results = env->NewObjectArray(mwinInfo->Length, stringClass, 0); + marshal_context context; //lets you marshal managed classes to unmanaged types + //char **childrenIds = new char *[mchildrenIds->Length]; + for(int i = 0 ; i < mwinInfo->Length ; i++) + { + //childrenIds[i] = (char *)context.marshal_as(mchildrenIds[i]); + //env->SetObjectArrayElement(results, i, env->GetStringUTFChars(childrenIds[i], 0) + env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mwinInfo[i]))); + } + //delete[] childrenIds; + env->ReleaseStringUTFChars(jproperties, properties); //release string + return results; +} + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (IILjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__IILjava_lang_String_2(JNIEnv *env, jobject obj, jint jx, jint jy, jstring jproperties) +{ + const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string + System::String ^mwinInfo = Global::AUTO_BRIDGE->getWindowInfo(jx, jy, marshal_as(properties)); + env->ReleaseStringUTFChars(jproperties, properties); //release string + marshal_context context; + return env->NewStringUTF(context.marshal_as(mwinInfo)); +} + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (ILjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__ILjava_lang_String_2(JNIEnv *env, jobject obj, jint jwindowHandle, jstring jproperties) +{ + const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string + System::String ^mwinInfo = Global::AUTO_BRIDGE->getWindowInfo(System::IntPtr(jwindowHandle), marshal_as(properties)); + env->ReleaseStringUTFChars(jproperties, properties); //release string + marshal_context context; + return env->NewStringUTF(context.marshal_as(mwinInfo)); +} + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__Ljava_lang_String_2Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jruntimeIdStr, jstring jproperties) +{ + const char *properties = env->GetStringUTFChars(jproperties, 0);//convert string + const char *runtimeIdStr = env->GetStringUTFChars(jruntimeIdStr, 0);//convert string + System::String ^mwinInfo = Global::AUTO_BRIDGE->getWindowInfo(marshal_as(runtimeIdStr), marshal_as(properties)); + env->ReleaseStringUTFChars(jruntimeIdStr, runtimeIdStr); //release string + env->ReleaseStringUTFChars(jproperties, properties); //release string + marshal_context context; + return env->NewStringUTF(context.marshal_as(mwinInfo)); +} diff --git a/native/uiabridge/org_synthuse_UiaBridge.h b/native/uiabridge/org_synthuse_UiaBridge.h new file mode 100644 index 0000000..a41405e --- /dev/null +++ b/native/uiabridge/org_synthuse_UiaBridge.h @@ -0,0 +1,85 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_synthuse_UiaBridge */ + +#ifndef _Included_org_synthuse_UiaBridge +#define _Included_org_synthuse_UiaBridge +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_synthuse_UiaBridge + * Method: initialize + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_initialize + (JNIEnv *, jobject, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: shutdown + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_shutdown + (JNIEnv *, jobject); + +/* + * Class: org_synthuse_UiaBridge + * Method: addEnumFilter + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_synthuse_UiaBridge_addEnumFilter + (JNIEnv *, jobject, jstring, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: clearEnumFilters + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_synthuse_UiaBridge_clearEnumFilters + (JNIEnv *, jobject); + +/* + * Class: org_synthuse_UiaBridge + * Method: enumWindowInfo + * Signature: (Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_synthuse_UiaBridge_enumWindowInfo__Ljava_lang_String_2 + (JNIEnv *, jobject, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: enumWindowInfo + * Signature: (ILjava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_synthuse_UiaBridge_enumWindowInfo__ILjava_lang_String_2 + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (IILjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__IILjava_lang_String_2 + (JNIEnv *, jobject, jint, jint, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (ILjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__ILjava_lang_String_2 + (JNIEnv *, jobject, jint, jstring); + +/* + * Class: org_synthuse_UiaBridge + * Method: getWindowInfo + * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_synthuse_UiaBridge_getWindowInfo__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *, jobject, jstring, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/WpfBridge/resource.h b/native/uiabridge/resource.h similarity index 100% rename from native/WpfBridge/resource.h rename to native/uiabridge/resource.h diff --git a/native/uiabridge/uiabridge.cpp b/native/uiabridge/uiabridge.cpp new file mode 100644 index 0000000..7b495c1 --- /dev/null +++ b/native/uiabridge/uiabridge.cpp @@ -0,0 +1,422 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ +// This is the main DLL file. + +#include "stdafx.h" +#include "uiabridge.h" + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Windows::Automation; +using namespace uiabridge; + +AutomationBridge::AutomationBridge() +{ + enumFilters = gcnew Dictionary(); + cacheRequest = nullptr; + initializeCache(); +} + +AutomationBridge::~AutomationBridge() +{ + enumFilters->Clear(); + if (cacheRequest != nullptr) + cacheRequest->Pop(); //disable UI Automation Cache + //Console::WriteLine("disposing of AutomationBridge"); +} + +void AutomationBridge::initializeCache() +{ + cacheRequest = gcnew CacheRequest(); + //cacheRequest->AutomationElementMode = AutomationElementMode::Full; + cacheRequest->TreeFilter = Automation::RawViewCondition; + cacheRequest->TreeScope = TreeScope::Element;// | TreeScope::Children; + /* + cacheRequest->Add(AutomationElement::RuntimeIdProperty); + cacheRequest->Add(AutomationElement::ProcessIdProperty); + cacheRequest->Add(AutomationElement::FrameworkIdProperty); + cacheRequest->Add(AutomationElement::LocalizedControlTypeProperty); + cacheRequest->Add(AutomationElement::ControlTypeProperty); + cacheRequest->Add(AutomationElement::ClassNameProperty); + cacheRequest->Add(AutomationElement::NameProperty); + cacheRequest->Add(AutomationElement::BoundingRectangleProperty); + */ + System::String ^cachedPropStr = L"RuntimeIdProperty,ParentRuntimeIdProperty,NativeWindowHandleProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ControlTypeProperty,ClassNameProperty,NameProperty,BoundingRectangleProperty"; + array ^rootProperties = AutomationElement::RootElement->GetSupportedProperties(); + List ^cacheList = gcnew List(); + if (cachedPropStr->Contains(L"NativeWindowHandleProperty")) //special property not in the root property list + { + cacheList->Add(AutomationElement::NativeWindowHandleProperty); + cacheRequest->Add(AutomationElement::NativeWindowHandleProperty); + } + for each(AutomationProperty ^ap in rootProperties) //loop through all supported Properties for a child + { + System::String ^currentPropertyStr = L""; //current property values + System::String ^shortPropName = L" null "; + if (ap->ProgrammaticName->Contains(L".")) //get short Property name + shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1); + if (cachedPropStr->Contains(shortPropName) || cachedPropStr->Contains(ap->ProgrammaticName)) + { + cacheList->Add(ap);// add property to cachedRootProperties + cacheRequest->Add(ap); // add property to cacheRequest + //Console::WriteLine("caching property {0}", ap->ProgrammaticName); + } + } + cachedRootProperties = cacheList->ToArray(); + cacheRequest->Push(); //enable UI Automation Cache + //cachedRootProperties = AutomationElement::RootElement->GetSupportedProperties(); +} + +int AutomationBridge::addEnumFilter(System::String ^propertyName, System::String ^propertyValue) +{ + enumFilters->Add(propertyName, propertyValue); + return enumFilters->Count; +} + +void AutomationBridge::clearEnumFilters() +{ + enumFilters->Clear(); +} + +Boolean AutomationBridge::isElementFiltered(System::Windows::Automation::AutomationElement ^element) +{ + return isElementFiltered(element, nullptr); +} + +Boolean AutomationBridge::isElementFiltered(System::Windows::Automation::AutomationElement ^element, List ^filterModifierList) +{ + Boolean result = false; + int filterMatchCount = 0; + if (enumFilters->Count == 0) + return result; + array ^aps = cachedRootProperties;//element->GetSupportedProperties(); + for each(AutomationProperty ^ap in aps) //loop through all supported Properties for a child + { + System::String ^currentPropertyStr = L""; //current property values + System::String ^shortPropName = L" null "; + if (ap->ProgrammaticName->Contains(L".")) //get short Property name + shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1); + //System::Console::WriteLine("property: {0}", shortPropName); + for each(System::String ^key in enumFilters->Keys) + { + if (filterModifierList != nullptr) + if (filterModifierList->Contains(key) && key->StartsWith(PARENT_MODIFIER+ "/") ) // modifier has been applied and filters should be ignored + { + ++filterMatchCount; + //System::Console::WriteLine("PARENT_MODIFIER {0}", key); + continue; + } + else if(filterModifierList->Contains(key) && key->StartsWith(FIRST_MODIFIER+ "/")) //first already found stop! + { + //System::Console::WriteLine("FIRST_MODIFIER {0}", key); + return true; + } + System::String ^filterProp = key; + System::String ^modifier = L""; + int pos = key->IndexOf(L"/"); + if (pos != -1)//tree modifier + { + modifier = filterProp->Substring(0, pos); + filterProp = filterProp->Substring(pos+1); + //System::Console::WriteLine("modifier: {0}, {1}, {2}", modifier, filterProp, key); + } + if (shortPropName->Equals(filterProp) || ap->ProgrammaticName->Equals(filterProp)) + {//this element has a matching filter property + //System::Console::WriteLine("matched property: {0}", filterProp); + System::String ^valStr = L""; + if (ap->ProgrammaticName->Equals(L"AutomationElementIdentifiers.RuntimeIdProperty")) + {//runtimeId are int array so need to test it differently + array ^idArray = (array ^)element->GetCurrentPropertyValue(ap); + for each(System::Int32 val in idArray) + { + valStr += System::Convert::ToString(val) + L"-"; + } + valStr = valStr->TrimEnd('-'); + //System::Console::WriteLine("runtimeId: {0}", valStr); + } + else //all other property types that are strings + { + valStr = element->GetCachedPropertyValue(ap)->ToString(); + //valStr = element->GetCurrentPropertyValue(ap)->ToString(); + } + //System::Console::WriteLine("test property vals: {0} , {1}", valStr, enumFilters[key]); + + if (valStr->Equals(enumFilters[key])) // value matches filter value + { + //System::Console::WriteLine("matched property vals: {0} , {1}", valStr, enumFilters[key]); + //result = false; + ++filterMatchCount; + if (filterModifierList != nullptr) + if (modifier->Equals(PARENT_MODIFIER)) //if modifier is parent then add to modifier list + { + //System::Console::WriteLine("modifier added1 {0}", key); + filterModifierList->Add(key); + } + else if(modifier->Equals(FIRST_MODIFIER)) { + //System::Console::WriteLine("first modifier added1 {0} {1}", key, filterModifierList->Count); + //for each (System::String ^mod in filterModifierList) + // System::Console::WriteLine("mod {0}", mod); + filterModifierList->Add(key); + return false; + } + } + else// not matched + if (filterModifierList != nullptr) + if (modifier->Equals(ALL_MODIFIER)) //doesn't matter if ALL modifier doesn't match, need to keep searching + { + //System::Console::WriteLine("modifier added2 {0}", key); + filterModifierList->Add(key); + } + else if(modifier->Equals(FIRST_MODIFIER)) + filterModifierList->Add(ALL_MODIFIER + "/" + filterProp); + } + } + + } + //System::Console::WriteLine("filterMatchCount: {0}", filterMatchCount); + if (filterMatchCount > 0) + return false; + else + return true; + //return result; +} + +void AutomationBridge::processFilterModifier(Boolean filtered, Boolean modifierChanged, List ^filterModifierList) +{ + if (!filtered) //not filtered so return element + { + //winInfoList->Add(getWindowInfo(currentElement, properties)); + //winInfoList->AddRange(enumWindowInfo(currentElement, properties, filterModifierList)); + if (modifierChanged && filterModifierList[filterModifierList->Count - 1]->StartsWith(FIRST_MODIFIER) == false) //modifier was added and needs to be removed + {// don't remove First modifier + //System::Console::WriteLine("modifier removed1 {0}", filterModifierList[filterModifierList->Count - 1]); + filterModifierList->RemoveAt(filterModifierList->Count - 1); + } + } + else //filtered, but if modifier used keep searching children + { + if (modifierChanged) //modifier was added and needs to be removed (ALL) + { + //winInfoList->AddRange(enumWindowInfo(currentElement, properties, filterModifierList)); + if (filterModifierList[filterModifierList->Count - 1]->StartsWith(FIRST_MODIFIER) == false)// don't remove First modifier + { + //System::Console::WriteLine("modifier removed2 {0}", filterModifierList[filterModifierList->Count - 1]); + filterModifierList->RemoveAt(filterModifierList->Count - 1); + } + } + } + +} + +System::String ^ AutomationBridge::getRuntimeIdFromElement(System::Windows::Automation::AutomationElement ^element) +{ + System::String ^result = L""; + System::Object ^currentVal = element->GetCurrentPropertyValue(AutomationElement::RuntimeIdProperty); + if (currentVal != nullptr) + { + array ^idArray = (array ^)currentVal; + for each(System::Int32 val in idArray) + { + result += System::Convert::ToString(val) + L"-"; + } + result = result->TrimEnd('-'); + //System::Console::WriteLine("id: {0}", result); + } + return result; +} + +array ^ AutomationBridge::enumWindowInfo(System::String ^properties) +{ + return enumWindowInfo(AutomationElement::RootElement, properties); +} + +array ^ AutomationBridge::enumWindowInfo(System::IntPtr windowHandle, System::String ^properties) +{ + AutomationElement ^element = AutomationElement::FromHandle(windowHandle); + List ^winInfoList = gcnew List(); + if (!isElementFiltered(element)) //test parent should be filtered + winInfoList->Add(getWindowInfo(element, properties)); + winInfoList->AddRange(enumWindowInfo(element, properties)); + return winInfoList->ToArray(); +} + +array ^ AutomationBridge::enumWindowInfo(AutomationElement ^element, System::String ^properties) +{ + List ^filterModifierList = gcnew List(); //can change descendants filters based on parent's filters + return enumWindowInfo(element, properties, filterModifierList); +} + +array ^ AutomationBridge::enumWindowInfo(AutomationElement ^element, System::String ^properties, List ^filterModifierList) +{ + List ^winInfoList = gcnew List(); + if (element == nullptr) + return winInfoList->ToArray(); + TreeWalker ^tw = TreeWalker::RawViewWalker; + //System::Console::WriteLine("get info: {0}", getWindowInfo(element, properties)); + //AutomationElement ^currentElement = tw->GetFirstChild(element, cacheRequest); + AutomationElement ^currentElement = nullptr; + /*if (element->CachedChildren != nullptr) + { + System::Console::WriteLine("using cached child"); + currentElement = element->CachedChildren[0]; + } + else*/ + { + //System::Console::WriteLine("not cached child"); + currentElement = tw->GetFirstChild(element, cacheRequest); + } + if (currentElement == nullptr) + { + //System::Console::WriteLine("no children {0}", element->CachedChildren->Count); + //System::Console::WriteLine("no children"); + return winInfoList->ToArray(); + } + //else + // System::Console::WriteLine("yes children"); + + while (currentElement != nullptr) + { + try + { + int fmlOriginalSize = filterModifierList->Count; + Boolean filtered = isElementFiltered(currentElement, filterModifierList); + Boolean modifierChanged = fmlOriginalSize != filterModifierList->Count; + if (!filtered) //not filtered so return element + { + winInfoList->Add(getWindowInfo(currentElement, properties)); + winInfoList->AddRange(enumWindowInfo(currentElement, properties, filterModifierList)); + } + else //filtered, but if modifier used keep searching children + { + if (modifierChanged) //modifier was added search children + winInfoList->AddRange(enumWindowInfo(currentElement, properties, filterModifierList)); + } + processFilterModifier(filtered, modifierChanged, filterModifierList); //cleans filterModifierList + //System::Console::WriteLine("element: {0}", currentElement); + //currentElement-> + currentElement = tw->GetNextSibling(currentElement, cacheRequest); + } catch (Exception ^ex) + { + System::Console::WriteLine("Exception: {0} {1}", ex->Message, ex->StackTrace); + } + } + return winInfoList->ToArray(); +} + +System::String ^ AutomationBridge::getWindowInfo(AutomationElement ^element, System::String ^properties) +{ + System::String ^resultProperties = L""; + System::String ^propertyNameErrorCheck = L""; + try + { + //when wildcard is enabled it will pull all property names & values + System::Boolean wildcardEnabled = false; + if (properties->Equals(L"*")) + wildcardEnabled = true; + + //create array for keeping order of properties + System::String ^delim = L","; + array ^propSpltArray = properties->Split(delim->ToCharArray()); + TreeWalker ^tw = TreeWalker::ControlViewWalker; + System::Int32 count = 0; + array ^aps = cachedRootProperties;//element->GetSupportedProperties(); + array ^propValues = gcnew array(propSpltArray->Length);//keep order + System::String ^wildcardProperties = L""; + if (wildcardEnabled) { + wildcardProperties += "ParentRuntimeIdProperty:" + getRuntimeIdFromElement(tw->GetParent(element, cacheRequest)) + ","; + //propValues = gcnew array(aps->Length +1 );//add one for parent property since it doesn't exist + } + for(int i=0 ; i < propValues->Length ; i++) + { + propValues[i] = L""; + if (propSpltArray[i]->Equals("ParentRuntimeIdProperty"))//custom property for getting parent + { + propValues[i] = getRuntimeIdFromElement(tw->GetParent(element, cacheRequest)); + } + } + for each(AutomationProperty ^ap in aps) //loop through all supported Properties for a child + { + propertyNameErrorCheck = ap->ProgrammaticName;//debug purposes + System::String ^currentPropertyStr = L""; //current property values + //System::Console::WriteLine("property: {0}", ap->ProgrammaticName); + System::String ^shortPropName = L" null "; + if (ap->ProgrammaticName->Contains(L".")) + shortPropName = ap->ProgrammaticName->Substring(ap->ProgrammaticName->IndexOf(L".") + 1); + if (properties->Contains(shortPropName) || properties->Contains(ap->ProgrammaticName) || ap->ProgrammaticName->Equals(properties) || wildcardEnabled) + { + //System::Console::WriteLine("shortPropName: {0}", shortPropName); + //System::Object ^currentVal = element->GetCurrentPropertyValue(ap); + System::Object ^currentVal = element->GetCachedPropertyValue(ap); + if (currentVal == nullptr) + continue; + if (ap->ProgrammaticName->Equals(L"AutomationElementIdentifiers.RuntimeIdProperty")) + { + array ^idArray = (array ^)currentVal; + for each(System::Int32 val in idArray) + { + currentPropertyStr += System::Convert::ToString(val) + L"-"; + } + currentPropertyStr = currentPropertyStr->TrimEnd('-'); + //System::Console::WriteLine("id: {0}", result); + } + else//not runtimeId which is an Int32[] + { + currentPropertyStr = currentVal->ToString(); + currentPropertyStr = currentPropertyStr->Replace(",",","); + } + } + if (currentPropertyStr->Equals(L"")) //if there isn't a value skip + continue; + if (wildcardEnabled) { + wildcardProperties += shortPropName + ":" +currentPropertyStr + ","; + continue; + } + //System::Console::WriteLine("currentPropertyStr: {0}", currentPropertyStr); + //find the correct order to return this property + for(int i=0 ; i < propSpltArray->Length ; i++) + { + if (propSpltArray[i]->Equals(shortPropName) || propSpltArray[i]->Equals(ap->ProgrammaticName)) + propValues[i] = currentPropertyStr; + } + } + //output properties in the correct order + for(int i=0 ; i < propSpltArray->Length ; i++) + resultProperties += propValues[i] + L","; + if (wildcardEnabled) + resultProperties += wildcardProperties; + } catch (Exception ^ex) //when some elements close during enumeration it might cause valid exceptions + { + System::Console::WriteLine("Exception ({2}): {0} {1}", ex->Message, ex->StackTrace, propertyNameErrorCheck); + } + return resultProperties; + +} + +System::String ^ AutomationBridge::getWindowInfo(System::Int32 x, System::Int32 y, System::String ^properties) +{ + AutomationElement ^element = AutomationElement::FromPoint(System::Windows::Point(x, y)); + return getWindowInfo(element, properties); +} + +System::String ^ AutomationBridge::getWindowInfo(System::IntPtr windowHandle, System::String ^properties) +{ + AutomationElement ^element = AutomationElement::FromHandle(windowHandle); + return getWindowInfo(element, properties); +} + +System::String ^ AutomationBridge::getWindowInfo(System::String ^runtimeIdStr, System::String ^properties) +{ + System::String ^filter = L"First/RuntimeIdProperty"; //get first matching runtimeIdProperty + enumFilters->Add(filter, runtimeIdStr); + array ^props = enumWindowInfo(properties); + enumFilters->Remove(filter); + if (props->Length > 0) //if result array has a match return first result + return props[0]; + else + return ""; + //return getWindowInfo(element, properties); +} diff --git a/native/uiabridge/uiabridge.h b/native/uiabridge/uiabridge.h new file mode 100644 index 0000000..16cd3aa --- /dev/null +++ b/native/uiabridge/uiabridge.h @@ -0,0 +1,46 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ +// uiabridge.h + +#pragma once + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Windows::Automation; + +namespace uiabridge { + + public ref class AutomationBridge + { + public: + AutomationBridge(void); + ~AutomationBridge(); + int addEnumFilter(System::String ^propertyName, System::String ^propertyValue); + void clearEnumFilters(); + Boolean isElementFiltered(System::Windows::Automation::AutomationElement ^element); + Boolean isElementFiltered(System::Windows::Automation::AutomationElement ^element, List ^filterModifierList); + System::String ^ getRuntimeIdFromElement(System::Windows::Automation::AutomationElement ^element); + array ^ enumWindowInfo(System::String ^properties); + array ^ enumWindowInfo(System::IntPtr windowHandle, System::String ^properties); + array ^ enumWindowInfo(AutomationElement ^element, System::String ^properties); + array ^ enumWindowInfo(AutomationElement ^element, System::String ^properties, List ^filterModifierList); + System::String ^ getWindowInfo(AutomationElement ^element, System::String ^properties); + System::String ^ getWindowInfo(System::Int32 x, System::Int32 y, System::String ^properties); + System::String ^ getWindowInfo(System::IntPtr windowHandle, System::String ^properties); + System::String ^ getWindowInfo(System::String ^runtimeIdStr, System::String ^properties); + + static System::String ^ALL_MODIFIER = L"All";// find all matching elements of this filter + static System::String ^PARENT_MODIFIER = L"Parent";//find all children of this matching parent filter + static System::String ^FIRST_MODIFIER = L"First"; //find first element matching this filter then stop + private: + void initializeCache(); + Dictionary ^enumFilters; + void AutomationBridge::processFilterModifier(Boolean filtered, Boolean modifierChanged, List ^filterModifierList); + CacheRequest ^cacheRequest; + array ^cachedRootProperties; + }; +} diff --git a/native/WpfBridge/WpfBridge.vcxproj b/native/uiabridge/uiabridge.vcxproj similarity index 93% rename from native/WpfBridge/WpfBridge.vcxproj rename to native/uiabridge/uiabridge.vcxproj index 3e86389..c39c426 100644 --- a/native/WpfBridge/WpfBridge.vcxproj +++ b/native/uiabridge/uiabridge.vcxproj @@ -19,10 +19,10 @@ - {3141812E-36D5-4E7C-A388-EFED9AE250A5} + {BAC1B079-7B87-4396-B17F-91A86DF1AE29} v4.0 ManagedCProj - WpfBridge + uiabridge @@ -120,8 +120,7 @@ mkdir "$(ProjectDir)bin" -copy /Y "$(TargetPath)" "$(ProjectDir)bin\wpfbridge$(PlatformArchitecture)$(TargetExt)" - +copy /Y "$(TargetPath)" "$(ProjectDir)bin\uiabridge$(PlatformArchitecture)$(TargetExt)" @@ -137,8 +136,7 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\wpfbridge$(PlatformArchitecture)$(Targ mkdir "$(ProjectDir)bin" -copy /Y "$(TargetPath)" "$(ProjectDir)bin\wpfbridge$(PlatformArchitecture)$(TargetExt)" - +copy /Y "$(TargetPath)" "$(ProjectDir)bin\uiabridge$(PlatformArchitecture)$(TargetExt)" @@ -146,28 +144,29 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\wpfbridge$(PlatformArchitecture)$(Targ + - + - + - + Create Create Create Create - + diff --git a/native/WpfBridge/WpfBridge.vcxproj.filters b/native/uiabridge/uiabridge.vcxproj.filters similarity index 90% rename from native/WpfBridge/WpfBridge.vcxproj.filters rename to native/uiabridge/uiabridge.vcxproj.filters index 9689af0..f0a71a3 100644 --- a/native/WpfBridge/WpfBridge.vcxproj.filters +++ b/native/uiabridge/uiabridge.vcxproj.filters @@ -15,16 +15,16 @@ + + Header Files + Header Files Header Files - - Header Files - - + Header Files @@ -32,16 +32,16 @@ + + Source Files + Source Files Source Files - - Source Files - - + Source Files diff --git a/native/WpfBridge/WpfBridge.vcxproj.user b/native/uiabridge/uiabridge.vcxproj.user similarity index 100% rename from native/WpfBridge/WpfBridge.vcxproj.user rename to native/uiabridge/uiabridge.vcxproj.user diff --git a/native/uiabtest/AssemblyInfo.cpp b/native/uiabtest/AssemblyInfo.cpp new file mode 100644 index 0000000..8c84297 --- /dev/null +++ b/native/uiabtest/AssemblyInfo.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("uiabtest")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("na")]; +[assembly:AssemblyProductAttribute("uiabtest")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) na 2014")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/native/uiabtest/ReadMe.txt b/native/uiabtest/ReadMe.txt new file mode 100644 index 0000000..0002f31 --- /dev/null +++ b/native/uiabtest/ReadMe.txt @@ -0,0 +1,35 @@ +======================================================================== + APPLICATION : uiabtest Project Overview +======================================================================== + +AppWizard has created this uiabtest Application for you. + +This file contains a summary of what you will find in each of the files that +make up your uiabtest application. + +uiabtest.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +uiabtest.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +uiabtest.cpp + This is the main application source file. + +AssemblyInfo.cpp + Contains custom attributes for modifying assembly metadata. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/native/uiabtest/app.ico b/native/uiabtest/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a5525fd794f7a7c5c8e6187f470ea3af38cd2b6 GIT binary patch literal 1078 zcmeHHJr05}7=1t!Hp3A*8IHkVf+j?-!eHY14Gtcw1Eb*_9>Bq^zETJ@GKj{_2j4$w zo9}xCh!8{T3=X##Skq>ikMjsvB|y%crWBM2iW(4pI}c%z6%lW!=~4v77#3{z!dmB1 z__&l)-{KUYR+|8|;wB^R|9ET$J@(@=#rd^=)qs85?vAy(PSF5CyNkus435LVkZ$rj zNw|JG-P7^hF<(;#o*Vk}5R#e|^13tBbQkeF?djULtvqyxd3<{9 literal 0 HcmV?d00001 diff --git a/native/uiabtest/app.rc b/native/uiabtest/app.rc new file mode 100644 index 0000000000000000000000000000000000000000..eab43064f13cd0a8912887c2c7299ddb6d7d2240 GIT binary patch literal 2558 zcmds(T~8B16o%hx6aT}cz0qh|kt;8OHl(p2u|ObcVzjun$-0o1ioYIx-q}uzY&RON zHfA$lXU?2CAMZJ{zwW#0D(FZvO*GfF7PL&a(9WSPwA5$itPb>wFX)@Ts-VrG-!fi7 zn=|^{lG%iLU}b4D-N4h)b3NmhZXDZC3wCq3uOe@&a=xRfT9Ml)+mSDe=PY|qCmQKg zXXO<`@Kh{u}P)77e+J`b^ZYfCz>(RWgv~!7P?33(2w$i>2#H#>Fc|_Qh z^$Vg|uu6y;F)Q)4&`b6oBW$sbY4hW1i<6cC#7@YRVoj;;1hOW_dS{Ze%gCRTQO?P0 zWC0DOPI5U zsW~oLw6^XIsan`<>ynio16ff0_sBq)>sz3jP1&!{CS&XIJM!nhN%CW}eNvC;0#Epr I_*Z2A2KzE2Bme*a literal 0 HcmV?d00001 diff --git a/native/uiabtest/resource.h b/native/uiabtest/resource.h new file mode 100644 index 0000000..d5ac7c4 --- /dev/null +++ b/native/uiabtest/resource.h @@ -0,0 +1,3 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by app.rc diff --git a/native/WpfBridge/Stdafx.cpp b/native/uiabtest/stdafx.cpp similarity index 75% rename from native/WpfBridge/Stdafx.cpp rename to native/uiabtest/stdafx.cpp index a9a851a..b0e7c23 100644 --- a/native/WpfBridge/Stdafx.cpp +++ b/native/uiabtest/stdafx.cpp @@ -1,5 +1,7 @@ // stdafx.cpp : source file that includes just the standard includes -// WpfBridge.pch will be the pre-compiled header +// uiabtest.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" + + diff --git a/native/uiabtest/stdafx.h b/native/uiabtest/stdafx.h new file mode 100644 index 0000000..347bb4c --- /dev/null +++ b/native/uiabtest/stdafx.h @@ -0,0 +1,11 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +// TODO: reference additional headers your program requires here +#include +#include +#include \ No newline at end of file diff --git a/native/uiabtest/uiabtest.cpp b/native/uiabtest/uiabtest.cpp new file mode 100644 index 0000000..c14b813 --- /dev/null +++ b/native/uiabtest/uiabtest.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ +// uiabtest.cpp : main project file. + +#include "stdafx.h" + +using namespace System; +using namespace System::Windows::Automation; +using namespace uiabridge; + +void outputResults(array ^winInfo) +{ + System::IO::StreamWriter ^file = gcnew System::IO::StreamWriter("c:\\temp.txt"); + for each(System::String ^prop in winInfo) + { + Console::WriteLine(prop); + file->WriteLine(prop); + } + file->Flush(); + file->Close(); + +} + +int main(array ^args) +{ + Console::WriteLine(L"UI Automation Bridge Test"); + AutomationBridge ^ab = gcnew AutomationBridge(); + System::String ^propList = L"RuntimeIdProperty,ParentRuntimeIdProperty,NativeWindowHandleProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ClassNameProperty,NameProperty"; + //System::String ^propList = L"RuntimeIdProperty,BoundingRectangleProperty"; + Console::WriteLine(propList); + //System::String ^winInfo1 = ab->getWindowInfo(System::IntPtr(3409618), propList); + + System::DateTime start = System::DateTime::Now; + //ab->addEnumFilter("Parent/ClassNameProperty", "Notepad"); + //ab->addEnumFilter("First/RuntimeIdProperty", "42-4784952"); + //ab->addEnumFilter("ClassNameProperty", "Notepad"); + //ab->addEnumFilter("All/ClassNameProperty", "Edit"); + //ab->addEnumFilter("All/LocalizedControlTypeProperty", "menu item"); + ab->addEnumFilter("Parent/FrameworkIdProperty", "WinForm"); + //ab->addEnumFilter("Parent/FrameworkIdProperty", "WPF"); + //ab->addEnumFilter("Parent/ClassNameProperty", "WindowsForms10.Window.8.app.0.2bf8098_r13_ad1"); + array ^winInfo = ab->enumWindowInfo(propList); //L"*" + //ab->clearEnumFilters(); + //ab->addEnumFilter("Parent/FrameworkIdProperty", "WinForm"); + //array ^winInfo = ab->enumWindowInfo(System::IntPtr(3409618), propList); //L"*" + //array ^winInfo = ab->enumWindowInfo(System::IntPtr(12977932), propList); //L"*" + //Console::WriteLine("enumWindowInfo x,y: {0}", ab->getWindowInfo(100,100, propList); //L"*" + + outputResults(winInfo); + //Globals::Global::AUTO_BRIDGE->clearEnumFilters(); + //winInfo = nullptr; + //winInfo = Globals::Global::AUTO_BRIDGE->enumWindowInfo(System::IntPtr(7603636), propList); + //Console::WriteLine("winInfo length: {0}", winInfo->Length); + //winInfo = Globals::Global::AUTO_BRIDGE->enumWindowInfo(System::IntPtr(7603636), propList); + //Console::WriteLine("winInfo length: {0}", winInfo->Length); + + //Console::WriteLine("getWindowInfo RuntimeIdProperty: {0}", ab->getWindowInfo("42-4784952", propList)); + + //System::Threading::Thread::Sleep(10000); //10 seconds sleep + //array ^winInfo2 = ab->enumWindowInfo(propList); //L"*" + //outputResults(winInfo2); + + System::Double seconds = System::Math::Round(System::DateTime::Now.Subtract(start).TotalSeconds, 4); + Console::WriteLine(L"Total Elements: {0} in {1} seconds", winInfo->Length, seconds); + getch();//wait for user input + return 0; +} diff --git a/native/uiabtest/uiabtest.vcxproj b/native/uiabtest/uiabtest.vcxproj new file mode 100644 index 0000000..1f42b2a --- /dev/null +++ b/native/uiabtest/uiabtest.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {7958D1B7-F169-40FA-A7A0-10E8E0F90CDA} + v4.0 + ManagedCProj + uiabtest + + + + Application + true + true + Unicode + + + Application + true + true + Unicode + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + Use + + + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + Use + + + true + + + + + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + Use + + + true + + + + + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + Use + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + {bac1b079-7b87-4396-b17f-91a86df1ae29} + + + + + + \ No newline at end of file diff --git a/native/uiabtest/uiabtest.vcxproj.filters b/native/uiabtest/uiabtest.vcxproj.filters new file mode 100644 index 0000000..7f6715d --- /dev/null +++ b/native/uiabtest/uiabtest.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + Resource Files + + + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/native/uiabtest/uiabtest.vcxproj.user b/native/uiabtest/uiabtest.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/native/uiabtest/uiabtest.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/org/synthuse/RobotMacro.java b/src/org/synthuse/RobotMacro.java index e35f5fc..999365b 100644 --- a/src/org/synthuse/RobotMacro.java +++ b/src/org/synthuse/RobotMacro.java @@ -195,14 +195,142 @@ public class RobotMacro { lastException = e; } } +/* SendKeys Special Keys List +{BACKSPACE}, {BS}, or {BKSP} +{BREAK} +{CAPSLOCK} +{DELETE} or {DEL} +{DOWN} +{END} +{ENTER} or ~ +{ESC} +{HELP} +{HOME} +{INSERT} or {INS} +{LEFT} +{NUMLOCK} +{PGDN} +{PGUP} +{PRTSC} (reserved for future use) +{RIGHT} +{SCROLLLOCK} +{TAB} +{UP} +{F1} +{F2} +{F3} +{F4} +{F5} +{F6} +{F7} +{F8} +{F9} +{F10} +{F11} +{F12} +{F13} +{F14} +{F15} +{F16} +{ADD} +{SUBTRACT} +{MULTIPLY} +{DIVIDE} +{{} +{}} +SHIFT + +CTRL ^ +ALT % + + */ public static boolean sendKeys(String keyCommands) { try { Robot robot = new Robot(); + boolean specialKeyFlag = false; + String specialKey = ""; + boolean modifierKeyFlag = false; + String modifierKeys = ""; for (int i = 0; i < keyCommands.length(); i++) { char key = keyCommands.charAt(i); - int[] keyCode = getKeyCode(key); - pressKeyCodes(robot, keyCode); + if (specialKeyFlag) + specialKey += key; + if (key == '{' && specialKeyFlag == false) { + specialKeyFlag = true; + specialKey = "{"; + } + + if (!specialKeyFlag) { //not special key(tab,enter,...) just press normal keys and modifiers + // Modifier key logic + if (key == '+' || key == '^' || key == '%') { //shift alt or ctrl + if (!modifierKeyFlag) { + modifierKeys = key + ""; + modifierKeyFlag = true; + } + else + modifierKeys += key + ""; //append multiple modifiers + if (key == '+') + robot.keyPress(KeyEvent.VK_SHIFT); + if (key == '^') + robot.keyPress(KeyEvent.VK_CONTROL); + if (key == '%') + robot.keyPress(KeyEvent.VK_ALT); + continue; //skip to next key + } + pressKeyCodes(robot, getKeyCode(key)); + } + if (specialKeyFlag) { + if (specialKey.equals("{ENTER}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_ENTER} ); + } + else if (specialKey.equals("{ESC}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_ESCAPE} ); + } + else if (specialKey.equals("{HOME}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_HOME} ); + } + else if (specialKey.equals("{END}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_END} ); + } + else if (specialKey.equals("{PGDN}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_PAGE_DOWN} ); + } + else if (specialKey.equals("{PGUP}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_PAGE_UP} ); + } + else if (specialKey.equals("{TAB}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_TAB} ); + } + else if (specialKey.equals("{UP}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_UP} ); + } + else if (specialKey.equals("{DOWN}")) { + specialKeyFlag = false; + pressKeyCodes(robot, new int[]{KeyEvent.VK_DOWN} ); + } + } + + if (modifierKeyFlag) { //time to release all the modifier keys + modifierKeyFlag = false; + for (int m = 0; m < modifierKeys.length(); m++) { + char mkey = modifierKeys.charAt(m); + if (mkey == '+') + robot.keyRelease(KeyEvent.VK_SHIFT); + if (mkey == '^') + robot.keyRelease(KeyEvent.VK_CONTROL); + if (mkey == '%') + robot.keyRelease(KeyEvent.VK_ALT); + } + modifierKeys = ""; + } } } catch (Exception e) { lastException = e; diff --git a/src/org/synthuse/SynthuseDlg.java b/src/org/synthuse/SynthuseDlg.java index 180f4e7..086dfa3 100644 --- a/src/org/synthuse/SynthuseDlg.java +++ b/src/org/synthuse/SynthuseDlg.java @@ -71,7 +71,7 @@ public class SynthuseDlg extends JFrame { /** * */ - public static String VERSION_STR = "1.1.0"; + public static String VERSION_STR = "1.1.2"; public static String RES_STR_MAIN_ICON = "/org/synthuse/img/gnome-robots.png"; public static String RES_STR_REFRESH_IMG = "/org/synthuse/img/rapidsvn.png"; @@ -102,7 +102,7 @@ public class SynthuseDlg extends JFrame { private TestIdeFrame testIde = null; private int targetX; private int targetY; - private WpfBridge wpf = new WpfBridge(); + private UiaBridge uiabridge = new UiaBridge(); /** * Launch the application. @@ -449,7 +449,8 @@ public class SynthuseDlg extends JFrame { String handleStr = Api.GetHandleAsString(hwnd); String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd)); String parentStr = Api.GetHandleAsString(User32.instance.GetParent(hwnd)); - String runtimeId = wpf.getRuntimeIdFromPoint(targetX, targetY); + String enumProperties = uiabridge.getWindowInfo(targetX, targetY, WindowInfo.UIA_PROPERTY_LIST); + String runtimeId = WindowInfo.getRuntimeIdFromProperties(enumProperties); lblStatus.setText("rid:" + runtimeId + " class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY); if (!lastDragHwnd.equals(handleStr) || !lastRuntimeId.equals(runtimeId)) { if (!lastDragHwnd.isEmpty()) { @@ -459,7 +460,7 @@ public class SynthuseDlg extends JFrame { lastRuntimeId = runtimeId; //lastDragHwnd = (hwnd + ""); Api.highlightWindow(hwnd); - XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents); + XpathManager.buildXpathStatementThreaded(hwnd, enumProperties, textPane, xpathEvents); } } diff --git a/src/org/synthuse/WpfBridge.java b/src/org/synthuse/UiaBridge.java similarity index 72% rename from src/org/synthuse/WpfBridge.java rename to src/org/synthuse/UiaBridge.java index 3e26bfd..fabaac9 100644 --- a/src/org/synthuse/WpfBridge.java +++ b/src/org/synthuse/UiaBridge.java @@ -1,23 +1,14 @@ -/* - * Copyright 2014, Synthuse.org - * Released under the Apache Version 2.0 License. - * - * last modified by ejakubowski7@gmail.com -*/ - package org.synthuse; import java.awt.Point; import java.io.*; -public class WpfBridge { - +public class UiaBridge { static { String archDataModel = System.getProperty("sun.arch.data.model");//32 or 64 bit - //System.loadLibrary("native/WpfBridge" + archDataModel); // WpfBridge32.dll (Windows) or WpfBridge32.so (Unixes) - loadNativeLibraryFromJar("/wpfbridge" + archDataModel + ".dll"); + loadNativeLibraryFromJar("/uiabridge" + archDataModel + ".dll"); } public static void loadNativeLibraryFromJar(String path) { @@ -49,7 +40,7 @@ public class WpfBridge { byte[] buffer = new byte[1024]; int readBytes; // Open and check input stream - InputStream is = WpfBridge.class.getResourceAsStream(path); + InputStream is = UiaBridge.class.getResourceAsStream(path); if (is == null) { //check if valid System.out.println("File " + path + " was not found inside JAR."); return; @@ -69,7 +60,27 @@ public class WpfBridge { // Finally, load the library System.load(temp.getAbsolutePath()); } + + public UiaBridge () + { + initialize(""); + } + + public native void initialize(String properties); + public native void shutdown(); + public native int addEnumFilter(String propertyName, String propertyValue); + public native void clearEnumFilters(); + public native String[] enumWindowInfo(String properties); + public native String[] enumWindowInfo(int windowHandle, String properties); + //native String[] enumWindowInfo(AutomationElement ^element, String properties); + //native String[] enumWindowInfo(AutomationElement ^element, String properties, String[] filterModifierList); + //native String getWindowInfo(AutomationElement ^element, String properties); + public native String getWindowInfo(int x, int y, String properties); + public native String getWindowInfo(int windowHandle, String properties); + public native String getWindowInfo(String runtimeId, String properties); + + /* public native void setFrameworkId(String propertyValue); //default is WPF, but also accepts Silverlight, Win32 public native void setTouchableOnly(boolean val); //default is true @@ -80,7 +91,7 @@ public class WpfBridge { public native int countChildrenWindows(); public native int countChildrenWindows(String runtimeIdValue); - public native String[] enumChildrenWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop + public String[] enumChildrenWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop public native String[] enumDescendantWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop public native String[] enumDescendantWindowIds(long processId); //In all the above Enumerate methods will return a list of Runtime Ids for all related windows. @@ -92,11 +103,12 @@ public class WpfBridge { public native String getProperty(String propertyName, String runtimeIdValue); public native String[] getProperties(String runtimeIdValue); public native String[] getPropertiesAndValues(String runtimeIdValue); - + */ public Point getCenterOfElement(String runtimeIdValue) { Point p = new Point(); - String boundary = getProperty("BoundingRectangleProperty", runtimeIdValue); - //System.out.println("boundary: " + boundary); //boundary: 841,264,125,29 + String boundary = getWindowInfo(runtimeIdValue, "BoundingRectangleProperty"); + boundary = WindowInfo.replaceEscapedCodes(boundary); + //System.out.println("runtimeId: " + runtimeIdValue + ", boundary: " + boundary); //boundary: 841,264,125,29 String[] boundarySplt = boundary.split(","); int x = Integer.parseInt(boundarySplt[0]); int y = Integer.parseInt(boundarySplt[1]); @@ -106,25 +118,4 @@ public class WpfBridge { p.y = ((height) /2) + y; return p; } - - public String getWindowClass(String runtimeIdValue) { - return getProperty("ClassNameProperty", runtimeIdValue); - } - - public String getWindowText(String runtimeIdValue) { - return getProperty("NameProperty", runtimeIdValue); - } - - public String getWindowValue(String runtimeIdValue) { - return getProperty("ValueProperty", runtimeIdValue); - } - - public String getWindowAutomationId(String runtimeIdValue) { - return getProperty("AutomationIdProperty", runtimeIdValue); - } - - public String getWindowFramework(String runtimeIdValue) { - return getProperty("FrameworkIdProperty", runtimeIdValue); - } - } diff --git a/src/org/synthuse/WindowInfo.java b/src/org/synthuse/WindowInfo.java index 3c47989..aae79af 100644 --- a/src/org/synthuse/WindowInfo.java +++ b/src/org/synthuse/WindowInfo.java @@ -24,7 +24,9 @@ import com.sun.jna.ptr.PointerByReference; public class WindowInfo { - public static String WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty,ValueProperty"; + public static String UIA_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ClassNameProperty,NameProperty,ValueProperty"; + public static String UIA_RUNTIME_ID = "RuntimeIdProperty"; + public static int MAX_TEXT_SIZE = 200; public HWND hwnd; public String hwndStr = ""; @@ -33,6 +35,7 @@ public class WindowInfo { public RECT rect; public String text; public String value; + public String controlType = ""; public String className = ""; public boolean isChild = false; public String processName = ""; @@ -53,9 +56,11 @@ public class WindowInfo { text = Native.toString(buffer); if (text.isEmpty()) text = new Api().sendWmGetText(hWnd); - if (text.isEmpty()) { + //if (text.isEmpty()) { //System.out.println("getting toolbar text"); - } + //} + if (text.length() > MAX_TEXT_SIZE) //if text is too large it will slow down xml display + text = text.substring(0, MAX_TEXT_SIZE); //Get item count depending on what type of control it is LRESULT tbCount = Api.User32.instance.SendMessage(hWnd, Api.TB_BUTTONCOUNT, new WPARAM(0), new LPARAM(0)); @@ -146,9 +151,11 @@ public class WindowInfo { } this.hwnd = hWnd; hwndStr = Api.GetHandleAsString(hWnd); + if (this.hwndStr == null) + this.hwndStr = ""; } - public String replaceEscapedCodes(String input) { + public static String replaceEscapedCodes(String input) { //, is a comma , String result = input; result = result.replaceAll(",", ","); @@ -160,9 +167,9 @@ public class WindowInfo { return result; } - //support for WPF and Silverlight + //support for WPF, Silverlight, WinForms public WindowInfo(String enumProperties, boolean isChild) { - //WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,ClassNameProperty,NameProperty"; + //WPF_PROPERTY_LIST = "RuntimeIdProperty,ParentRuntimeIdProperty,ProcessIdProperty,FrameworkIdProperty,LocalizedControlTypeProperty,ClassNameProperty,NameProperty,ValueProperty"; String[] spltProperties = enumProperties.split(","); this.isChild = isChild; if (SynthuseDlg.config.isFilterWpfDisabled()) { //use wildcard mode @@ -180,6 +187,8 @@ public class WindowInfo { this.pid = Long.parseLong(propertyNameAndValue[1]); else if (propertyNameAndValue[0].equals("FrameworkIdProperty")) this.framework = propertyNameAndValue[1]; + else if (propertyNameAndValue[0].equals("LocalizedControlTypeProperty")) + this.controlType = propertyNameAndValue[1]; else if (propertyNameAndValue[0].equals("ClassNameProperty")) this.className = propertyNameAndValue[1]; else if (propertyNameAndValue[0].equals("NameProperty")) @@ -191,6 +200,13 @@ public class WindowInfo { } } this.hwndStr = this.runtimeId; + if (text != null) + if (text.length() > MAX_TEXT_SIZE) + text = text.substring(0, MAX_TEXT_SIZE); + if (this.hwndStr == null) + this.hwndStr = ""; + //if (this.framework == null) + // this.framework = "na"; return; } // non-wildcard mode @@ -204,11 +220,21 @@ public class WindowInfo { if (spltProperties.length > 3) this.framework = spltProperties[3]; if (spltProperties.length > 4) - this.className = replaceEscapedCodes(spltProperties[4]); + this.controlType = replaceEscapedCodes(spltProperties[4]); if (spltProperties.length > 5) - this.text = replaceEscapedCodes(spltProperties[5]); + this.className = replaceEscapedCodes(spltProperties[5]); if (spltProperties.length > 6) - this.value = replaceEscapedCodes(spltProperties[6]); + this.text = replaceEscapedCodes(spltProperties[6]); + if (spltProperties.length > 7) + this.value = replaceEscapedCodes(spltProperties[7]); + if (this.className == "") + this.className = this.controlType; + if (text != null) + if (text.length() > MAX_TEXT_SIZE) + text = text.substring(0, MAX_TEXT_SIZE); + if (this.hwndStr == null) + this.hwndStr = ""; + /* this.rect = new RECT(); try { @@ -224,8 +250,16 @@ public class WindowInfo { */ } + public static String getRuntimeIdFromProperties(String enumProperties) + { + String[] spltProperties = enumProperties.split(","); + if (spltProperties.length > 0) + return spltProperties[0]; + return ""; + } + public String toString() { - return String.format("(%d,%d)-(%d,%d) : \"%s\" [%s] {%s}", rect.left,rect.top,rect.right,rect.bottom,text,className,hwnd.toString()); + return String.format("%s \"%s\" [%s] (%s) {%s}", framework, text, className, controlType, hwndStr); } } diff --git a/src/org/synthuse/WindowsEnumeratedXml.java b/src/org/synthuse/WindowsEnumeratedXml.java index 8d9d85e..a3ee89e 100644 --- a/src/org/synthuse/WindowsEnumeratedXml.java +++ b/src/org/synthuse/WindowsEnumeratedXml.java @@ -45,6 +45,7 @@ import com.sun.jna.platform.win32.WinUser; import com.sun.jna.platform.win32.WinDef.HWND; public class WindowsEnumeratedXml implements Runnable{ + public static Exception lastException = null; public static AtomicBoolean enumeratingXmlFlag = new AtomicBoolean(false); public JTextPane outputPane = null; @@ -81,10 +82,16 @@ public class WindowsEnumeratedXml implements Runnable{ final Map processList = new LinkedHashMap(); final List wpfParentList = new ArrayList();//HwndWrapper final List silverlightParentList = new ArrayList();//MicrosoftSilverlight + final List winFormParentList = new ArrayList();//class="WindowsForms*" int wpfCount = 0; + int winFormCount = 0; int silverlightCount = 0; int menuCount = 0; + //wpf.setTouchableOnly(false); + //wpf.countChildrenWindows();//fix for missing cached elements + + class ChildWindowCallback implements WinUser.WNDENUMPROC { @Override public boolean callback(HWND hWnd, Pointer lParam) { @@ -105,22 +112,31 @@ public class WindowsEnumeratedXml implements Runnable{ infoList.put(wi.hwndStr, wi); if (wi.className.startsWith("HwndWrapper")) wpfParentList.add(wi.hwndStr); + if (wi.className.startsWith("WindowsForms")) + winFormParentList.add(wi.hwndStr); Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0)); return true; } } Api.User32.instance.EnumWindows(new ParentWindowCallback(), 0); - //Enumerate WPF windows and add to list + //Enumerate WPF, WinForm, Silverlight windows and add to list if (!SynthuseDlg.config.isWpfBridgeDisabled()) { + UiaBridge uiabridge = new UiaBridge(); for (String handle : wpfParentList) { - Map wpfInfoList = EnumerateWindowsWithWpfBridge(handle, "WPF"); + Map wpfInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "*"); wpfCount += wpfInfoList.size(); infoList.putAll(wpfInfoList); } + for (String handle : winFormParentList) { + //System.out.println("winform parent " + handle); + Map winFormInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "*"); + winFormCount += winFormInfoList.size(); + infoList.putAll(winFormInfoList); + } for (String handle : silverlightParentList) { - Map slInfoList = EnumerateWindowsWithWpfBridge(handle, "Silverlight"); + Map slInfoList = EnumerateWindowsWithUiaBridge(uiabridge, handle, "Silverlight"); silverlightCount += slInfoList.size(); infoList.putAll(slInfoList); } @@ -147,8 +163,13 @@ public class WindowsEnumeratedXml implements Runnable{ win = doc.createElement("win"); else if (w.framework.equals("WPF")) win = doc.createElement("wpf"); + else if (w.framework.equals("WinForm")) + win = doc.createElement("winfrm"); else if (w.framework.equals("Silverlight")) win = doc.createElement("silver"); + else + win = doc.createElement("win"); + //System.out.println(w.toString()); win.setAttribute("hwnd", w.hwndStr); win.setAttribute("text", w.text); @@ -159,6 +180,9 @@ public class WindowsEnumeratedXml implements Runnable{ win.setAttribute("class", w.className); if (w.className != null) win.setAttribute("CLASS", w.className.toUpperCase()); + if (w.controlType != null) + if (!w.controlType.isEmpty()) + win.setAttribute("type", w.controlType); if (!w.isChild) { parentCount++; if (w.processName != null && !w.processName.isEmpty()) { @@ -207,6 +231,7 @@ public class WindowsEnumeratedXml implements Runnable{ totals.setAttribute("windowCount", infoList.size()+""); totals.setAttribute("wpfWrapperCount", wpfParentList.size()+""); totals.setAttribute("wpfCount", wpfCount+""); + totals.setAttribute("winFormCount", winFormCount+""); totals.setAttribute("silverlightCount", silverlightCount+""); totals.setAttribute("menuCount", menuCount+""); totals.setAttribute("processCount", processList.size()+""); @@ -242,22 +267,19 @@ public class WindowsEnumeratedXml implements Runnable{ return xmlElement; } - public static Map EnumerateWindowsWithWpfBridge(String parentHwndStr, String frameworkType) { + public static Map EnumerateWindowsWithUiaBridge(UiaBridge uiabridge, String parentHwndStr, String frameworkType) { final Map infoList = new LinkedHashMap(); - WpfBridge wb = new WpfBridge(); - wb.setFrameworkId(frameworkType); - if (SynthuseDlg.config.isFilterWpfDisabled()) - wb.setTouchableOnly(false); + //WpfBridge wb = new WpfBridge(); + //wpf.setFrameworkId(frameworkType); long hwnd = Long.parseLong(parentHwndStr); - //List parentIds = new ArrayList(Arrays.asList(wb.enumChildrenWindowIds(""))); - //System.out.println("getRuntimeIdFromHandle"); - String parentRuntimeId = wb.getRuntimeIdFromHandle(hwnd); + //System.out.println("getRuntimeIdFromHandle of " + hwnd); + String parentRuntimeId = uiabridge.getWindowInfo((int) hwnd, WindowInfo.UIA_RUNTIME_ID); //System.out.println("runtimeId=" + runtimeId); String[] allIds = null; if (SynthuseDlg.config.isFilterWpfDisabled()) - allIds = wb.enumDescendantWindowInfo(parentRuntimeId, "*"); + allIds = uiabridge.enumWindowInfo((int) hwnd, "*"); else - allIds = wb.enumDescendantWindowInfo(parentRuntimeId, WindowInfo.WPF_PROPERTY_LIST); + allIds = uiabridge.enumWindowInfo((int) hwnd, WindowInfo.UIA_PROPERTY_LIST); if (allIds == null) return infoList; //empty list //System.out.println("enumDescendantWindowIds " + allIds.length); diff --git a/src/org/synthuse/XpathManager.java b/src/org/synthuse/XpathManager.java index 17f632f..08c26d7 100644 --- a/src/org/synthuse/XpathManager.java +++ b/src/org/synthuse/XpathManager.java @@ -21,9 +21,8 @@ import com.sun.jna.platform.win32.WinDef.HWND; public class XpathManager implements Runnable{ private HWND hwnd = null; - private String runtimeId = null; + private String enumProperties = null; private JTextPane windowsXmlTextPane = null; - private WpfBridge wpf = null; public static interface Events { void statusChanged(String status); @@ -44,12 +43,11 @@ public class XpathManager implements Runnable{ this.windowsXmlTextPane = windowsXmlTextPane; } - public XpathManager(HWND hwnd, String runtimeId, JTextPane windowsXmlTextPane, Events events) { + public XpathManager(HWND hwnd, String enumProperties, JTextPane windowsXmlTextPane, Events events) { this.events = events; this.hwnd = hwnd; - this.runtimeId = runtimeId; + this.enumProperties = enumProperties; this.windowsXmlTextPane = windowsXmlTextPane; - this.wpf = new WpfBridge(); } @Override @@ -78,44 +76,46 @@ public class XpathManager implements Runnable{ return escapedTxtStr; } - public String buildWpfXpathStatement() { + public String buildUiaXpathStatement() { + if (enumProperties == null) + return ""; + if (enumProperties.isEmpty()) + return ""; String builtXpath = ""; String xml = this.windowsXmlTextPane.getText(); - String classStr = wpf.getWindowClass(runtimeId); - //System.out.println("class: " + classStr); - String txtOrig = wpf.getWindowText(runtimeId); - String winValueOrig = wpf.getWindowValue(runtimeId); - if (classStr == null || txtOrig == null) + + WindowInfo wi = new WindowInfo(enumProperties, true); + String onlyRuntimeIdXpath = "//*[@hwnd='" + wi.runtimeId + "']"; + List wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, onlyRuntimeIdXpath); + //System.out.println("evaluateXpathGetValues1: " + onlyRuntimeIdXpath + " = " + wpfResultList.size()); + if (wpfResultList.size() == 0) + return""; + //System.out.println("enumProperties: " + enumProperties); + String typeStr = wi.controlType; + String txtOrig = wi.text; + //String winValueOrig = wpf.getWindowValue(runtimeId); + if (typeStr == null || txtOrig == null) return ""; //System.out.println("text: " + txtOrig); String txtStr = compareLongTextString(txtOrig); - String valueStr = ""; - if (winValueOrig != null) - if (!winValueOrig.isEmpty()) //if value attribute exists then use it too - valueStr = " and starts-with(@value,'" + compareLongTextString(winValueOrig) + "')"; - - builtXpath = "//*[@class='" + classStr + "' and starts-with(@text,'" + txtStr + "')" + valueStr + "]"; + builtXpath = "//*[@type='" + typeStr + "' and starts-with(@text,'" + txtStr + "')" + "]"; //builtXpath = "//*[@hwnd='" + runtimeId + "']"; - //System.out.println("evaluateXpathGetValues: " + builtXpath); - List wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); + wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); + //System.out.println("evaluateXpathGetValues2: " + builtXpath + " = " + wpfResultList.size()); if (wpfResultList.size() == 1) return builtXpath; - builtXpath = "//*[@hwnd='" + runtimeId + "']"; - wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); - if (wpfResultList.size() > 0) - return builtXpath; - return ""; + return onlyRuntimeIdXpath; } public String buildXpathStatement() { String builtXpath = ""; try { String xml = this.windowsXmlTextPane.getText(); - if (runtimeId != null && !SynthuseDlg.config.isWpfBridgeDisabled()) { - if (!runtimeId.isEmpty()) { - builtXpath = buildWpfXpathStatement(); + if (enumProperties != null && !SynthuseDlg.config.isWpfBridgeDisabled()) { + if (!enumProperties.isEmpty()) { + builtXpath = buildUiaXpathStatement(); } } if (builtXpath != "") @@ -164,6 +164,9 @@ public class XpathManager implements Runnable{ } builtXpath = "//win[@class='" + classStr + "'" + txtStr + "]"; } + resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); + if (resultList.size() > 1) //still too many matched, only use hwnd + builtXpath = "//win[@hwnd='" + handleStr + "']"; } } catch (Exception e) { e.printStackTrace(); diff --git a/src/org/synthuse/commands/BaseCommand.java b/src/org/synthuse/commands/BaseCommand.java index 958b119..03abcf1 100644 --- a/src/org/synthuse/commands/BaseCommand.java +++ b/src/org/synthuse/commands/BaseCommand.java @@ -16,7 +16,7 @@ public class BaseCommand { static long LAST_UPDATED_XML = 0; protected Api api = new Api(); - protected WpfBridge wpf = new WpfBridge(); + protected UiaBridge wpf = new UiaBridge(); protected CommandProcessor parentProcessor = null; protected int getExecuteErrorCount() { @@ -116,6 +116,7 @@ public class BaseCommand { resultStr = item; break; } + resultStr = resultStr.replaceAll("[^\\d-.]", ""); //remove all non-numeric values (except dash -) if (WinPtr.isWpfRuntimeIdFormat(resultStr)) { result.runtimeId = resultStr; if (!ignoreFailedFind && result.isEmpty()) @@ -143,7 +144,7 @@ public class BaseCommand { if (item.contains("hmenu=")) { List list = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@id"); if (list.size() > 0) - resultStr = list.get(0); //get first id; + resultStr = list.get(0); //get first id } else resultStr = item; diff --git a/src/org/synthuse/test/UnitTestHelper.java b/src/org/synthuse/test/UnitTestHelper.java index ce82523..fc75158 100644 --- a/src/org/synthuse/test/UnitTestHelper.java +++ b/src/org/synthuse/test/UnitTestHelper.java @@ -54,7 +54,7 @@ public class UnitTestHelper { byte[] buffer = new byte[1024]; int readBytes; // Open and check input stream - InputStream is = WpfBridgeTest.class.getResourceAsStream(path); + InputStream is = UnitTestHelper.class.getResourceAsStream(path); if (is == null) { //check if valid System.out.println("File " + path + " was not found inside JAR."); return null; diff --git a/src/org/synthuse/test/WpfBridgeTest.java b/src/org/synthuse/test/WpfBridgeTest.java deleted file mode 100644 index 9749950..0000000 --- a/src/org/synthuse/test/WpfBridgeTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.synthuse.test; -import static org.junit.Assert.*; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.synthuse.*; - -public class WpfBridgeTest { - - - @BeforeClass - public static void setUpBeforeClass() { - //this runs only once for this class - UnitTestHelper.RunApp("/org/synthuse/test/WpfMockTestApp.exe"); - } - - @AfterClass - public static void tearDownAfterClass() { - //this runs only once for this class - UnitTestHelper.DestroyApp(); - } - - @Before - public void setUp() throws Exception { - - } - - @After - public void tearDown() throws Exception { - - } - - @Test - public void countChildrenWin32() { - WpfBridge wb = new WpfBridge(); - wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF - int win32Cnt = wb.countChildrenWindows(); - System.out.println("win32 countChildrenWindows: " + win32Cnt); - assertTrue(win32Cnt > 0); - wb.setTouchableOnly(false);//disable filter - int ufwin32Cnt = wb.countChildrenWindows(); - System.out.println("win32 unfiltered countChildrenWindows: " + ufwin32Cnt); - assertTrue(ufwin32Cnt >= win32Cnt); - } - - @Test - public void countChildrenWpf() { - WpfBridge wb = new WpfBridge(); - wb.setFrameworkId("WPF");// maybe not WPF - wb.setTouchableOnly(true);//enable filter - System.out.println("wpf countChildrenWindows: " + wb.countChildrenWindows()); - wb.setTouchableOnly(false);//disable filter - System.out.println("wpf unfiltered countChildrenWindows: " + wb.countChildrenWindows()); - } - - //@Test - public void countDescendantsWin32() { - WpfBridge wb = new WpfBridge(); - wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF - int win32Cnt = wb.countDescendantWindows(); - System.out.println("win32 countDescendantWindows: " + win32Cnt); - assertTrue(win32Cnt > 0); - wb.setTouchableOnly(false);//disable filter - int ufwin32Cnt = wb.countDescendantWindows(); - System.out.println("win32 unfiltered countDescendantWindows: " + ufwin32Cnt); - assertTrue(ufwin32Cnt >= win32Cnt); - } - - //@Test - public void countDescendantsWpf() { - WpfBridge wb = new WpfBridge(); - wb.setFrameworkId("WPF");// maybe not WPF - wb.setTouchableOnly(true);//enable filter - System.out.println("wpf countDescendantWindows: " + wb.countDescendantWindows()); - wb.setTouchableOnly(false);//disable filter - System.out.println("wpf unfiltered countDescendantWindows: " + wb.countDescendantWindows()); - } - - - @Test - public void getRuntimeFromHandle() { - WpfBridge wb = new WpfBridge(); - Api api = new Api(); - wb.setFrameworkId("WPF"); - WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe - long handle = Long.parseLong(wp.hWndStr); - if (handle == 0) - return; - System.out.println("calling getRuntimeIdFromHandle: " + handle); - String rid = wb.getRuntimeIdFromHandle(handle); - System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid); - assertTrue(rid != null); - - String[] props = wb.getPropertiesAndValues(rid); - assertTrue(props != null); - - //for(String p : props) - // System.out.println(p); - } - - @Test - public void enumerateWindowInfo() { - WpfBridge wb = new WpfBridge(); - Api api = new Api(); - wb.setFrameworkId("WPF"); - wb.setTouchableOnly(false); - WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe - long handle = Long.parseLong(wp.hWndStr); - if (handle == 0) - return; - String rid = wb.getRuntimeIdFromHandle(handle); - System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid); - if (rid == null) - return; - String[] wInfo = wb.enumDescendantWindowInfo(rid, WindowInfo.WPF_PROPERTY_LIST); - System.out.println("enumDescendantWindowInfo length: " + wInfo.length); - System.out.println(WindowInfo.WPF_PROPERTY_LIST); - for(String w : wInfo) - System.out.println(w); - } - -} diff --git a/synthuse.properties b/synthuse.properties index 734eda2..4eb8c02 100644 --- a/synthuse.properties +++ b/synthuse.properties @@ -1,8 +1,8 @@ # -#Tue Apr 08 22:36:47 EDT 2014 +#Tue Apr 29 23:17:11 EDT 2014 DEFAULT_PROP_FILENAME= urlList= disableFiltersWpf=false -xpathList= -xpathHightlight=.*process\="([^"]*)".* +xpathList=//*[@class\='TextBlock' and starts-with(@text,'CheckBox is here')]\u00BA//win[@class\='HwndWrapper[WpfMockTestApp.exe;;e0e45a66-6643-4c32-a6b8-6313b79ee700]']\u00BA//win[starts-with(@class,'HwndWrapper[WpfMockTestApp.exe;;')]\u00BA//win[starts-with(@class,'HwndWrapper[WpfMockTestApp.exe;;' and @TEXT\="MAINWINDOW")]\u00BA//win[starts-with(@class,'HwndWrapper[WpfMockTestApp.exe;;' and @TEXT\='MAINWINDOW')]\u00BA//win[starts-with(@class,'HwndWrapper[WpfMockTestApp.exe;;') and @TEXT\='MAINWINDOW']\u00BA//win[@class\='HwndWrapper[WpfMockTestApp.exe;;7c11a88a-1622-4ec8-8a29-0d7f20d3f36d]']\u00BA//win[@class\='HwndWrapper']\u00BA//win[starts-with(@class,'HwndWrapper')]\u00BA//wpf\u00BA//*[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1' and starts-with(@text,'')]\u00BA//*[@ListViewCount]\u00BA//*[@menuCount]\u00BA//*[@dgText]\u00BA//menus\u00BA//menu\u00BA//win[@class\='SunAwtFrame']\u00BA//win[@class\='Edit']\u00BA//win[@class\='Notepad']\u00BA//*[@class\='\#32768']\u00BA//menu[@text\='Help']\u00BA//win[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1']/win[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1'][2]\u00BA//*[@tbCount]\u00BA//win[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1']/win[@class\='WindowsForms10.EDIT.app.0.2bf8098_r13_ad1']\u00BA//*[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1' and starts-with(@text,'Form1')]\u00BA//*[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1' and starts-with(@text,'Win Forms Mock Test ')]\u00BA//*[@class\='' and starts-with(@text,'toolStripButton1')]\u00BA//win[@class\='3147770']\u00BA//*[@hwnd\='52298946']\u00BA//*[@hwnd\='14222438']\u00BA//*[@hwnd\='9175840']\u00BA//*[@hwnd\='6751400']\u00BA//*[starts-with(@class,'WindowsForms')]\u00BA//win[starts-with(@class,'WindowsForms')]\u00BA/*/win[starts-with(@class,'WindowsForms')]\u00BA//winfrm\u00BA//win[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1']/win[@class\='WindowsForms10.EDIT.app.0.2bf8098_r13_ad1'][2]\u00BA//*[@class\='WindowsForms10.Window.8.app.0.2bf8098_r13_ad1']/win[@class\='WindowsForms10.EDIT.app.0.2bf8098_r13_ad1']\u00BA//*[@type\='button' and starts-with(@text,'button1')]\u00BA disableWpf=false +xpathHightlight=.*process\="([^"]*)".*