Go to the first, previous, next, last section, table of contents.

Win32 Package Reference

Overview

The Win32 implementation is still in a state of development. It is expected that changes will be necessary when MIT Scheme is ported to Windows NT on the DEC Alpha architecture. In particular, the current system is not arranged in a way that adequately distinguishes between issues that are a consequence of the NT operating system and those which are a consequence of the Intel x86 architecture.

Thus this documentation is not definitive, it merely outlines how the current system works. Parts of the system will change and any project implemented using the win32 system must plan for a re-implementation stage.

The Win32 implementation has several components:

Note that all the names in the Win32 support are part of the win32 package. The names are bound in the (win32) environment, and do not appear as bindings in the user or root environments. An effect of this is that it is far easier to develop Win32 software in the (win32) package environment or a child environment.

Foreign Function Interface

The Win32 foreign function interface (FFI) is a primitive and fairly simple system for calling procedures written in C in a dynamically linked library (DLL). Both user's procedures from a custom DLL and system procedures (e.g. MessageBox) are called using the same mechanism.

Warning: The FFI as it stands has several flaws which make it difficult to use reliably. It is expected that both the interface to and the mechanisms used by the FFI will be changed in the future. We provide it, and this documentation, only to give people an early start in accessing some of the features of Win32 from Scheme. Should you use it in an experiment we welcome any feedback.

The FFI is designed for calling C procedures that use C data types rather than Scheme data objects. Thus it is not possible to write and call a C procedure that returns, for example, a Scheme list. The object returned will always be an integer (which may represent the address of a C data structure).

Warning: It is extremely dangerous to try to pass Scheme callback procedures to C procedures. It is only possible by passing integer `handles' rather than the actual procedures, and even so, if a garbage collection occurs during the execution of the callback procedure objects in Scheme's heap will have moved. Thus in a foreign procedure that has a callback and a string, after calling the callback the string value may no longer be valid. Playing this game requires a profound knowledge of the implementation.

The interface to the FFI has two main components: a language for declaring the types of values passed to and returned from the foreign procedures and a form for declaring foreign procedures.

Windows Types

Foreign types are designed to represent a correspondence between a Scheme data type that is used to represent an object within the Scheme world and a C data type that represents the data object in the C world. Thus we cannot manipulate true C objects in Scheme, nor can we manipulate Scheme objects in C.

Each foreign type has four aspects that together ensure that the correspondence between the Scheme and C objects is maintained. These aspects are all encoded as procedures that either check for validity or convert between representations. Thus a foreign type is not a declarative type so much as a procedural description of how to pass the type. The underlying foreign procedure call mechanism can pass integers and vector-like Scheme objects, and returns integer values. All other objects must be translated into integers or some other basic type, and must be recovered from integers.

The aspects are:

check
A predicate that returns #t if the argument is of an acceptable Scheme type, otherwise returns #f. The check procedure is used for type-checking.
convert
A procedure of one argument which returns a Scheme object of one of the basic types. It is used to convert an object into a `simpler' object that will eventually be converted into a C object. The legal simpler objects are integers and strings.
return-convert
A procedure of one argument that, given an integer, returns a Scheme object of a type satisfying check. Its purpose is to convert the result returned by the foreign procedure into a Scheme value.
revert
Some C procedures modify one or more of their arguments. These arguments are passed by reference, i.e. as a pointer to their address. Since a Scheme object might have a different memory layout and storage conventions, it must be passed by copy-in and copy-out rather than by reference. Revert is a procedure of two parameters, the original object passed and the result of convert on that object. Revert may then inspect the converted object and copy back the changes to the original.

special form+: define-windows-type name check convert return revert
special form+: define-similar-windows-type name model [check [convert [return [revert]]]]
Both forms define a windows type. The first form defines a type in terms of its aspects as described above. The second defines the type as being like another type, except for certain aspects, which are redefined. Name is the name of the type. Model is the name of a type. Check, convert, return and revert are procedures or the value #f. A #f means use the default value, which in the second form means use the definition provided for model. The defaults are

check
(lambda (x) #t), i.e. unchecked.
convert
(lambda (x) x), i.e. no translation performed.
return
(lambda (x) x), i.e. no translation performed.
revert
(lambda (x y) unspecific), i.e. no update performed

The unchecked windows type (see below) is defined as:

(define-windows-type unchecked #f #f #f #f)

Windows types are not first class values, so they cannot be stored in variables or defined using define:

(define my-type unchecked)                       error-->  Unbound variable
(define-similar-windows-type my-type unchecked)  ;; the correct way

Scheme characters must be converted to integers. This is accomplished as follows:

(define-windows-type char
   char?          ; check
   char->integer  ; convert
   integer->char  ; convert return value
   #f             ; cannot be passed by reference
)

windows type: unchecked
The type which is not checked and undergoes only the basic conversion from a Scheme integer to a C integer or from a Scheme string to a C pointer to the first byte of the string. Returned unchecked values are returned as integers.

windows type: bool
Scheme booleans are analogous to C integers 0 and 1. Windows type bool have been defined as:

(define-windows-type bool
   boolean?
   (lambda (x) (if x 1 0))
   (lambda (x) (if (eq? x 0) #f #t))
   #f)

windows type: char
Scheme characters are converted into C objects of type char, which are indistinguishable from small integers.

windows type: int
windows type: uint
windows type: long
windows type: ulong
windows type: short
windows type: ushort
windows type: word
windows type: byte
Various integer types that are passed without conversion.

windows type: string
A string that is passed as a C pointer of type char* to the first character in the string.

windows type: char*
A string or #f. The string is passed as a pointer to characters. The string is correctly null-terminated. #f is passed as the null pointer. This is an example where there is a more complex mapping between C objects and Scheme objects. C's char* type is represented as one of two Scheme types depending on its value. This allows us us to distinguish between the C string (pointer) that points to the empty sequence of characters and the null pointer (which doesnt point anywhere).

windows type: handle
windows type: hbitmap
windows type: hbrush
windows type: hcursor
windows type: hdc
windows type: hicon
windows type: hinstance
windows type: hmenu
windows type: hpalette
windows type: hpen
windows type: hrgn
windows type: hwnd
Various kinds of Win32 handle. These names correspond to the same, but all uppercase, names in the Windows C language header files. Win32 API calls are the source of values of this type and the values are meaningless except as arguments to other Win32 API calls. Currently these values are represented as integers but we expect that Win32 handles will in future be represented by allocated Scheme objects (e.g. records) that will allow predicates (e.g. hmenu?) and sensible interlocking with the garbage collector to free the programmer of the current tedious allocation and deallocation of handles.

windows type: resource-id
A Windows resource identifier is either a small integer or a string. In C, this distinction is possible because pointers look like larger integers, so a machine word representing a small integer can be distinguished from a machine word that is a pointer to the text of the name of the resource.

Windows Foreign Procedures

Foreign procedures are declared as callable entry-points in a module, usually a dynamically linked library (DLL).

procedure+: find-module name
Returns a module suitable for use in creating procedures with windows-procedure. Name is a string which is the name of a DLL file. Internally, find-module uses the LoadLibrary Win32 API, so name should conform to the specifications for this call. Name should be either a full path name of a DLL, or the name of a DLL that resides in the same directory as the Scheme binary `SCHEME.EXE' or in the system directory.

The module returned is a description for the DLL, and the DLL need not necessarily be linked at or immediately after this call. DLL modules are linked on need and unlinked before Scheme exits and when there are no remaining references to entry points after a garbage-collection. This behaviour ensures that the Scheme system can run when a DLL is absent, provided the DLL is not actually used (i.e. no attempt is made to call a procedure in the DLL).

variable+: gdi32.dll
This variable is bound to the module describing the `GDI32.DLL' library, which contains the Win32 API graphics calls, e.g. LineTo.

variable+: kernel32.dll
This variable is bound to the module describing the `KERNEL32.DLL' library.

variable+: user32.dll
This variable is bound to the module describing the `USER32.DLL' library. This module contains many useful Win32 API procedures, like MessageBox and SetWindowText.

special form+: windows-procedure (name (parameter type) ...) return-type module entry-name [options]
This form creates a procedure, and could be thought of as "foreign-named-lambda". The form creates a Scheme procedure that calls the C procedure identified by the exported entry point entry-name in the module identified by the value of module. Both entry-name and module are evaluated at procedure creation time, so either may be expression. Entry-name must evaluate to a string and module must evaluate to a module as returned by find-module. These are the only parts of the form that are evaluated at procedure creation time.

Name is the name of the procedure and is for documentation purposes only. This form does not define a procedure called name. It is more like lambda. The name might be used for debugging and pretty-printing.

A windows procedure has a fixed number of parameters (i.e. no `rest' parameters or `varargs'), each of which is named and associated with a windows type type. Both the name parameter and the windows type type must be symbols and are not evaluated. The procedure returns a value of the windows type return-type.

The following example creates a procedure that takes a window handle (hwnd) and a string and returns a boolean (bool) result. The procedure does this by calling the SetWindowText entry in the module that is the value of the variable user32.dll. The variable set-window-title is defined to have this procedure as it's value.

(define set-window-title
  (windows-procedure (set-window-text (window hwnd) (text string))
    bool user32.dll "SetWindowText"))

(set-window-title my-win "Hi")   =>  #t
                                 ;; Changes window's title/text

set-window-title                 =>  #[compiled-procedure  ...]
set-window-text                  error-->  Unbound variable

When there are no options the created procedure will (a) check its arguments against the types, (b) convert the arguments, (c) call the C procedure and (d) convert the returned value. No reversion is performed, even if one of the types has a reversion defined. (Reverted types are rare [I have never used one], so paying a cost for this unless it is used seems silly).

The following options are allowed:

with-reversions
The reversions are included in the type conversions.
expand
A synonym for with-reversions.
Scheme code
The Scheme code is placed between steps (a) and (b) in the default process. The Scheme code can enforce constraints on the arguments, including constraints between arguments such as checking that an index refers to a valid position in a string.

If both options (i.e. with-reversions and Scheme code) are used, with-reversions must appear first. There can be arbitrarily many Scheme expression.

Win32 API names and procedures

This section is a moving target.

The #define values from `wingdi.h' and `winuser.h' are available as bindings in the (win32) package environment. The #define symbols are all uppercase; these have been translated to all lowercase Scheme identifiers, thus WM_LBUTTONUP is the scheme variable wm_lbuttonup. As Scheme is case insensitive, the upper-case version may be used and probably should to make the code look more like conventional Windows code. The Scheme bindings have been produced automagically. Most of the #define-symbols contain an underscore so there are not many name clashes. There is one very notable name clash, however: ERROR is #defined to 0, which shadows the scheme procedure error in the root package environment. To signal an error, use access to get error from the system global environment:

(declare (usual-integrations))
...
((access error system-global-environment) "Complain" ...)

The set of procedures is incomplete because procedures have been added on a by-need basis for the implementation of other parts of the system, e.g. Scheme Graphics. Look in the implementation for further details.

Win32 API procedure names have been uniformly converted into Scheme identifiers as follows:

  • A leading uppercase letter is translated into a lowercase letter.
  • Subsequent sequences of uppercase letters are translated into lowercase letters preceeded by a hyphen (minus symbol), i.e. hyphens are inserted at a lowercase to uppercase transition.
  • Predicates beginning with Is finally have a question-mark appended.

Example: applying these rules to IsWindow yields is-window?, and GetDC is translated into get-dc.

Device Independent Bitmap Utilities

The Device Independent Bitmap (DIB) utilities library `DIBUTILS.DLL' and the associated procedures in `dib.scm' in the Win32 system source is an example of how to use the foreign function interface to access and manipulate non-Scheme objects.

windows type: dib
In the C world a DIB is a handle to a piece of memory containing the bits that represent information about the image and the pixels of the image. The handle is a machine-word sized piece of data which may be thought of as a 32 bit integer. The handle may be null (i.e. zero), indicating that there is no block of memory describing the DIB. The null value is usually returned by C functions that are supposed to create a DIB but failed, for some reason like the memory could not be allocated or a file could not be opened.

In the Scheme world a DIB is a structure containing information about the bitmap (specifically the integer that represents the handle). We also include #f in the dib windows type to mirror the null handle error value.

(define dib-result
  (lambda (handle)
    (if (= handle 0)
        #f
        (make-dib handle))))

(define dib-arg
  (lambda (dib)
    (if dib
        (cell-contents (dib-handle dib))
        0)))  

(define-windows-type dib
  (lambda (thing) (or (dib? thing) (eq? thing #f)))
  dib-arg
  dib-result)

DIB procedures

The following procedures have typed parameters, using the same convention as windows-procedure.

procedure+: open-dib (filename string)
Return type: dib. Calls the OpenDIB entry of `DIBUTILS.DLL'. If the return value is not #f then the file filename was found, successfully opened, and the contents were suitable for loading into memory as a device independent bitmap.

procedure+: write-dib (filename string) (dib dib)
Return type: bool. Calls the WriteDIB entry of `DIBUTILS.DLL'. Returns #t if the file filename could be opened and written to. After this operation the file contains the bitmap data in a standard format that is understood by open-dib and various system utilities like the bitmap editor. Any problems resulting in failure are signalled by a #f return value.

procedure+: bitmap-from-dib (dib dib) (palette hpalette)
Return type: hbitmap. Calls the BitmapFromDib entry of `DIBUTILS.DLL'. The returned value is a device dependent bitmap. The colours from the DIB are matched against colors in palette.

procedure+: dib-from-bitmap (bitmap hbitmap) (style dword) (bits word) (palette hpalette)
Return type: dib. Returns a DIB containing the same image as the device dependent bitmap bitmap. Style determines the kind of DIB, e.g. compression style. Calls the DibFromBitmap entry of `DIBUTILS.DLL'.

procedure+: dib-blt (dest hdc) (x int) (y int) (w int) (h int) (src dib) (src-x int) (src-y int) (raster-op long)
Return type: bool. Calls the DibBlt entry of `DIBUTILS.DLL'. Similar to the Win32 API BitBlt call, but draws a DIB rather than a piece of another device context. Draws the dib on device context hdc at position (x,y). A rectangle of width w and height h is copied from position (src-x,src-y) of dib. Raster-op is supposed to allow the source and destination to be combined but I don't think I got this right so stick to SRCCOPY.

procedure+: %delete-dib (dib-handle handle)
Return type: bool. Calls the DeleteDIB entry of `DIBUTILS.DLL'. Note that the parameter is a handle, and not a dib. This allows us to destroy a DIB and reclaim its memory by knowing only the handle value, and not needing the dib record. The importance of this is that if the dib record is GC-ed then a GC hook can reclaim the storage knowing only the handle.

procedure+: delete-dib (dib dib)
Return type: bool. This procedure calls %delete-dib to reclaim the storage occupied by a DIB. After being deleted, the DIB should not be used. This procedure allows the programmer to reclaim external heap storage rather than risking it running out before the next garbage collection.

procedure+: dib-height (dib dib)
Return type: int. Calls the DibHeight expand entry of `DIBUTILS.DLL', which returns the height of the bitmap in pixels.

procedure+: dib-width (dib dib)
Return type: int. Calls the DibWidth entry of `DIBUTILS.DLL', which returns the width of the bitmap in pixels.

procedure+: copy-bitmap (bm hbitmap)
Return type: hbitmap. Calls the CopyBitmap of `DIBUTILS.DLL', which creates a new bitmap with the same size and contents as the original.

procedure+: create-dib (width int) (height int) (style int) (depth int) (palette hpalette)
Return type: dib. Calls the CreateDIB entry of `DIBUTILS.DLL'. Creates a DIB of width by height pixels and depth bits of colour information. The style parameter determines how the bitmap is stored. I have only ever used BI_RGB. If depth<=8 then the palette determines the DIB's colour table.

procedure+: crop-bitmap (bm hbitmap) (left int) (top int) (right int) (bottom int)
Return type: hbitmap. Calls the CropBitmap entry of `DIBUTILS.DLL'. Returns a new bitmap containing the image from a region of the original.

procedure+: dib-set-pixels-unaligned dib (pixels string)
Return type: bool. Calls the DIBSetPixelsUnaligned entry of `DIBUTILS.DLL'. Stuffs bytes from pixels into the bitmap. There are no alignment constraints on pixels (the usual way of doing this is to use the SetDIBits function which requires that every scan line of the bitmap is 32-bit word aligned, even if the scan lines are not a multiple of 4 bytes long). doing this

Other parts of the DIB Utilities implementation

The `DIBUTILS.DLL' library is an ordinary DLL. See the standard Microsoft Windows documentation on how to create DLLs. Look at the code in the `WIN32/DIBUTILS' directory of the Scheme source.

Please note:

  • For the foreign function interface to find the procedures they must be declared as exports in the `.DEF' definition file.
  • To load the `.DLL' file use the find-module Scheme function. Look at `WIN32/DIB.SCM' to see how this is done.
  • The current system works with C procedures with the __stdcall and __cdecl calling conventions but not the __fastcall calling convention.

Go to the first, previous, next, last section, table of contents.