This is fine:
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSLog(@"%@", (__bridge NSArray *)windowList);
This causes EXC_BAD_ACCESS:
CFArrayRef windowIDList = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSLog(@"Array %@", (__bridge NSArray*) windowIDList);
Arrays created as NSArray
s can only contain elements that act like Objective-C objects.
Arrays created as CFArray
s can contain anything, if you pass the appropriate CFArrayCallBacks
to CFArrayCreate
.
CGWindowListCreate
is creating a CFArray
, and filling it with things that don't act like objects, but CGWindowListCreate
is using CFArrayCallbacks
that don't care about that.
When you try to print this CFArray
with the %@
format specifier, NSLog
sends an Objective-C -description
message to the array. The CFArray
handles this by sending an Objective-C description
message to each of its elements. Unfortunately, its elements are not objects and so it's impossible to sending Objective-C messages to them. Thus the crash.
Try this instead:
CFStringRef description = CFCopyDescription(windowIDList);
NSLog(@"Array %@", description);
CFRelease(description);
The CFCopyDescription
function uses one of the CFArrayCallbacks
functions on each element of the array, instead of trying to send an Objective-C message to each one. The callback knows how to handle the array element, so it works fine. I get this output in my test program:
2011-11-10 18:50:23.888 test[15156:707] <CFArray 0x1001140c0 [0x7fff7fd24ea0]>{type = mutable-small, count = 19, values = (
0 : <0x7d7>
1 : <0x2d>
2 : <0x20>
3 : <0x21>
4 : <0x1e>
5 : <0x9>
6 : <0x7a8>
7 : <0x2c>
8 : <0x2e>
9 : <0x743>
10 : <0x32>
11 : <0x85>
12 : <0x695>
13 : <0x62a>
14 : <0x62b>
15 : <0xa>
16 : <0x26>
17 : <0x18>
18 : <0x2>
)}
From the documentation on NSArray
:
NSArray is “toll-free bridged” with its Core Foundation counterpart, CFArray Reference. What this means is that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object, providing you cast one type to the other. Therefore, in an API where you see an NSArray * parameter, you can pass in a CFArrayRef, and in an API where you see a CFArrayRef parameter, you can pass in an NSArray instance. This arrangement also applies to your concrete subclasses of NSArray.
So the problem must be with the two methods being called. Again, from the documentation, CGWindowListCopyWindowInfo
has return value:
An array of CFDictionaryRef types, each of which contains information about one of the windows in the current user session. If there are no windows matching the desired criteria, the function returns an empty array. If you call this function from outside of a GUI security session or when no window server is running, this function returns NULL.
and CGWindowListCreate
has return value:
An array of CGWindowID values corresponding to the desired windows. If there are no windows matching the desired criteria, the function returns an empty array. If you call this function from outside of a GUI security session or when no window server is running, this function returns NULL.
When you call NSLog(@"%@",array);
, the message description
is sent to each object in the array. Floats, BOOLs and ints do not respond to this message. For example, you will get an error for
NSLog(@"Printing 2: %@",2);
but the error goes away if you use the int
call:
NSLog(@"Printing 2: %d",2);
For your case, CGWindowListCreate
is returning an array of CGWindowID
values, and these are 32 bit unsigned integers. Therefore they do no respond to %@
, but will respond to %u
. Hence the fix is to print the array manually using %u
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With