294 lines
11 KiB
Racket
294 lines
11 KiB
Racket
#lang racket/base
|
|
(require ffi/unsafe
|
|
ffi/unsafe/define)
|
|
|
|
(provide get-resource
|
|
write-resource)
|
|
|
|
(define _HKEY (_cpointer/null 'HKEY))
|
|
|
|
(define (const-hkey v)
|
|
(cast (bitwise-ior v (arithmetic-shift -1 32)) _intptr _HKEY))
|
|
|
|
(define HKEY_CLASSES_ROOT (const-hkey #x80000000))
|
|
(define HKEY_CURRENT_USER (const-hkey #x80000001))
|
|
(define HKEY_LOCAL_MACHINE (const-hkey #x80000002))
|
|
(define HKEY_USERS (const-hkey #x80000003))
|
|
(define HKEY_CURRENT_CONFIG (const-hkey #x80000005))
|
|
|
|
(define REG_SZ 1)
|
|
(define REG_BINARY 3)
|
|
(define REG_DWORD 4)
|
|
|
|
(define (section->hkey who section)
|
|
(cond
|
|
[(equal? section "HKEY_CLASSES_ROOT")
|
|
HKEY_CLASSES_ROOT]
|
|
[(equal? section "HKEY_CURRENT_CONFIG")
|
|
HKEY_CURRENT_CONFIG]
|
|
[(equal? section "HKEY_CURRENT_USER")
|
|
HKEY_CURRENT_USER]
|
|
[(equal? section "HKEY_LOCAL_MACHINE")
|
|
HKEY_LOCAL_MACHINE]
|
|
[(equal? section "HKEY_USERS")
|
|
HKEY_USERS]
|
|
[(string? section) #f]
|
|
[else
|
|
(raise-type-error who "string" section)]))
|
|
|
|
(define advapi-dll (and (eq? (system-type) 'windows)
|
|
(ffi-lib "Advapi32.dll")))
|
|
(define kernel-dll (and (eq? (system-type) 'windows)
|
|
(ffi-lib "kernel32.dll")))
|
|
|
|
(define-ffi-definer define-advapi advapi-dll
|
|
#:default-make-fail make-not-available)
|
|
(define-ffi-definer define-kernel kernel-dll
|
|
#:default-make-fail make-not-available)
|
|
|
|
(define win64? (equal? "win32\\x86_64" (path->string (system-library-subpath #f))))
|
|
(define win_abi (if win64? #f 'stdcall))
|
|
|
|
(define _LONG _long)
|
|
(define _DWORD _int32)
|
|
(define _REGSAM _DWORD)
|
|
(define _BOOL (make-ctype _int (lambda (v) (if v 1 0)) (lambda (v) (not (zero? v)))))
|
|
|
|
(define KEY_QUERY_VALUE #x1)
|
|
(define KEY_SET_VALUE #x2)
|
|
|
|
(define ERROR_SUCCESS 0)
|
|
|
|
(define-advapi RegOpenKeyExW (_fun #:abi win_abi
|
|
_HKEY _string/utf-16 _DWORD _REGSAM (hkey : (_ptr o _HKEY))
|
|
-> (r : _LONG)
|
|
-> (and (= r ERROR_SUCCESS) hkey)))
|
|
(define-advapi RegCreateKeyExW (_fun #:abi win_abi
|
|
_HKEY _string/utf-16 (_DWORD = 0)
|
|
(_pointer = #f) ; class
|
|
_DWORD ; options
|
|
_REGSAM
|
|
_pointer ; security
|
|
(hkey : (_ptr o _HKEY))
|
|
(_ptr o _DWORD) ; disposition
|
|
-> (r : _LONG)
|
|
-> (and (= r ERROR_SUCCESS) hkey)))
|
|
|
|
(define-advapi RegQueryValueExW (_fun #:abi win_abi
|
|
_HKEY _string/utf-16 (_pointer = #f)
|
|
(type : (_ptr o _DWORD))
|
|
_pointer (len : (_ptr io _DWORD))
|
|
-> (r : _LONG)
|
|
-> (if (= r ERROR_SUCCESS)
|
|
(values len type)
|
|
(values #f #f))))
|
|
(define-advapi RegSetValueExW (_fun #:abi win_abi
|
|
_HKEY _string/utf-16 (_pointer = #f)
|
|
_DWORD _pointer _DWORD
|
|
-> (r : _LONG)
|
|
-> (= r ERROR_SUCCESS)))
|
|
|
|
(define-advapi RegCloseKey (_fun #:abi win_abi _HKEY -> _LONG))
|
|
|
|
(define-kernel WritePrivateProfileStringW (_fun #:abi win_abi
|
|
_string/utf-16 ; app
|
|
_string/utf-16 ; key
|
|
_string/utf-16 ; val
|
|
_string/utf-16 ; filename
|
|
-> _BOOL))
|
|
(define-kernel GetPrivateProfileStringW (_fun #:abi win_abi
|
|
_string/utf-16 ; app
|
|
_string/utf-16 ; key
|
|
_string/utf-16 ; default
|
|
_pointer ; result
|
|
_DWORD ; result size in wide chars
|
|
_string/utf-16 ; filename
|
|
-> _DWORD))
|
|
|
|
(define (file->ini f)
|
|
(cond
|
|
[(not f) (file->ini
|
|
(build-path (find-system-path 'home-dir) "mred.ini"))]
|
|
[(string? f) (file->ini (string->path f))]
|
|
[(path? f) (path->string (cleanse-path (path->complete-path f)))]))
|
|
|
|
(define (extract-sub-hkey file hkey entry op create-key?)
|
|
(cond
|
|
[(not (eq? 'windows (system-type))) (values #f #f)]
|
|
[file (values #f #f)]
|
|
[(regexp-match #rx"^(.*)\\\\+([^\\]*)$" entry)
|
|
=> (lambda (m)
|
|
(let ([sub-hkey (RegOpenKeyExW hkey (cadr m) 0 op)]
|
|
[sub-entry (caddr m)])
|
|
(if (and (not sub-hkey)
|
|
create-key?)
|
|
(values (RegCreateKeyExW hkey (cadr m) 0 op #f)
|
|
sub-entry)
|
|
(values sub-hkey sub-entry))))]
|
|
[else (values hkey entry)]))
|
|
|
|
(define (get-resource section entry [value #f] [file #f]
|
|
#:type [rtype (or (and (box? value)
|
|
(or
|
|
(and (exact-integer? (unbox value))
|
|
'integer)
|
|
(and (bytes? (unbox value))
|
|
'bytes)))
|
|
'string)])
|
|
(define hkey (section->hkey 'get-resource section))
|
|
(unless (string? entry)
|
|
(raise-type-error 'get-resource "string" entry))
|
|
(unless (or (not value)
|
|
(and (box? value)
|
|
(let ([value (unbox value)])
|
|
(or (string? value) (bytes? value) (exact-integer? value)))))
|
|
(raise-type-error 'get-resource "box of string, byte string, or exact integer"))
|
|
(unless (or (not file)
|
|
(path-string? file))
|
|
(raise-type-error 'get-resource "path string or #f" file))
|
|
(unless (memq rtype '(string bytes integer))
|
|
(raise-type-error 'get-resource "'string, 'bytes, or 'integer" rtype))
|
|
|
|
(define (to-rtype s)
|
|
(let ([to-string (lambda (s)
|
|
(if (bytes? s)
|
|
(bytes->string/utf-8 s #\?)
|
|
s))])
|
|
(cond
|
|
[(eq? rtype 'string) (to-string s)]
|
|
[(eq? rtype 'integer)
|
|
(let ([n (string->number (to-string s))])
|
|
(or (and n (exact-integer? n) n)
|
|
0))]
|
|
[else
|
|
(if (string? s)
|
|
(string->bytes/utf-8 s)
|
|
s)])))
|
|
|
|
(define-values (sub-hkey sub-entry)
|
|
(extract-sub-hkey file hkey entry KEY_QUERY_VALUE #f))
|
|
|
|
(cond
|
|
[sub-hkey
|
|
(begin0
|
|
(let-values ([(len type)
|
|
;; Get size, first
|
|
(RegQueryValueExW sub-hkey sub-entry #f 0)])
|
|
(and len
|
|
(let ([s (make-bytes len)])
|
|
(let-values ([(len2 type2)
|
|
;; Get value, now that we have a bytes string of the right size
|
|
(RegQueryValueExW sub-hkey sub-entry s len)])
|
|
(and len2
|
|
(let ([r
|
|
;; Unmarhsal according to requested type:
|
|
(let ([s (cond
|
|
[(= type REG_SZ)
|
|
(cast s _pointer _string/utf-16)]
|
|
[(= type REG_DWORD)
|
|
(number->string (ptr-ref s _DWORD))]
|
|
[else
|
|
s])])
|
|
(to-rtype s))])
|
|
(if (box? value)
|
|
(begin
|
|
(set-box! value r)
|
|
#t)
|
|
r)))))))
|
|
(unless (eq? hkey sub-hkey)
|
|
(RegCloseKey sub-hkey)))]
|
|
[(eq? 'windows (system-type))
|
|
(let* ([SIZE 1024]
|
|
[dest (make-bytes (* SIZE 2) 0)]
|
|
[DEFAULT "$$default"]
|
|
[len (GetPrivateProfileStringW section entry DEFAULT
|
|
dest SIZE
|
|
(file->ini file))])
|
|
(let ([s (cast dest _pointer _string/utf-16)])
|
|
(and (not (equal? s DEFAULT))
|
|
(let ([r (to-rtype s)])
|
|
(if value
|
|
(begin
|
|
(set-box! value r)
|
|
#t)
|
|
r)))))]
|
|
[else #f]))
|
|
|
|
(define (write-resource section entry value [file #f]
|
|
#:type [type 'string]
|
|
#:create-key? [create-key? #f])
|
|
(define hkey (section->hkey 'write-resource section))
|
|
(unless (string? entry)
|
|
(raise-type-error 'write-resource "string" entry))
|
|
(unless (or (string? value) (bytes? value) (exact-integer? value))
|
|
(raise-type-error 'write-resource "string, byte string, or exact integer"))
|
|
(unless (or (not file)
|
|
(path-string? file))
|
|
(raise-type-error 'write-resource "path string or #f" file))
|
|
(unless (memq type '(string bytes dword))
|
|
(raise-type-error 'write-resource "'string, 'bytes, or 'dword" type))
|
|
|
|
(define (to-string value)
|
|
(cond
|
|
[(exact-integer? value) (number->string value)]
|
|
[(string? value) value]
|
|
[else (bytes->string/utf-8 value #\?)]))
|
|
|
|
(define-values (sub-hkey sub-entry)
|
|
(extract-sub-hkey file hkey entry KEY_SET_VALUE create-key?))
|
|
|
|
(cond
|
|
[sub-hkey
|
|
(begin0
|
|
(let ([v (case type
|
|
[(string)
|
|
(to-utf-16 (to-string value))]
|
|
[(bytes)
|
|
(cond
|
|
[(exact-integer? value)
|
|
(string->bytes/utf-8 (number->string value))]
|
|
[(string? value) (string->bytes/utf-8 value)]
|
|
[else value])]
|
|
[(dword)
|
|
(to-dword-ptr
|
|
(cond
|
|
[(exact-integer? value) value]
|
|
[(string? value) (string->number value)]
|
|
[(bytes? value)
|
|
(string->number (bytes->string/utf-8 value #\?))]))])]
|
|
[ty (case type
|
|
[(string) REG_SZ]
|
|
[(bytes) REG_BINARY]
|
|
[(dword) REG_DWORD])])
|
|
(RegSetValueExW sub-hkey sub-entry ty v (bytes-length v)))
|
|
(unless (eq? hkey sub-hkey)
|
|
(RegCloseKey sub-hkey)))]
|
|
[(eq? 'windows (system-type))
|
|
(WritePrivateProfileStringW section entry (to-string value) (file->ini file))]
|
|
[else #f]))
|
|
|
|
(define (to-utf-16 s)
|
|
(let ([v (malloc _gcpointer)])
|
|
(ptr-set! v _string/utf-16 s)
|
|
(let ([p (ptr-ref v _gcpointer)])
|
|
(let ([len (* 2 (+ 1 (utf-16-length s)))])
|
|
(ptr-ref v (_bytes o len))))))
|
|
|
|
(define (utf-16-length s)
|
|
(for/fold ([len 0]) ([c (in-string s)])
|
|
(+ len
|
|
(if ((char->integer c) . > . #xFFFF)
|
|
2
|
|
1))))
|
|
|
|
(define (to-dword-ptr v)
|
|
(let ([v (if (and (exact-integer? v)
|
|
(<= (- (expt 2 31))
|
|
v
|
|
(sub1 (expt 2 31))))
|
|
v
|
|
0)])
|
|
(let ([p (malloc _DWORD)])
|
|
(ptr-set! p _DWORD v)
|
|
(cast p _pointer (_bytes o (ctype-sizeof _DWORD))))))
|