[Openmcl-devel] Handle/Pointer-to-Pointer FFI Question

Gary Byers gb at clozure.com
Fri Dec 29 01:59:08 UTC 2006

A handle (in the Classic MacOS memory manager) is basically a pointer
to a pointer.  If H is a handle, then:

(rlet ((p (%get-ptr h)))
   ;; "p" probably points to something interesting ...

It wasn't always that simple: the address obtained by indirecting
through a handle could change over time (though the "master pointer" -
the handle itself - remained at a constant address).  It was possible
to lock and unlock a handle (via #_HLock/#_HUnLock; I hope that I'm
capitalizing those names correctly) to keep things from moving while
you were accessing.

Some handles denoted the in-core contents of "resources", which
were loaded from files.  When memory got really tight, resource-based
handles could be "purged" from memory, so dereferencing such a handle
without first un-purging it (I forget how ...) yielded a null pointer.

Having purgeable/relocatable blocks of memory helped early Mac
applications to run in small address spaces, but didn't scale
particularly well.  The only reasons that I can think of for
handles to continue to exist involve backward-compatibility; #_HLock
and #_HUnLock are no-ops on OSX, and a "handle" is just something
that involves an extra level of indirection for no obvious reason.

If a function returns a handle by reference, that means that the
caller reserves space for a handle somewhere and passes a pointer
to that space to that function.  RLET ((<var> <type>)) reserves some
space for (generally uninitialized) instance of <type>, and binds
<var> to a pointer to that reserved space.  So:

(rlet (....
        (&ImageDescriptor :<I>mage<D>escription))

allocates an (uninitialized) ImageDescription handle and binds
&ImageDescriptor to a pointer to that 4 or 8 bytes of garbage.

(If we were to do (PREF &ImageDescriptor :<I>mage<D>escription)
at this point, we'd get some random bits instead of a real handle;
if we'd said RLETZ instead of RLET, those bits would be 0.)

If we then do :

     (let ((err (#_GraphicsImportGetImageDescription &gi &ImageDescriptor)))
         (unless (= err #$noErr)
             (error "GraphicsImportGetImageDescription ~a" err)))

and don't get an error, we'd be able to say:

   (let* ((descriptor-handle (PREF &ImageDescriptor :<I>mage<D>escription)))

and the value accessed by PREF should be a "real" handle.

     (let* ((descriptor-pointer (%get-ptr descriptor-handle)))

If we cared about Classic MacOS compatibility, we would have locked
the handle before dereferencing it.

  (let ((Width (pref descriptor-pointer :<I>mage<D>escription.width))
        (Height (pref descriptor-pointer :<I>mage<D>escription.height))
        (Depth (pref descriptor-pointer :<I>mage<D>escription.depth))

should give meaningful values.

The documentation for #_GraphicsImportGetImageDescription should
probably tell you whether or not it's necessary to dispose of the
handle when you're done with it.  Disposing of the handle will
make the memory that it (indirecly) pointed to free as well as
freeing the handle itself.

On Thu, 28 Dec 2006, Brent Fulgham wrote:

> I'm attempting to call a QuickTime API function, which expects to be passed a
> First call is to a function to get the graphics importer.
> OSErr GetGraphicsImporterForFile (
>   const FSSpec         *theFile,
>   ComponentInstance    *gi );
> This call is done in Lisp as:
> (rlet ((&gi :<G>raphics<I>mport<C>omponent)
>        (Fsspec :<FSS>pec)
>        (Fsref    :<FSR>ef)
>        (&ImageDescriptor :<I>mage<D>escription)
> ... Get a FSRef from a CURL-based path (per Gary's earlier instructions)....
>      (let ((err (#_GetGraphicsImporterForFile Fsspec &gi)))
>         (unless (= err #$noErr)
>                 (error "GetGraphicsImporterForFile ~a" err)))
> This works great, returns no error, and produces a &gi value that can be used further in the routine.
> Later (with the enclosing rlet above) I wish to get the graphics description of my image file, using the API call:
> ComponentResult GraphicsImportGetImageDescription (
>   GraphicsImportComponent    ci,
>   ImageDescriptionHandle     *desc );
> In this case, the component instance is supposed to be the actual GraphicsImportComponent object
> (whereas the earlier call used a pointer to a <C>omponent<I>nstance).  The FFI seems to handle this
> properly, or at least returns a <G>raphics<I>mport<C>omponent object that I can pass to the above
> function without API complaints.
> However, I wish to get an <I>mage<D>escription from the ImageDescriptionHandle*, which is effectively
> a "ImageDescriptionHandle**".
> This looks like the following (still inside the above rlet, so the stuff is in scope):
>    (let ((err (#_GraphicsImportGetImageDescription &gi &ImageDescriptor)))
>        (unless (= err #$noErr)
>            (error "GraphicsImportGetImageDescription ~a" err)))
> This seems to work, but produces incorrect results.  If I attempt to extract values from the ImageDescriptor:
> (let ((Width (rref &ImageDescriptor :<I>mage<D>escription.width))
>       (Height (rref &ImageDescriptor :<I>mage<D>escription.height))
>       (Depth (rref &ImageDescriptor :<I>mage<D>escription.depth))
> ...
> I get very strange values.
> I think my problem has to do with the pointer-to-pointer dereferencing, but I'm not sure how to express this using the OpenMCL FFI.
> I could do something like:
>  (let ((id-ptr (%get-ptr &ImageDescriptor)))
>    (let ((err (#_GraphicsImportGetImageDescription &gi id-ptr)))
>        (unless (= err #$noErr)
>            (error "GraphicsImportGetImageDescription ~a" err)))
> ... But trying this also gives me weird values.
> Can anyone help me figure out how to pass these FFI values?
> Thanks,
> -Brent

More information about the Openmcl-devel mailing list