diff --git a/native/WpfBridge/AssemblyInfo.cpp b/native/WpfBridge/AssemblyInfo.cpp index 118f215..8d9e333 100644 --- a/native/WpfBridge/AssemblyInfo.cpp +++ b/native/WpfBridge/AssemblyInfo.cpp @@ -37,7 +37,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.0.*")]; +[assembly:AssemblyVersionAttribute("1.1.*")]; [assembly:ComVisible(false)]; diff --git a/native/WpfBridge/WpfAutomation.cpp b/native/WpfBridge/WpfAutomation.cpp index 31a9ee2..14e27ce 100644 --- a/native/WpfBridge/WpfAutomation.cpp +++ b/native/WpfBridge/WpfAutomation.cpp @@ -13,6 +13,7 @@ using namespace System::Windows::Automation; WpfAutomation::WpfAutomation(void) { this->frameworkId = WpfAutomation::DEFAULT_FRAMEWORK; + this->touchableOnly = true; } void WpfAutomation::setFrameworkId(System::String ^propertyValue) @@ -20,6 +21,21 @@ 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 = gcnew array(3); + cons[0] = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId);//is WPF framework + cons[1] = gcnew PropertyCondition(AutomationElement::IsEnabledProperty, this->touchableOnly);//is enabled + cons[2] = gcnew PropertyCondition(AutomationElement::IsOffscreenProperty, !this->touchableOnly);// is off screen + System::Windows::Automation::Condition ^result = gcnew System::Windows::Automation::AndCondition(cons); + return result; +} + array ^ WpfAutomation::convertRuntimeIdString(System::String ^runtimeIdValue) { System::String ^delim = L"-"; @@ -37,9 +53,9 @@ AutomationElement ^ WpfAutomation::findAutomationElementById(System::String ^run if (runtimeIdValue == nullptr || runtimeIdValue->Equals(L"")) return AutomationElement::RootElement; array ^idArray = this->convertRuntimeIdString(runtimeIdValue); - Condition ^pcFramework = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId); + //Condition ^pcFramework = gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId); Condition ^pcRunId = gcnew PropertyCondition(AutomationElement::RuntimeIdProperty, idArray); - Condition ^frameworkAndRuntimeId = gcnew AndCondition(pcFramework, pcRunId); + Condition ^frameworkAndRuntimeId = gcnew AndCondition(getSearchConditions(), pcRunId); return AutomationElement::RootElement->FindFirst(TreeScope::Descendants, frameworkAndRuntimeId); } @@ -76,7 +92,9 @@ System::String ^ WpfAutomation::getRuntimeIdFromElement(System::Windows::Automat System::Int32 WpfAutomation::countDescendantWindows() { //AutomationElementCollection ^aec = rootElem->FindAll(TreeScope::Children, Condition::TrueCondition); - AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Descendants, getSearchConditions()); + if (aec == nullptr) + return 0; System::Int32 result = aec->Count; //delete aec; return result; @@ -85,7 +103,9 @@ System::Int32 WpfAutomation::countDescendantWindows() System::Int32 WpfAutomation::countDescendantWindows(System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); + if (aec == nullptr) + return 0; System::Int32 result = aec->Count; //delete aec; //delete frameworkAndRuntimeId; @@ -95,7 +115,9 @@ System::Int32 WpfAutomation::countDescendantWindows(System::String ^runtimeIdVal System::Int32 WpfAutomation::countChildrenWindows() { //AutomationElementCollection ^aec = rootElem->FindAll(TreeScope::Children, Condition::TrueCondition); - AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Children, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = AutomationElement::RootElement->FindAll(TreeScope::Children, getSearchConditions()); + if (aec == nullptr) + return 0; System::Int32 result = aec->Count; //delete aec; return result; @@ -104,7 +126,9 @@ System::Int32 WpfAutomation::countChildrenWindows() System::Int32 WpfAutomation::countChildrenWindows(System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, getSearchConditions()); + if (aec == nullptr) + return 0; System::Int32 result = aec->Count; //delete aec; //delete frameworkAndRuntimeId; @@ -114,14 +138,18 @@ System::Int32 WpfAutomation::countChildrenWindows(System::String ^runtimeIdValue array ^ WpfAutomation::enumChildrenWindowIds(System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Children, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + 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); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); + if (aec == nullptr) + return nullptr; return getRuntimeIdsFromCollection(aec); } @@ -131,22 +159,27 @@ array ^ WpfAutomation::enumDescendantWindowIds(System::Int32 p 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, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); + if (aec == nullptr) + return nullptr; return getRuntimeIdsFromCollection(aec); } -array ^ WpfAutomation::EnumDescendantWindowIdsFromHandle(System::IntPtr windowHandle) +System::String ^ WpfAutomation::getRuntimeIdFromHandle(System::IntPtr windowHandle) { //AutomationElement test = AutomationElement.FromHandle(new System.IntPtr(123123)); - AutomationElement ^parent = AutomationElement::FromHandle(windowHandle); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); - return getRuntimeIdsFromCollection(aec); + AutomationElement ^target = AutomationElement::FromHandle(windowHandle); + if (target == nullptr) + return nullptr; + return getRuntimeIdFromElement(target); } -array ^ WpfAutomation::EnumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties) +array ^ WpfAutomation::enumDescendantWindowInfo(System::String ^runtimeIdValue, System::String ^properties) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); - AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, gcnew PropertyCondition(AutomationElement::FrameworkIdProperty, this->frameworkId)); + if (parent == nullptr) + return nullptr; + AutomationElementCollection ^aec = parent->FindAll(TreeScope::Descendants, getSearchConditions()); //create array for keeping order of properties System::String ^delim = L","; @@ -213,9 +246,19 @@ array ^ WpfAutomation::EnumDescendantWindowInfo(System::String 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); + if (target == nullptr) + return nullptr; TreeWalker ^tw = TreeWalker::ControlViewWalker; AutomationElement ^parent = tw->GetParent(target); return getRuntimeIdFromElement(parent); @@ -224,6 +267,8 @@ System::String ^ WpfAutomation::getParentRuntimeId(System::String ^runtimeIdValu System::String ^ WpfAutomation::getProperty(System::String ^propertyName, System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + if (parent == nullptr) + return nullptr; //System::Object ^currentVal = parent->GetCurrentPropertyValue(AutomationElement::RuntimeIdProperty); array ^aps = parent->GetSupportedProperties(); for each(AutomationProperty ^ap in aps) @@ -238,9 +283,12 @@ System::String ^ WpfAutomation::getProperty(System::String ^propertyName, System return nullptr; } + array ^ WpfAutomation::getProperties(System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + if (parent == nullptr) + return nullptr; array ^aps = parent->GetSupportedProperties(); array ^propStrArray = gcnew array(aps->Length); System::Int32 count = 0; @@ -258,6 +306,8 @@ array ^ WpfAutomation::getProperties(System::String ^runtimeId array ^ WpfAutomation::getPropertiesAndValues(System::String ^runtimeIdValue) { AutomationElement ^parent = findAutomationElementById(runtimeIdValue); + if (parent == nullptr) + return nullptr; array ^aps = parent->GetSupportedProperties(); array ^propStrArray = gcnew array(aps->Length); System::Int32 count = 0; diff --git a/native/WpfBridge/WpfAutomation.h b/native/WpfBridge/WpfAutomation.h index ea9308e..a22d6bc 100644 --- a/native/WpfBridge/WpfAutomation.h +++ b/native/WpfBridge/WpfAutomation.h @@ -11,6 +11,7 @@ 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(); @@ -22,11 +23,11 @@ public: 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); - array ^ EnumDescendantWindowIdsFromHandle(System::IntPtr windowHandle); //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); - + 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); @@ -36,8 +37,11 @@ private: System::Windows::Automation::AutomationElement ^ findAutomationElementById(System::String ^runtimeIdValue); 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/WpfBridge.vcxproj b/native/WpfBridge/WpfBridge.vcxproj index 93b88ec..3e86389 100644 --- a/native/WpfBridge/WpfBridge.vcxproj +++ b/native/WpfBridge/WpfBridge.vcxproj @@ -148,6 +148,7 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\wpfbridge$(PlatformArchitecture)$(Targ + diff --git a/native/WpfBridge/bin/wpfbridge32.dll b/native/WpfBridge/bin/wpfbridge32.dll index b275d90..e7732cc 100644 Binary files a/native/WpfBridge/bin/wpfbridge32.dll and b/native/WpfBridge/bin/wpfbridge32.dll differ diff --git a/native/WpfBridge/bin/wpfbridge64.dll b/native/WpfBridge/bin/wpfbridge64.dll index d063b13..fa640a9 100644 Binary files a/native/WpfBridge/bin/wpfbridge64.dll and b/native/WpfBridge/bin/wpfbridge64.dll differ diff --git a/native/WpfBridge/org_synthuse_WpfBridge.cpp b/native/WpfBridge/org_synthuse_WpfBridge.cpp index a23de4c..85e6d0e 100644 --- a/native/WpfBridge/org_synthuse_WpfBridge.cpp +++ b/native/WpfBridge/org_synthuse_WpfBridge.cpp @@ -22,6 +22,16 @@ JNIEXPORT void JNICALL Java_org_synthuse_WpfBridge_setFrameworkId(JNIEnv *env, j 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 @@ -80,7 +90,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumChildrenWindowIds { 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); @@ -107,7 +118,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI { 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); @@ -133,7 +145,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI 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); @@ -148,22 +161,17 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI /* * Class: org_synthuse_WpfBridge - * Method: EnumDescendantWindowIdsFromHandle - * Signature: (J)[Ljava/lang/String; + * Method: getRuntimeIdFromHandle + * Signature: (J)Ljava/lang/String; */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIdsFromHandle(JNIEnv *env, jobject obj, jlong jwindowHandle) +JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getRuntimeIdFromHandle(JNIEnv *env, jobject obj, jlong jwindowHandle) { - array ^mchildrenIds = Global::WPF_AUTO->EnumDescendantWindowIdsFromHandle(System::IntPtr(jwindowHandle)); - - //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); + 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 - for(int i = 0 ; i < mchildrenIds->Length ; i++) - { - env->SetObjectArrayElement(results, i, env->NewStringUTF(context.marshal_as(mchildrenIds[i]))); - } - return results; + jstring result = env->NewStringUTF(context.marshal_as(mrunId)); + return result; } /* @@ -175,7 +183,9 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI { 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)); + 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); @@ -190,6 +200,21 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI } +/* + * 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 @@ -199,6 +224,8 @@ JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getParentRuntimeId(JNIEnv { 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 @@ -215,6 +242,8 @@ JNIEXPORT jstring JNICALL Java_org_synthuse_WpfBridge_getProperty(JNIEnv *env, j 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 @@ -232,7 +261,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getProperties(JNIEnv { 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); @@ -255,7 +285,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_getPropertiesAndValue { 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); diff --git a/native/WpfBridge/org_synthuse_WpfBridge.h b/native/WpfBridge/org_synthuse_WpfBridge.h index 8ecb288..6a546b9 100644 --- a/native/WpfBridge/org_synthuse_WpfBridge.h +++ b/native/WpfBridge/org_synthuse_WpfBridge.h @@ -15,6 +15,14 @@ extern "C" { 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 @@ -71,14 +79,6 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIds__J (JNIEnv *, jobject, jlong); -/* - * Class: org_synthuse_WpfBridge - * Method: enumDescendantWindowIdsFromHandle - * Signature: (J)[Ljava/lang/String; - */ -JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowIdsFromHandle - (JNIEnv *, jobject, jlong); - /* * Class: org_synthuse_WpfBridge * Method: enumDescendantWindowInfo @@ -87,6 +87,22 @@ JNIEXPORT jobjectArray JNICALL Java_org_synthuse_WpfBridge_enumDescendantWindowI 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 diff --git a/src/org/synthuse/Api.java b/src/org/synthuse/Api.java index 0cb236d..469ddb8 100644 --- a/src/org/synthuse/Api.java +++ b/src/org/synthuse/Api.java @@ -129,6 +129,7 @@ public class Api { boolean EnumChildWindows(HWND hWnd, WNDENUMPROC lpEnumFunc, Pointer data); HWND GetParent(HWND hWnd); boolean IsWindowVisible(HWND hWnd); + boolean IsWindow(HWND hWnd); int GetWindowRect(HWND hWnd, RECT r); int MapWindowPoints(HWND hWndFrom, HWND hWndTo, RECT r, int cPoints); diff --git a/src/org/synthuse/Config.java b/src/org/synthuse/Config.java index d5afe05..8440901 100644 --- a/src/org/synthuse/Config.java +++ b/src/org/synthuse/Config.java @@ -25,4 +25,9 @@ public class Config extends PropertiesSerializer { super(propertyFilename); load(propertyFilename); } + + public boolean isWpfBridgeDisabled() + { + return disableWpf.equals("true") || disableWpf.equals("True"); + } } diff --git a/src/org/synthuse/SynthuseDlg.java b/src/org/synthuse/SynthuseDlg.java index 5249d47..5f9e1e1 100644 --- a/src/org/synthuse/SynthuseDlg.java +++ b/src/org/synthuse/SynthuseDlg.java @@ -95,12 +95,14 @@ public class SynthuseDlg extends JFrame { public static Config config = new Config(Config.DEFAULT_PROP_FILENAME); private String dialogResult = ""; private String lastDragHwnd = ""; + private String lastRuntimeId =""; private JComboBox cmbXpath; private JButton btnTestIde; private TestIdeFrame testIde = null; private int targetX; private int targetY; + private WpfBridge wpf = new WpfBridge(); /** * Launch the application. @@ -423,16 +425,17 @@ 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)); - - lblStatus.setText("class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY); - if (!lastDragHwnd.equals(handleStr)) { + String runtimeId = wpf.getRuntimeIdFromPoint(targetX, targetY); + lblStatus.setText("rid:" + runtimeId + " class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY); + if (!lastDragHwnd.equals(handleStr) || !lastRuntimeId.equals(runtimeId)) { if (!lastDragHwnd.isEmpty()) { Api.refreshWindow(Api.GetHandleFromString(lastDragHwnd)); } lastDragHwnd = handleStr; + lastRuntimeId = runtimeId; //lastDragHwnd = (hwnd + ""); Api.highlightWindow(hwnd); - XpathManager.buildXpathStatementThreaded(hwnd, textPane, xpathEvents); + XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents); } } } diff --git a/src/org/synthuse/WinPtr.java b/src/org/synthuse/WinPtr.java new file mode 100644 index 0000000..03fe5d2 --- /dev/null +++ b/src/org/synthuse/WinPtr.java @@ -0,0 +1,52 @@ +package org.synthuse; + +import com.sun.jna.platform.win32.WinDef.HWND; + +public class WinPtr { + + public HWND hWnd = null; + public String hWndStr = ""; + public String runtimeId = ""; + + public WinPtr() { + } + + public WinPtr(HWND hWnd) { + this.hWnd = hWnd; + this.hWndStr = Api.GetHandleAsString(hWnd); + } + + public WinPtr(String runtimeId) { + this.runtimeId = runtimeId; + } + + public boolean isWin32() { + return (hWnd != null || !hWndStr.equals("")); + } + + public boolean isWpf() { + return (!runtimeId.equals("")); + } + + public boolean isEmpty() { + return (hWnd == null && hWndStr.equals("") && runtimeId.equals("")); + } + + public static boolean isWpfRuntimeIdFormat(String runtimeIdTest) { + return (runtimeIdTest.contains("-")); + } + + public String toString() { + if (isWin32() && !hWndStr.equals("")) + return hWndStr; + else if (isWin32() && hWnd != null) + { + hWndStr = Api.GetHandleAsString(hWnd); + return hWndStr; + } + else if (isWpf()) + return runtimeId; + else + return null; + } +} diff --git a/src/org/synthuse/WindowsEnumeratedXml.java b/src/org/synthuse/WindowsEnumeratedXml.java index 4973a91..647f8be 100644 --- a/src/org/synthuse/WindowsEnumeratedXml.java +++ b/src/org/synthuse/WindowsEnumeratedXml.java @@ -12,7 +12,6 @@ import java.io.StringWriter; import java.sql.Timestamp; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; @@ -74,11 +73,16 @@ public class WindowsEnumeratedXml implements Runnable{ public static String getXml() { final Map infoList = new LinkedHashMap(); final Map processList = new LinkedHashMap(); + final Map wpfParentList = new LinkedHashMap(); + int wpfCount = 0; class ChildWindowCallback implements WinUser.WNDENUMPROC { @Override public boolean callback(HWND hWnd, Pointer lParam) { - infoList.put(Api.GetHandleAsString(hWnd), new WindowInfo(hWnd, true)); + WindowInfo wi = new WindowInfo(hWnd, true); + infoList.put(wi.hwndStr, wi); + if (wi.className.startsWith("HwndWrapper")) + wpfParentList.put(wi.hwndStr, null); return true; } } @@ -86,7 +90,10 @@ public class WindowsEnumeratedXml implements Runnable{ class ParentWindowCallback implements WinUser.WNDENUMPROC { @Override public boolean callback(HWND hWnd, Pointer lParam) { - infoList.put(Api.GetHandleAsString(hWnd), new WindowInfo(hWnd, false)); + WindowInfo wi = new WindowInfo(hWnd, false); + infoList.put(wi.hwndStr, wi); + if (wi.className.startsWith("HwndWrapper")) + wpfParentList.put(wi.hwndStr, null); Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0)); return true; } @@ -94,8 +101,19 @@ public class WindowsEnumeratedXml implements Runnable{ Api.User32.instance.EnumWindows(new ParentWindowCallback(), 0); //Enumerate WPF windows and add to list - if (!SynthuseDlg.config.disableWpf.equals("true")) - infoList.putAll(EnumerateWindowsWithWpfBridge("WPF")); + if (!SynthuseDlg.config.isWpfBridgeDisabled()) + { + ////win[starts-with(@class,'HwndWrapper')] + //WpfBridge wb = new WpfBridge(); + //if (wb.countDescendantWindows() > 0) + for (String handle : wpfParentList.keySet()) { + //WindowInfo w = wpfParentList.get(handle); + //System.out.println("EnumerateWindowsWithWpfBridge: " + handle); + Map wpfInfoList = EnumerateWindowsWithWpfBridge(handle, "WPF"); + wpfCount += wpfInfoList.size(); + infoList.putAll(wpfInfoList); + } + } // convert window info list to xml dom try { @@ -159,6 +177,8 @@ public class WindowsEnumeratedXml implements Runnable{ totals.setAttribute("parentCount", parentCount+""); totals.setAttribute("childCount", childCount+""); totals.setAttribute("windowCount", infoList.size()+""); + totals.setAttribute("wpfWrapperCount", wpfParentList.size()+""); + totals.setAttribute("wpfCount", wpfCount+""); totals.setAttribute("processCount", processList.size()+""); totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + ""); rootElement.appendChild(totals); @@ -172,24 +192,26 @@ public class WindowsEnumeratedXml implements Runnable{ return ""; } - public static Map EnumerateWindowsWithWpfBridge(String frameworkType) { + public static Map EnumerateWindowsWithWpfBridge(String parentHwndStr, String frameworkType) { final Map infoList = new LinkedHashMap(); WpfBridge wb = new WpfBridge(); wb.setFrameworkId(frameworkType); - List parentIds = new ArrayList(Arrays.asList(wb.enumChildrenWindowIds(""))); - //System.out.println("enumChildrenWindowIds"); - String[] allIds = wb.enumDescendantWindowInfo("", WindowInfo.WPF_PROPERTY_LIST); + 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("runtimeId=" + runtimeId); + String[] allIds = wb.enumDescendantWindowInfo(parentRuntimeId, WindowInfo.WPF_PROPERTY_LIST); + if (allIds == null) + return infoList; //empty list //System.out.println("enumDescendantWindowIds " + allIds.length); for(String runtimeIdAndInfo : allIds) { //System.out.println("getting window info for: " + runtimeIdAndInfo); - String onlyRuntimeId = runtimeIdAndInfo; - if (runtimeIdAndInfo.contains(",")) - onlyRuntimeId = runtimeIdAndInfo.substring(0, runtimeIdAndInfo.indexOf(",")); + WindowInfo wi = new WindowInfo(runtimeIdAndInfo, true); + if (wi.parentStr.equals(parentRuntimeId)) + wi.parentStr = parentHwndStr; //System.out.println("is parent? " + onlyRuntimeId); - if (parentIds.contains(onlyRuntimeId)) //is Parent? - infoList.put(onlyRuntimeId, new WindowInfo(runtimeIdAndInfo, false)); - else// must be child - infoList.put(onlyRuntimeId, new WindowInfo(runtimeIdAndInfo, true)); + infoList.put(wi.runtimeId, wi); } return infoList; } diff --git a/src/org/synthuse/WpfBridge.java b/src/org/synthuse/WpfBridge.java index 769e236..c76cf1c 100644 --- a/src/org/synthuse/WpfBridge.java +++ b/src/org/synthuse/WpfBridge.java @@ -7,6 +7,7 @@ package org.synthuse; +import java.awt.Point; import java.io.*; public class WpfBridge { @@ -69,24 +70,57 @@ public class WpfBridge { System.load(temp.getAbsolutePath()); } - native void setFrameworkId(String propertyValue); //default is WPF, but also accepts Silverlight, Win32 + public native void setFrameworkId(String propertyValue); //default is WPF, but also accepts Silverlight, Win32 + public native void setTouchableOnly(boolean val); //default is true //Descendants will walk the full tree of windows, NOT just one level of children - native int countDescendantWindows(); - native int countDescendantWindows(String runtimeIdValue); + public native int countDescendantWindows(); + public native int countDescendantWindows(String runtimeIdValue); - native int countChildrenWindows(); - native int countChildrenWindows(String runtimeIdValue); + public native int countChildrenWindows(); + public native int countChildrenWindows(String runtimeIdValue); - native String[] enumChildrenWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop - native String[] enumDescendantWindowIds(String runtimeIdValue); //if runtimeIdValue is null will start at desktop - native String[] enumDescendantWindowIds(long processId); - native String[] enumDescendantWindowIdsFromHandle(long windowHandle); + public native 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. - native String[] enumDescendantWindowInfo(String runtimeIdValue, String properties); //returns properties comma separated + public native String[] enumDescendantWindowInfo(String runtimeIdValue, String properties); //returns properties comma separated - native String getParentRuntimeId(String runtimeIdValue); - native String getProperty(String propertyName, String runtimeIdValue); - native String[] getProperties(String runtimeIdValue); - native String[] getPropertiesAndValues(String runtimeIdValue); + public native String getRuntimeIdFromHandle(long windowHandle); + public native String getRuntimeIdFromPoint(int x, int y); + public native String getParentRuntimeId(String runtimeIdValue); + 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[] boundarySplt = boundary.split(","); + int x = Integer.parseInt(boundarySplt[0]); + int y = Integer.parseInt(boundarySplt[1]); + int width = Integer.parseInt(boundarySplt[2]); + int height = Integer.parseInt(boundarySplt[3]); + p.x = ((width) /2) + x; + 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 getWindowAutomationId(String runtimeIdValue) { + return getProperty("AutomationIdProperty", runtimeIdValue); + } + + public String getWindowFramework(String runtimeIdValue) { + return getProperty("FrameworkIdProperty", runtimeIdValue); + } + } diff --git a/src/org/synthuse/XpathManager.java b/src/org/synthuse/XpathManager.java index 330d61e..2c11563 100644 --- a/src/org/synthuse/XpathManager.java +++ b/src/org/synthuse/XpathManager.java @@ -21,7 +21,9 @@ import com.sun.jna.platform.win32.WinDef.HWND; public class XpathManager implements Runnable{ private HWND hwnd = null; + private String runtimeId = null; private JTextPane windowsXmlTextPane = null; + private WpfBridge wpf = null; public static interface Events { void statusChanged(String status); @@ -42,6 +44,14 @@ public class XpathManager implements Runnable{ this.windowsXmlTextPane = windowsXmlTextPane; } + public XpathManager(HWND hwnd, String runtimeId, JTextPane windowsXmlTextPane, Events events) { + this.events = events; + this.hwnd = hwnd; + this.runtimeId = runtimeId; + this.windowsXmlTextPane = windowsXmlTextPane; + this.wpf = new WpfBridge(); + } + @Override public void run() { String results = buildXpathStatement(); @@ -53,14 +63,56 @@ public class XpathManager implements Runnable{ t.start(); } + public static void buildXpathStatementThreaded(HWND hwnd, String runtimeId, JTextPane windowsXmlTextPane, Events events) { + Thread t = new Thread(new XpathManager(hwnd, runtimeId, windowsXmlTextPane, events)); + t.start(); + } + + public String compareLongTextString(String rawText) { + String escapedTxtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(rawText); + if (!escapedTxtStr.isEmpty()) { + if (rawText.length() > 20) {// if the raw text is too long only test the first 20 characters + escapedTxtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(rawText.substring(0, 20)); + } + } + return escapedTxtStr; + } + + public String buildWpfXpathStatement() { + String builtXpath = ""; + String xml = this.windowsXmlTextPane.getText(); + String classStr = wpf.getWindowClass(runtimeId); + //System.out.println("class: " + classStr); + String txtOrig = wpf.getWindowText(runtimeId); + if (classStr == null || txtOrig == null) + return ""; + //System.out.println("text: " + txtOrig); + String txtStr = compareLongTextString(txtOrig); + builtXpath = "//wpf[@class='" + classStr + "' and starts-with(@text,'" + txtStr + "')]"; + //builtXpath = "//*[@hwnd='" + runtimeId + "']"; + System.out.println("evaluateXpathGetValues: " + builtXpath); + List wpfResultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); + if (wpfResultList.size() == 0) + return ""; + // return builtXpath; + return builtXpath; + } + public String buildXpathStatement() { String builtXpath = ""; try { + String xml = this.windowsXmlTextPane.getText(); + if (runtimeId != null && !SynthuseDlg.config.isWpfBridgeDisabled()) { + if (!runtimeId.isEmpty()) { + builtXpath = buildWpfXpathStatement(); + } + } + if (builtXpath != "") + return builtXpath; String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd)); String handleStr = Api.GetHandleAsString(hwnd); String txtOrig = Api.GetWindowText(hwnd); String txtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(txtOrig); - String xml = this.windowsXmlTextPane.getText(); builtXpath = "//win[@class='" + classStr + "']"; List resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath); //int matches = nextXpathMatch(builtXpath, textPane, true); diff --git a/src/org/synthuse/commands/BaseCommand.java b/src/org/synthuse/commands/BaseCommand.java index ae81354..9d74682 100644 --- a/src/org/synthuse/commands/BaseCommand.java +++ b/src/org/synthuse/commands/BaseCommand.java @@ -1,6 +1,7 @@ package org.synthuse.commands; +import java.awt.Point; import java.io.PrintWriter; import java.io.StringWriter; import java.sql.Timestamp; @@ -9,14 +10,13 @@ import java.util.List; import org.synthuse.*; -import com.sun.jna.platform.win32.WinDef.HWND; - public class BaseCommand { static String WIN_XML = ""; static long LAST_UPDATED_XML = 0; protected Api api = new Api(); + protected WpfBridge wpf = new WpfBridge(); protected CommandProcessor parentProcessor = null; protected int getExecuteErrorCount() { @@ -92,12 +92,12 @@ public class BaseCommand { return cmdResult; } - public HWND findHandleWithXpath(String xpath) { + public WinPtr findHandleWithXpath(String xpath) { return findHandleWithXpath(xpath, false); } - public HWND findHandleWithXpath(String xpath, boolean ignoreFailedFind) { - HWND result = null; + public WinPtr findHandleWithXpath(String xpath, boolean ignoreFailedFind) { + WinPtr result = new WinPtr(); double secondsFromLastUpdate = ((double)(System.nanoTime() - LAST_UPDATED_XML) / 1000000000); if (secondsFromLastUpdate > CommandProcessor.XML_UPDATE_THRESHOLD) { //default 5 second threshold WIN_XML = WindowsEnumeratedXml.getXml(); @@ -109,18 +109,34 @@ public class BaseCommand { for(String item: resultList) { if (item.contains("hwnd=")) { List hwndList = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@hwnd"); - resultStr = hwndList.get(0); + if (hwndList.size() > 0) + resultStr = hwndList.get(0); //get first hwnd; } else resultStr = item; break; } - result = Api.GetHandleFromString(resultStr); - if (result == null && !ignoreFailedFind) + if (WinPtr.isWpfRuntimeIdFormat(resultStr)) + result.runtimeId = resultStr; + else { + result.hWnd = Api.GetHandleFromString(resultStr); + if (!api.user32.IsWindow(result.hWnd)) + appendError("Error: Failed to located HWND(" + resultStr + ") from : " + xpath); + } + if (result.isEmpty()) appendError("Error: Failed to find window handle matching: " + xpath); return result; } + public Point getCenterWindowPosition(WinPtr handle) { + Point p = null; + if (handle.isWin32()) + p = api.getWindowPosition(handle.hWnd); + else + p = wpf.getCenterOfElement(handle.runtimeId); + return p; + } + public String convertListToString(List listStr, String delimiter) { StringBuilder result = new StringBuilder(""); for (String item: listStr) { diff --git a/src/org/synthuse/commands/MainCommands.java b/src/org/synthuse/commands/MainCommands.java index db042b2..4570c26 100644 --- a/src/org/synthuse/commands/MainCommands.java +++ b/src/org/synthuse/commands/MainCommands.java @@ -5,7 +5,6 @@ import java.awt.Toolkit; import java.io.IOException; import org.synthuse.*; -import com.sun.jna.platform.win32.WinDef.HWND; public class MainCommands extends BaseCommand { @@ -55,12 +54,12 @@ public class MainCommands extends BaseCommand { long totalAttempts = (long) (CommandProcessor.WAIT_TIMEOUT_THRESHOLD / (CommandProcessor.XML_UPDATE_THRESHOLD * 1000)); long attemptCount = 0; String xpath = "/EnumeratedWindows/win[@TEXT='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']"; - HWND handle = findHandleWithXpath(xpath, true); - if (handle != null)// first test without a timeout + WinPtr handle = findHandleWithXpath(xpath, true); + if (!handle.isEmpty())// first test without a timeout return true; while (attemptCount < totalAttempts) { handle = findHandleWithXpath(xpath, true); - if (handle != null) + if (!handle.isEmpty()) break; try {Thread.sleep((long)(CommandProcessor.XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();} ++attemptCount; @@ -76,12 +75,12 @@ public class MainCommands extends BaseCommand { long totalAttempts = (long) (CommandProcessor.WAIT_TIMEOUT_THRESHOLD / (CommandProcessor.XML_UPDATE_THRESHOLD * 1000)); long attemptCount = 0; String xpath = "//[@TEXT='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']"; - HWND handle = findHandleWithXpath(xpath, true); - if (handle != null)// first test without a timeout + WinPtr handle = findHandleWithXpath(xpath, true); + if (!handle.isEmpty())// first test without a timeout return true; while (attemptCount < totalAttempts) { handle = findHandleWithXpath(xpath, true); - if (handle != null) + if (!handle.isEmpty()) break; try {Thread.sleep((long)(CommandProcessor.XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();} ++attemptCount; @@ -97,12 +96,12 @@ public class MainCommands extends BaseCommand { long totalAttempts = (long) (CommandProcessor.WAIT_TIMEOUT_THRESHOLD / (CommandProcessor.XML_UPDATE_THRESHOLD * 1000)); long attemptCount = 0; String xpath = "//win[@CLASS='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']"; - HWND handle = findHandleWithXpath(xpath, true); - if (handle != null)// first test without a timeout + WinPtr handle = findHandleWithXpath(xpath, true); + if (!handle.isEmpty())// first test without a timeout return true; while (attemptCount < totalAttempts) { handle = findHandleWithXpath(xpath, true); - if (handle != null) + if (!handle.isEmpty()) break; try {Thread.sleep((long)(CommandProcessor.XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();} ++attemptCount; @@ -117,12 +116,12 @@ public class MainCommands extends BaseCommand { return false; long totalAttempts = (long) (CommandProcessor.WAIT_TIMEOUT_THRESHOLD / (CommandProcessor.XML_UPDATE_THRESHOLD * 1000)); long attemptCount = 0; - HWND handle = findHandleWithXpath(args[0], true); - if (handle != null)// first test without a timeout + WinPtr handle = findHandleWithXpath(args[0], true); + if (!handle.isEmpty())// first test without a timeout return true; while (attemptCount < totalAttempts) { handle = findHandleWithXpath(args[0], true); - if (handle != null) + if (!handle.isEmpty()) break; try {Thread.sleep((long)(CommandProcessor.XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();} ++attemptCount; diff --git a/src/org/synthuse/commands/MouseCommands.java b/src/org/synthuse/commands/MouseCommands.java index 231f80f..c4f8ce7 100644 --- a/src/org/synthuse/commands/MouseCommands.java +++ b/src/org/synthuse/commands/MouseCommands.java @@ -4,8 +4,6 @@ import java.awt.Point; import org.synthuse.*; -import com.sun.jna.platform.win32.WinDef.HWND; - public class MouseCommands extends BaseCommand { public MouseCommands(CommandProcessor commandProcessor) { @@ -15,10 +13,12 @@ public class MouseCommands extends BaseCommand { public boolean cmdClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + //System.out.println("cmdClick1: " + args[0]); + if (handle.isEmpty()) return false; - Point p = api.getWindowPosition(handle); + Point p = getCenterWindowPosition(handle); + //System.out.println("cmdClick3: " + p.x + "," + p.y); RobotMacro.mouseMove(p.x + parentProcessor.targetOffset.x, p.y + parentProcessor.targetOffset.y); RobotMacro.leftClickMouse(); return true; @@ -27,10 +27,10 @@ public class MouseCommands extends BaseCommand { public boolean cmdDoubleClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - Point p = api.getWindowPosition(handle); + Point p = getCenterWindowPosition(handle); RobotMacro.mouseMove(p.x + parentProcessor.targetOffset.x, p.y + parentProcessor.targetOffset.y); RobotMacro.doubleClickMouse(); return true; @@ -39,10 +39,10 @@ public class MouseCommands extends BaseCommand { public boolean cmdRightClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - Point p = api.getWindowPosition(handle); + Point p = getCenterWindowPosition(handle); RobotMacro.mouseMove(p.x + parentProcessor.targetOffset.x, p.y + parentProcessor.targetOffset.y); RobotMacro.rightClickMouse(); return true; @@ -71,10 +71,10 @@ public class MouseCommands extends BaseCommand { public boolean cmdMouseMove(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - Point p = api.getWindowPosition(handle); + Point p = getCenterWindowPosition(handle); RobotMacro.mouseMove(p.x + parentProcessor.targetOffset.x, p.y + parentProcessor.targetOffset.y); //System.out.println("point " + p.x + "," + p.y); return true; @@ -101,30 +101,30 @@ public class MouseCommands extends BaseCommand { public boolean cmdWinClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.sendClick(handle); + api.sendClick(handle.hWnd); return true; } public boolean cmdWinDoubleClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.sendDoubleClick(handle); + api.sendDoubleClick(handle.hWnd); return true; } public boolean cmdWinRightClick(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.sendRightClick(handle); + api.sendRightClick(handle.hWnd); return true; } } diff --git a/src/org/synthuse/commands/WindowsCommands.java b/src/org/synthuse/commands/WindowsCommands.java index 6526bf1..5be8327 100644 --- a/src/org/synthuse/commands/WindowsCommands.java +++ b/src/org/synthuse/commands/WindowsCommands.java @@ -2,8 +2,6 @@ package org.synthuse.commands; import org.synthuse.*; -import com.sun.jna.platform.win32.WinDef.HWND; - public class WindowsCommands extends BaseCommand { public WindowsCommands(CommandProcessor cp) { @@ -13,10 +11,10 @@ public class WindowsCommands extends BaseCommand { public boolean cmdWindowFocus(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.activateWindow(handle); + api.activateWindow(handle.hWnd); //api.showWindow(handle); return true; } @@ -24,60 +22,60 @@ public class WindowsCommands extends BaseCommand { public boolean cmdWindowMinimize(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.minimizeWindow(handle); + api.minimizeWindow(handle.hWnd); return true; } public boolean cmdWindowMaximize(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.maximizeWindow(handle); + api.maximizeWindow(handle.hWnd); return true; } public boolean cmdWindowRestore(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.restoreWindow(handle); + api.restoreWindow(handle.hWnd); return true; } public boolean cmdWindowHide(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.hideWindow(handle); + api.hideWindow(handle.hWnd); return true; } public boolean cmdWindowShow(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.showWindow(handle); + api.showWindow(handle.hWnd); return true; } public boolean cmdWindowSwitchToThis(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.switchToThisWindow(handle, true); + api.switchToThisWindow(handle.hWnd, true); return true; } @@ -85,29 +83,29 @@ public class WindowsCommands extends BaseCommand { public boolean cmdWindowClose(String[] args) { if (!checkArgumentLength(args, 1)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.closeWindow(handle); + api.closeWindow(handle.hWnd); return true; } public boolean cmdSetText(String[] args) { if (!checkArgumentLength(args, 2)) return false; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return false; - api.sendWmSetText(handle, args[1]); + api.sendWmSetText(handle.hWnd, args[1]); return true; } public String cmdGetText(String[] args) { if (!checkArgumentLength(args, 1)) return ""; - HWND handle = findHandleWithXpath(args[0]); - if (handle == null) + WinPtr handle = findHandleWithXpath(args[0]); + if (handle.isEmpty()) return ""; - return api.sendWmGetText(handle); + return api.sendWmGetText(handle.hWnd); } } diff --git a/src/org/synthuse/test/WpfBridgeTest.java b/src/org/synthuse/test/WpfBridgeTest.java new file mode 100644 index 0000000..317da61 --- /dev/null +++ b/src/org/synthuse/test/WpfBridgeTest.java @@ -0,0 +1,59 @@ +package org.synthuse.test; +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.synthuse.*; + +public class WpfBridgeTest { + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + + } + + @Test + public void countChildren() { + WpfBridge wb = new WpfBridge(); + wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF + int win32Cnt = wb.countChildrenWindows(); + assertTrue(win32Cnt > 0); + System.out.println("win32 countChildrenWindows: " + win32Cnt); + wb.setFrameworkId("WPF");// maybe not WPF + //System.out.println("wpf countChildrenWindows: " + wb.countChildrenWindows()); + } + + @Test + public void getRuntimeFromHandle() { + long handle = 1639790; + //String rid = wb.getRuntimeIdFromHandle(handle); + //System.out.println("getRuntimeIdFromHandle: " + rid); + handle = 984416; + //rid = wb.getRuntimeIdFromHandle(handle); + //System.out.println("getRuntimeIdFromHandle: " + rid); + } + + @Test + public void enumerateWindowInfo() { + WpfBridge wb = new WpfBridge(); + /* + EnumerateWindowsWithWpfBridge: 1639790 + getRuntimeIdFromHandle + runtimeId=42-1639790 + enumDescendantWindowIds 18 + EnumerateWindowsWithWpfBridge: 984416 + getRuntimeIdFromHandle + runtimeId=42-984416 + */ + //int count = wb.countDescendantWindows("42-984416"); + String[] wInfo = wb.enumDescendantWindowInfo("42-984416", WindowInfo.WPF_PROPERTY_LIST); + if (wInfo != null) + System.out.println("enumDescendantWindowInfo: " + wInfo.length); + } +}