| 924 | |
| 925 | |
| 926 | BOOL CALLBACK EnumControlFind(HWND aWnd, LPARAM lParam) |
| 927 | { |
| 928 | WindowSearch &ws = *(WindowSearch *)lParam; // For performance and convenience. |
| 929 | if (*ws.mCriterionClass) // Caller told us to search by class name and number. |
| 930 | { |
| 931 | int length = GetClassName(aWnd, ws.mCandidateTitle, WINDOW_CLASS_SIZE); // Restrict the length to a small fraction of the buffer's size (also serves to leave room to append the sequence number). |
| 932 | // Below: i.e. this control's title (e.g. List) in contained entirely |
| 933 | // within the leading part of the user specified title (e.g. ListBox). |
| 934 | // Even though this is incorrect, the appending of the sequence number |
| 935 | // in the second comparison will weed out any false matches. |
| 936 | // Note: since some controls end in a number (e.g. SysListView32), |
| 937 | // it would not be easy to parse out the user's sequence number to |
| 938 | // simplify/accelerate the search here. So instead, use a method |
| 939 | // more certain to work even though it's a little ugly. It's also |
| 940 | // necessary to do this in a way functionally identical to the below |
| 941 | // so that Window Spy's sequence numbers match the ones generated here: |
| 942 | // Concerning strnicmp(), see lstrcmpi note below for why a locale-insensitive match isn't done instead. |
| 943 | if (length && !_tcsnicmp(ws.mCriterionClass, ws.mCandidateTitle, length)) // Preliminary match of base class name. |
| 944 | { |
| 945 | // mAlreadyVisitedCount was initialized to zero by WindowSearch's constructor. It is used |
| 946 | // to accumulate how many quasi-matches on this class have been found so far. Also, |
| 947 | // comparing ws.mAlreadyVisitedCount to atoi(ws.mCriterionClass + length) would not be |
| 948 | // the same as the below examples such as the following: |
| 949 | // Say the ClassNN being searched for is List01 (where List0 is the class name and 1 |
| 950 | // is the sequence number). If a class called "List" exists in the parent window, it |
| 951 | // would be found above as a preliminary match. The below would copy "1" into the buffer, |
| 952 | // which is correctly deemed not to match "01". By contrast, the atoi() method would give |
| 953 | // the wrong result because the two numbers are numerically equal. |
| 954 | _itot(++ws.mAlreadyVisitedCount, ws.mCandidateTitle, 10); // Overwrite the buffer to contain only the count. |
| 955 | // lstrcmpi() is not used: 1) avoids breaking existing scripts; 2) provides consistent behavior |
| 956 | // across multiple locales: |
| 957 | if (!_tcsicmp(ws.mCandidateTitle, ws.mCriterionClass + length)) // The counts match too, so it's a full match. |
| 958 | { |
| 959 | ws.mFoundChild = aWnd; // Save this in here for return to the caller. |
| 960 | return FALSE; // stop the enumeration. |
| 961 | } |
| 962 | } |
| 963 | } |
| 964 | else // Caller told us to search by the text of the control (e.g. the text printed on a button) |
| 965 | { |
| 966 | // Use GetWindowText() rather than GetWindowTextTimeout() because we don't want to find |
| 967 | // the name accidentally in the vast amount of text present in some edit controls (e.g. |
| 968 | // if the script's source code is open for editing in notepad, GetWindowText() would |
| 969 | // likely find an unwanted match for just about anything). In addition, |
| 970 | // GetWindowText() is much faster. Update: Yes, it seems better not to use |
| 971 | // GetWindowTextByTitleMatchMode() in this case, since control names tend to be so |
| 972 | // short (i.e. they would otherwise be very likely to be undesirably found in any large |
| 973 | // edit controls the target window happens to own). Update: Changed from strstr() |
| 974 | // to strncmp() for greater selectivity. Even with this degree of selectivity, it's |
| 975 | // still possible to have ambiguous situations where a control can't be found due |
| 976 | // to its title being entirely contained within that of another (e.g. a button |
| 977 | // with title "Connect" would be found in the title of a button "Connect All"). |
| 978 | // The only way to address that would be to insist on an entire title match, but |
| 979 | // that might be tedious if the title of the control is very long. As alleviation, |
| 980 | // the class name + seq. number method above can often be used instead in cases |
| 981 | // of such ambiguity. Update: Using IsTextMatch() now so that user-specified |
| 982 | // TitleMatchMode will be in effect for this also. Also, it's case sensitivity |
| 983 | // helps increase selectivity, which is helpful due to how common short or ambiguous |
nothing calls this directly
no test coverage detected