Getting a unique ID for a window of another application - cocoa

I'm a newbie Cocoa developer and I'm developing my first application. I want to read a unique identifier from any window of any application - whether it's Cocoa or Carbon. Cocoa applications make their window IDs available to AppleScript (although I'm sure there's a much better way to do this through a proper Objective C route), but I'm trying to access window IDs from documents in the Adobe apps. This seems to be a lot trickier. All I can seem to find in the reference libraries is HIWindowGetCGWindowID:
"This function returns the window ID assigned by the window server when a window is created. The window ID is not generally useful with any other Carbon function, but may be used with other Mac OS X functions that require a window ID, such as functions in OpenGL."
Can this be used to get the ID from my program? Or is it just a function that can be used within one application?
If someone could point me in the right direction, I'd be eternally grateful.

The function HIWindowGetCGWindowID() can only return a CGWindowID for one of your app's windows, since a WindowRef from another program won't be valid in yours.
The function CGWindowListCopyWindowInfo() from CGWindow.h will return an array of dictionaries, one for each window that matches the criteria you set, including ones in other applications. It only lets you filter by windows above a given window, windows below a given window and "onscreen" windows, but the dictionary returned includes a process ID for the owning app which you can use to match up window to app. In each returned dictionary the kCGWindowNumber key will point to the window ID as a CFNumber. There is also a CGWindowListCreate() function that only returns an array of CGWindowIDs. There is basically no documentation for these functions beyond the CGWindow.h header and the Son of Grab sample code. Also, it's 10.5 only.

Related

Create a program that alters the execution of a windows application

I have a windows application which has several sub-forms. i have to navigate through 5 or 6 forms to reach the form i need. this is time consuming since i have to open it several times through the day and i do it daily.
my need: i dont have the source project for this application, i got it as an executable program, but i need to create some application that does these steps for me automatically. In other words i need to find a way to automatically click the buttons that navigate through the forms and opens the form i need from step one.
is there any way i can do this ?
There is indeed, though generic solutions already exist to perform just this kind of function to arbitrary programs.
You can use Spy++ or a resource-editor, like ResHack or ResEdit to look at the program and get the control ids of the navigation buttons.
Once done, you can get a handle to the program itself and then send messages to it's WindowProcedure that would be generated if the user clicked the controls with a mouse,
Another alternative, is to get the position of the running target application, after you've got it's HWND, by using the GetWindowRect function. You could then use this position along with vert/horiz distances to generate mouse events.
The two have more-or-less the same result, though some applications won't work with approach #1.
In one instance, you need to use Spy++ to get the control IDs.
In the other instance, you need to use an image editor to get the pixel offsets of the controls.
In both instances, you'll need to use FindWindow, along with the window's title-text in order to get a HWND handle.
You could use a combination of the two - asking the program itself with GetDlgItem for the handle of the controls you need to click. You could then query the control for its position, before using mouse_event to position the mouse above it and again to click it.
Quite a few ways to skin this cat, actually.
Pre-existing solutions like AutoIt are said to be very easy to use and will be much easier than coding a new program for each target.

UI Automating a VB6 application from .NET

For the last few days i have been trying to figure out the best way to get AutomationElement for a specific control in a vb6 application.
My initial way of doing so was by doing a search with the following condition:
new PropertyCondition(AutomationElement.NameProperty, controlName)
I was under the assumption that this was working correctly for about a week in a little test VB6 application.
but i few days ago i realized something... when i dragged a vb6 textbox into the form, the 'Name' property and 'Text' property were both set to 'Text1'
So when i searched with:
new PropertyCondition(AutomationElement.NameProperty, 'Text1')
it return the correct element, but if i then went and set the 'Text' property to '' the same search would bring nothing back.
Question: Has anyone found a way to get a AutomationElement based on a the VB6 control name
What i have tried:
getting the MSAA equivalent interface and looking at the 'Name' property - Result: ''
http://msdn.microsoft.com/en-us/library/windows/desktop/dd318490%28v=vs.85%29.aspx
getting the control based on other properties (AutomationId, RuntimeId) - Result: AutomationId - not all controls seem to have this property available - RuntimeId - changes each time the app runs
I have looked at alot of different sites the main one is listed below - while some say they have manage to get it working - i don't believe i can see how they do it.. or i just dont understand it :$
http://blogs.msdn.com/b/brianmcm/archive/2006/01/17/getting-the-winforms-id-of-a-control.aspx
While i have access to the demo app, i will not access to the production app as that has been created by a third party.
What i plan on doing from here is to get the Automation element based on their position on the form..
Thank you
Can't comment due to low rep. Do you absolutely HAVE to have an AutomationElement?
You may want to look at invoking [user32.dll] (http://pinvoke.net/default.aspx/user32.EnumChildWindows). Look at FindWindowEx, GetWindow, EnumWindows, EnumChildWindows, GetWindowText, etc.
You need the handle of the parent window, so you can use this loop to get it. From there you can use the other functions to get the information you need about the control.
IntPtr hWnd = IntPtr.Zero;
foreach(var process in System.Diagnostics.Process.GetProcesses())
if(condition)
hWnd = process.Handle;
Comment with the exact information you need out of the VB6 window, and I'll give you better code.
You can use the relational position of the AutomationElement in a specific Window (or any other container for that matter), in order to detect it. For example, if you have 5 TextBox AutomationElements in your main window, and you're certain that the order will not be changing, you could create a PropertyCondition on the TextBox class name, and then use the FindAll method to return a collection of AutomationElements and iterate through it, querying the BoundingRectangle property to find out which is the lowest one (or middle, or any other position, for that matter).
I would create a helper method that would return a Dictionary<int,AutomationElement> with the key being the visual position of the AutomationElement, and the value being the AutomationElement itself.
This way you can avoid using a specific Point on your screen (any window size change or element positioning will easily break your code) while not being bound to the AutomationId property.

Cocoa CGWindowListCopyWindowInfo & AXUIElementSetAttributeValue

I'm using the following piece of code to get all windows:
CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
This gives me an array of dictionaries as found here: Front most window using CGWindowListCopyWindowInfo
Then I check their bounds to see if the mouse is in it, and the first I find is is the one my mouse is over.
Then I would like to be able to move it. I know how to use AXUIElementSetAttributeValue to move a window, though then I need a AXUIElementRef, which I can't figure out how to get out of the dictionary.
How can I solve this?
There is no way to go from a window number to an AXUIElementRef.
The closest you can get is to find the owner of the window, then ask it through Accessibility for its windows and look for one with the same title—but an app may have more than one window with the same title, so you need to figure out what you'll do in that case.
The only ways to move another application's windows are by AppleScript or by Accessibility. Using Accessibility is much more reliable than AppleScript (many, if not most, apps have incomplete and/or flaky scripting support). You can get the process ID and window title from the same window dictionary that gives the window number, but that's as specific as you can get. With AppleScript, you could only use the window's index within its application's window list, and hope that the list hasn't changed order in the split-second between you computing the index and trying to use it.

Access window by document filename

I don't do much programming in applescript but I have a personal app which is mostly python but generates simple applescripts and calls them via a system call. Applescript is so different from the languages I usually program in that I can't figure out how I...
get the window order of a document within an application?
For making calls like:
set bounds of **first** window to %s
in other words, how can I get the document's "window order" for an application?
Is it possible to interact with a window through accessing the document like this:
to get bounds of first window whose document is "%s"
(which doesn't work) or do I have to get the document's window order first and then interact with that window (via its order) in a second line?
Any insight would be great. Thanks.
You can do both of these things just fine. The first line is just set bounds of window 1 to ..., or, if you prefer, set bounds of the first window to ... The second one depends on what, exactly, you want to do. If you want to access the first window whose name is something in particular, you can just do get the bounds of window "NAME"; if you really want the name of the document, though, you'll need to do something like
set d to the document "NAME"
repeat with w in windows
if w's document is d then return bounds of w
end repeat
You should be able to do the first window whose document is d, but this fails; as far as I can tell, it's because document is also a type name. Also, if window "NAME"/document "NAME" fails—it's the sort of thing that I remember sometimes not working, even though it should—you can instead use the first window whose name is "NAME" (or the first document ...). But the simple form will almost certainly work.
Also, if you're just generating these AppleScripts, calling them, and deleting them—in other words, if you're pretending they're Python functions, rather than generating them for later use—I'd highly recommend using appscript instead,. I've never used in in Python, but I have in Ruby, and it's a really great way to deal with everything AppleScript does while still using your language of choice. For instance, I think your first example would become app('Whatever').windows[1].bounds.set((0,0,0,0)), (or ...windows.first.... if you prefer) and your second would become either app('Whatever').windows['NAME'].bounds.get() or app('Whatever').windows[its.document.name == 'NAME'].get(), depending on if you need the window's name or the window's document's name. This is all untested, but certainly captures the flavor of what appscript tends to look like (nice and concise).

Setting value of AXTextField programmatically (OS X Cocoa Accessibility API)

I'm using the Cocoa Accessibility API to try and modify the value of a text field (AXTextField) in another application, but I've run into a problem: my code correctly identifies and modifies the contents of the text field in question, and the text of the field visibly changes, but the changes aren't registered by the program I'm trying to control. Is there a way to do this in with the API without having to generate keyboard events?
Sample code:
AXUIElementCopyElementAtPosition(appRef,
clickPoint.x,
clickPoint.y,
&boxRef);
NSString *valueToSet = [NSString stringWithFormat:#"%f",amount];
AXUIElementSetAttributeValue(boxRef,kAXValueAttribute,valueToSet);
And the text field changes to the value specified in "amount" but the other program doesn't recognize the change - I have to go type the number in myself to get it to pick up the change (I can tell the difference, because the program responds when a new value is typed in the box). Can anyone point me in the right direction?
For posterity: Informed sources tell me that this is actually a bug in the application I'm trying to control. You can tell the difference by using UI Browser (http://prefabsoftware.com/uibrowser/) to try and set the value of the textfield; if UI Browser can't make the change stick, then the matter is out of your control.
Try telling the text field:
perform action "AXConfirm"
This is Applescript, but whatever the Cocoa equivalent is, it may make the change stick even if UI Browser can not (I've used it before).

Resources