[racket] ffi->SendMessageTimeoutW

From: Thomas Chust (chust at web.de)
Date: Fri Jan 18 11:33:20 EST 2013

On Fri, 18 Jan 2013, heraklea at gmx.de wrote:

> [...]
> (define ENV (cast "Environment" _string/utf-16 _intptr))
> [...]
> But in Racket when I call the function:
>
> (define-values ( r4 res4) (SendMessageTimeoutW HWND_BROADCAST
>                                               WM_SETTINGCHANGE
>                                               0
>                                               ENV
>                                               SMTO_ABORTIFHUNG
>                                               5000))
> I get r4= 1 and res4=0
> But (GetLastError) gives 3 and this means ERROR_PATH_NOT_FOUND.
>
> Which path is here mentioned?
> Is the ffi define correct, especially the way I define ENV gives me a headache?
> [...]

Hello,

the C pointer to string data allocated by Racket's FFI when _string/utf-16 
processes its input is valid only temporarily until the next time the 
garbage collector runs. When you cast it to an _intptr and store the 
result you effectively get some random integer that once corresponded to 
an address in the Racket heap but there is no guarantee about its meaning 
whenever you actually want to use it ;-)

You could instead allocate an immobile buffer for the string in UTF-16 
encoding, keep it in a module variable to protect it from garbage 
collection while the pointer is alive and cast the address of the buffer 
into an _intptr for use as an argument to SendMessageTimeoutW:

   (define-values (*ENV ENV)
     (let* ([data
             (call-with-output-bytes
              (lambda (out)
                (let ([out (reencode-output-port
                            out (string-append
                                 "UTF-16"
                                 (if (system-big-endian?)
                                     "BE" "LE")))])
                  (display "Environment\u0000" out)
                  (close-output-port out))))]
            [cdata
             (malloc 'atomic-interior (bytes-length data))])
       (memmove cdata data (bytes-length data))
       (values cdata (cast cdata _pointer _intptr))))

If you replace 'atomic-interior by 'raw you don't even have to keep the 
pointer *ENV around since the memory will never be garbage collected. 
However, you would then need to (free (cast _intptr _pointer ENV)) the 
memory explicitly at some point when ENV is no longer used.

And yes, this code looks clumsy. I really miss functions like 
string->bytes/utf-8 for arbitrary encodings, too ;-)

Ciao,
Thomas


-- 
When C++ is your hammer, every problem looks like your thumb.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 1600 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.racket-lang.org/users/archive/attachments/20130118/abdc224b/attachment.p7s>

Posted on the users mailing list.