FB II Compiler

PG PRO

Debugging

Memory

System

Mathematics

Resources

Disk I/O

Windows

Controls

Menus

Mouse

Keyboard

Text

Fonts

Drawing

Sound

Clipboard

Printing

Communication

ASM

Made with FB

WINDOWS

Get the window list of launched applications


Okay you guys, go crazy...

1. I'd like to be able to get the window list for the current frontmost application from a code resource, and even better, for any application that is running (I think the Toolbox keeps a separate window list for each application). I have a little bit of familiarity with the Process Manager, but a check through Think Reference does not show any PM records that have a pointer to a window list. There was an old FB example that cycled through a window list, but darned if I can find it now.

2. I'd like to be able to register with the Gestalt function as described in "Ultimate Mac Programming", p. 266, by Dave Mark. To do this, one needs to do two things: set up a response function that has a certain structure, then call NewGestalt. Calling NewGestalt will be the easy part; it's defining the function that I'm not sure about. The structure of the function is this:
static pascal OSErr BeeperGestalt(OSType selector, long*_response)
I've done this before in assembler, but am not sure of the FB syntax. As far as I can tell, one would do this:
ENTERPROC%(selector%,result&)
do stuff
EXITPROC%=result&
Trouble is, I don't know what to do with the OsErr% value. Even worse, this wil take place in an INIT, so I'd like to cloak this in a local function to prevent it from being automatically executed. Is this possible? Feel free to respond with inline assembler.

Any help will save a few evenings of multiple restarts and system errors.

wave


This is a very hard problem. The window list is a linked list of window records. The address of the first record (the head of the list) is stored in the low memory global WindowList which lives at &09D6. This is the value FrontWindow returns (unless FrontWindow has been patched).

The problem exists because the Process Manager does an excellent job at switching between applications. It maintains a list of low-memory-global blocks, one for each application. Whenever you switch between applications, it copies the low memory globals in for that application.

So the only way to directly track down another application's globals is to disassemble the process manager and figure out how to look through its table of blocks - not something I would suggest attempting!

However, you can also find the value you want indirectly, by waiting for it with a jGNEFilter.

The jGNEFilter is one of the low memory globals that does *not* get swapped out between applications. Although it is commonly used to install event filters from extensions, there's no reason you can't patch it from an application.

The scheme runs like this: each trip through the event loop, your function calls GetCurrentProcess and GetFrontProcess, then compares them with SameProcess. If the values match, the function can grab the WindowList global. Since the front process is current, its window list will be active.

I did something much like this for an application several years ago, but it was the menu bar I was concerned with rather than the window list. The principle will still work, but there are some things to watch out for.

1) If your app crashes, it will take the whole system down with it, since you will have no time to unload the jGNEFilter.

2) If you install a jGNEFilter, then someone else installs one after you, uninstalling your filter will effectively cut off the rest.

The solution? Don't actually install your routine as the jGNEFilter. Instead, write a short machine languge function that does this:

push savedAppProcessNumber
_GetNextProcess
test result
if no error
load savedFunctionAddress
jsr(a0)
end if
load savedNextJNEAddress
jmp(A0)

To install this, create a new block in the system heap with NewSysPtr. Blockmove this function into that block. Poke in the values for the process serial number, the function address, and the old jGNEFilter value at the appropriate points in the code. Install the block as the jGNEFilter. Forget about it.

Every time it's called, this function will check to see if your application is still alive. If so, it will call the function address given. If not, it won't call. So if your app crashes, it doesn't bring down the system, and it never has to unload the stub function so the jGNE chain is not damaged.

Conclusion: It's possible, but not easy. You'll have to be fairly familiar with assembly language and willing to do a lot of MacsBug stepping.

<< 2. I'd like to be able to register with the Gestalt function as described in "Ultimate Mac Programming", p. 266, by Dave Mark.
[snip]
As far as I can tell, one would do this:
ENTERPROC%(selector%,result&)
do stuff
EXITPROC%=result&
Trouble is, I don't know what to do with the OsErr% value. Even worse, this wil take place in an INIT, so I'd like to cloak this in a local function to prevent it from being automatically executed. Is this possible? Feel free to respond with inline assembler. >>

The osErr% value is what you return as the ExitProc% value. It is the function's return value.

The result& is what is called a "reference parameter". The system doesn't give you the value - it gives you the _location_ of the value. So your result& variable in the example will contain the address of the actual result& variable.

To return a value, poke a long into the variable pointed at by result&. To return an error, pass it to EXITPROC%.

To keep an enterproc from being automatically executed, skip around it with GOTO:

GOTO "endofenterproc"
ENTERPROC%(selector, resultPtr&)
...
& resultPtr&, result&
EXITPROC%=error%
"endofenterproc"


This is basically all FB does to keep a local function from executing, anyway.

Mars


<< 1. I'd like to be able to get the window list for the current frontmost application from a code resource, and even better, for any application that is running (I think the Toolbox keeps a separate window list for each application). I have a little bit of familiarity with the Process Manager, but a check through Think Reference does not show any PM records that have a pointer to a window list. There was an old FB example that cycled through a window list, but darned if I can find it now. >>

I think what you are remembering is an example showing how to look at FB's own window list.

I would think you would need to access the system global "WindowList" at $09D6. The problem is, this global is maintained automatically by the Process Manager. I believe it always contains the window list of the current application (which doesn't neccessarily have to be the front-most application).

I haven't had a chance to look at them yet, but you may be able to find what you are looking for in the sparsely-documented Layer Manager. I can't seem to lay my hands on any reference to it at the moment; but I'm sure someone on this list would be able to point you in the right direction. It may be just what you are looking for.

<< 2. I'd like to be able to register with the Gestalt function as described in "Ultimate Mac Programming", p. 266, by Dave Mark. To do this, one needs to do two things: set up a response function that has a certain structure, then call NewGestalt. Calling NewGestalt will be the easy part; it's defining the function that I'm not sure about. The structure of the function is this:
static pascal OSErr BeeperGestalt(OSType selector, long*_response)
I've done this before in assembler, but am not sure of the FB syntax. As far as I can tell, one would do this:
ENTERPROC%(selector%,result&)
do stuff
EXITPROC%=result&
Trouble is, I don't know what to do with the OsErr% value. Even worse, this wil take place in an INIT, so I'd like to cloak this in a local function to prevent it from being automatically executed. Is this possible? Feel free to respond with inline assembler.
Any help will save a few evenings of multiple restarts and system errors. >>

static pascal OSErr BeeperGestalt(OSType selector, long*_response)

"OSType selector" says that selector is a value of OSType, which is a 4-character string, or basically, a long integer (ie. _"buba")

"long*_response" is saying that response is a long integer, and that it is a var (meaning that it can be modified by your function - so a pointer to the longint will be passed to you, rather than the longint itself. So to access/change it within the function, you can use something like this:

responsePtr&.nil& = x

"static pascal OSErr" just says that this is a Pascal-style function that returns a value of type OSErr - which happens to be a short integer.

So you end up with something like this:

ENTERPROC% (selector&,@responsePtr&)
responsePtr&.nil& = whatever
EXITPROC% = err

Hopefully I've gotten this right...

David Blache


<< 1. I'd like to be able to get the window list for the current frontmost application from a code resource, and even better, for any application that is running (I think the Toolbox keeps a separate window list for each application). >>

Mars said:
(Too many fantastic things to relate. Is he wonderful, or what!!)

I'm not sure exactly what your appl is after, but I'm working on something that sounds kinda similar and maybe something here will help.

To get a list of windows for the current appl:

On entry/start-up:

A.) ON TIMER(1) = a Process Manager routine to poll for process changes -- for the sake of argument we'll call it FN getCurrentProcess.

B.) Install a jGNEFilter so that calls to FN FRONTWINDOW will be vaild.

From FN getCurrentProcess, you'll be able to keep track of appls as they're switched in and out. If you find a process that you're interested in then you can set a global boolean value to _true which triggers your event handler from the jGNEFilter to start looking at windows. And you'll look at windows with wPtr&=FN FRONTWINDOW. The pointer returned contains the window information plus a pointer to the next window (in the offset wPtr&.nextwindow.) So it's easy to get all the windows by setting up a little loop until wPtr&.nextwindow=0.

Maybe Mars or Staz can correct me if I'm wrong, but I *believe* that FN FRONTWINDOW only points to windows for the currently running process. My code is pretty much only concerned with that front window so I've never really tracked the chain of nextwindows. But even if it points to all the open windows then you can easily weed out the ones that you have already tracked.

As Mars pointed out, use of the jGENFilter can be unfriendly -- and I'm going to take a look at his alternative. But, what I've done thus far works great (it's placed in the start up folder and has no user exits).
If you would like some sample code, I'll tidy-up some of what I've got.

DL