Pregunta

Edit: To future googlers abarnert's answer works giving a NSHIObject (from HIToolbox, HI standing for human interface), I can find little more documentation on this, the only mention on apple.com (there is ZERO, ZIP, NADA documentation on a class called NSHI object as of this point https://www.google.com/#q=%22NSHIObject%22+site%3Aapple.com&filter=0 ) is In object c if I have the following http://lists.apple.com/archives/webkitsdk-dev/2010/Sep/msg00007.html which doesn't work for this object the NSWindow isn't created. Yay for apples api documentation.

NSWindow.initWithWindowRef_(nshi_object)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: Expecting instance of NSWindow as self, got one of NSHIObject

Original Question about loading the pointer given from wxPython


C Code

// our data
int a = 42;
// create a pointer to that data
int *p = &a;
// p now contains the address (say 1776) and that address has the integer 42
// get the data back
int ret_a = *p;

// now if we already have an address
int* manual_pointer = (int*) 1776; 
int man_a = *manual_pointer;

printf("%d,%d\n", ret_a, man_a);

How do I do this syntax in pyobjc? I see it mentions here

More complex types can be represented using longer type strings:

    a pointer to some type is _C_PTR followed by the type string of the pointed-to type.

So what do I do? This (if the object type is NSInt)?

arg = _C_PTR + "NSInt" + "1776"

Objective C Code (Added later) Please note you have to somehow inject this into the python process other wise it won't work

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        char line[2048];
        // this will store are address
        long adr;
        printf("According to \n- http://forums.wxwidgets.org/viewtopic.php?f=23&t=31778\n- http://forums.wxwidgets.org/viewtopic.php?f=23&t=18645\n");
        printf("This will prompt you for a pointer address; please paste in the long you received from GetHandle()\n");
        printf("Please enter pointer address: ");
        fgets(line, sizeof(line), stdin);
        sscanf(line, "%ld", &adr);
        printf("Using address: %ld\n", adr);

        printf("Grabbing view...");

        // our NSView    
        NSView *view =  (__bridge NSView *)((void *)adr);

        NSRect f = view.frame;
        int w = f.size.width;
        int h = f.size.height;
        printf("Size(%d, %d)", w, h);

    }
    return 0;
}
¿Fue útil?

Solución

From your comment on bbum's answer:

The problem is I am getting a pointer address from a C++ library (wxPython) that points to a NSView object I want to get.

For that, just use the objc_object constructor:

view = objc.objc_object(c_void_p=ptr)

Of course I don't think wxPython gives you ctypes.c_void_p values for whatever function you're using to get the pointer. I don't know what it does give you, but the most likely possibility is an int, in which case you can just do this:

view = objc.objc_object(c_void_p=ctypes.c_void_p(ptr))

You didn't mention which version of PyObjC you're using. If it's before 2.5 (you can check objc.version), you either have to manually build a PyCapsule object, or do this from the other side of the bridge. I don't have an old copy to test with, or the old docs, so this is from memory, but it looked something like this:

view = NSView.objectWithSomethingOrOther_(ptr)

Meanwhile, from your original question:

arg = _C_PTR + "NSInt" + "1776"

I'm not sure why you're trying to build a type string in the first place, but…

The type string for a pointer to some type X is _C_PTR followed by the type string for X, not the name of X. Also, there is no type NSInt. If you meant a class type like NSNumber, you can just use _C_ID (just as you can use id instead of NSNumber * in native ObjC). If you meant an integral type NSInteger, that's _C_PTR + _C_NSInteger. If you meant an integral type that doesn't have a special name, you have to find out which type it's a typedef for and use that—for example, _C_PTR + _C_LNG will work for NSInteger * on 64-bit Macs.

Meanwhile, a type string names a type, not a value, so if that 1776 is supposed to be the pointed-to location, or the value at that location, or really anything else I can think of, that doesn't even make sense.


Finally, I have no idea what your C code (there's no ObjC in it anywhere) is supposed to be demonstrating, but I'm willing to bet that if you actually run it, it will segfault.


Meanwhile, from your comment and your code, what you're trying to do doesn't actually make any sense, and can't possibly work. The fact that you're now doing it successfully is why you're crashing.

Each process has its own virtual address space. When you take a pointer from one process's address space, and use it in another process, it isn't pointing at the same objects; it's pointing at either completely different objects, or nothing at all. That's why you get that segfault: 0x000000007a6e5c90 points to an NSView in your wxPython program's address space, but the same address points to unmapped memory in your interactive interpreter's address space.

Also, while there are APIs to map memory regions from other processes, or to directly read and write other processes' address space, that wouldn't help you here, because if there are any pointers—including opaque internal pointers—within the object you're pointing at, you have to explicitly map those as well. And, in the case of ObjC objects, even that won't work, because ObjC depends on a (process-)global object workspace. (In theory, you could write C code that manipulates the other process's ObjC workspace directly, instead of using ObjC, but… you don't want to.)

If you want to access an NSView from one process in a different process, there are two ways to do it. Either you have to use high-level cross-process APIs to manipulate the window, or you have to inject code into the other process and trigger it (as, for example, F-Script Anywhere does)—or, as a minor variation of the second, attach to the other process as a debugger.

But if you're writing both programs yourself, there's a much cleaner way to do it. Write your GUI app to be externally controllable in some way. One way to do on the Mac is with an AppleScript interface. Use CocoaScripting to export an API that allows other apps to do things like move your windows around. Then use appscript or ScriptingBridge, or use a dual-language approach via NSAppleScript, to use that API, instead of trying to talk directly to your NSView objects. (You can actually export the whole NSView interface through CocoaScripting if you really want to, but that's probably not the best answer.)

Otros consejos

Python doesn't deal with pointers at all; in fact, it actively discourages their use in all ways. You can use the id() function to retrieve the address of a Python object, but there isn't a corresponding function to go the other way (that isn't a joke).

Now, given that you have a pointer to something from C, you should be able to describe that something with the ctypes module and then treat the address as a pointer and decode at will. But it is tricky.

Not the most concrete of examples, but the Venn diagram of people that know Python, Objective-C, and are familiar with ctypes is a rather small group (which is one big reason why you are generally better off sticking to native ObjC for iOS/Cocoa).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top