1(*
2    Copyright (c) 2001, 2015
3        David C.J. Matthews
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License version 2.1 as published by the Free Software Foundation.
8    
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13    
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*)
18(* This contains various types and other values which are needed in various
19   modules.  All the exported types are contained in other structures. *)
20structure Base:
21sig
22    val winCall0: Foreign.symbol -> unit -> 'a Foreign.conversion -> unit -> 'a
23    val winCall1: Foreign.symbol -> 'a Foreign.conversion -> 'b Foreign.conversion -> 'a -> 'b
24    val winCall2: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion -> 'c Foreign.conversion -> 'a * 'b -> 'c
25    val winCall3: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion -> 'd Foreign.conversion -> 'a * 'b * 'c -> 'd
26    val winCall4: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion -> 'e Foreign.conversion ->
27            'a * 'b * 'c * 'd -> 'e
28    val winCall5:
29        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion *  'e Foreign.conversion ->
30            'f Foreign.conversion -> 'a * 'b * 'c * 'd * 'e -> 'f
31    val winCall6:
32        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
33             'f Foreign.conversion -> 'g Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f -> 'g
34    val winCall7:
35        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
36             'f Foreign.conversion * 'g Foreign.conversion -> 'h Foreign.conversion ->
37             'a * 'b * 'c * 'd * 'e * 'f * 'g -> 'h
38    val winCall8:
39        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
40             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion -> 'i Foreign.conversion ->
41             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h -> 'i
42    val winCall9:
43        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
44             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion ->
45             'j Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i -> 'j
46    val winCall10:
47        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
48             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion ->
49             'k Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j -> 'k
50    val winCall11:
51        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
52             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion ->
53             'l Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k -> 'l
54    val winCall12:
55        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
56             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
57             'l Foreign.conversion -> 'm Foreign.conversion ->
58             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l -> 'm
59    val winCall13:
60        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
61             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
62             'l Foreign.conversion * 'm Foreign.conversion -> 'n Foreign.conversion ->
63             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm -> 'n
64    val winCall14:
65        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
66             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
67             'l Foreign.conversion * 'm Foreign.conversion * 'n Foreign.conversion ->
68            'o Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm * 'n -> 'o
69            
70    val winAbi: Foreign.LibFFI.abi
71
72    val kernel: string -> Foreign.symbol
73    and user: string -> Foreign.symbol
74    and commdlg: string -> Foreign.symbol
75    and gdi: string -> Foreign.symbol
76    and shell: string -> Foreign.symbol
77    and comctl: string -> Foreign.symbol
78    
79    val cSIZE_T: int Foreign.conversion
80    and cLPARAM: int Foreign.conversion
81    and cLONG_PTR: int Foreign.conversion
82    and cULONG_PTR: int Foreign.conversion
83    and cINT_PTR: int Foreign.conversion
84    and cUINT_PTR: int Foreign.conversion
85    and cDWORD: int Foreign.conversion
86    and cWORD: int Foreign.conversion
87    and cDWORD_PTR: int Foreign.conversion
88    and cUINT_PTRw: SysWord.word Foreign.conversion
89
90    val cUint8w: Word8.word Foreign.conversion
91    and cUint16w: Word.word Foreign.conversion
92    and cUint32w: Word32.word Foreign.conversion
93    and cUintw: Word32.word Foreign.conversion
94    and cUlongw: Word32.word Foreign.conversion
95
96    val cDWORDw: Word32.word Foreign.conversion
97    and cWORDw: Word.word Foreign.conversion
98
99    val cBool: bool Foreign.conversion
100    
101    val successState: string -> unit Foreign.conversion
102    val cPOSINT: string -> int Foreign.conversion
103    
104    type POINT = { x: int, y: int }
105    val cPoint: POINT Foreign.conversion
106    type RECT =  { left: int, top: int, right: int, bottom: int }
107    val cRect: RECT Foreign.conversion
108    type SIZE = { cx: int, cy: int }
109    val cSize: SIZE Foreign.conversion
110
111    eqtype 'a HANDLE
112    val hNull: 'a HANDLE
113    val isHNull: 'a HANDLE -> bool
114    val handleOfVoidStar: Foreign.Memory.voidStar -> 'a HANDLE
115    and voidStarOfHandle: 'a HANDLE -> Foreign.Memory.voidStar
116
117    eqtype HMENU and HDC and HWND and HINSTANCE and HGDIOBJ
118    and HDROP and HRSRC and HUPDATE
119
120    val cHGDIOBJ:   HGDIOBJ Foreign.conversion
121    and cHDROP:     HDROP Foreign.conversion
122    and cHMENU:     HMENU Foreign.conversion
123    and cHINSTANCE: HINSTANCE Foreign.conversion
124    and cHDC:       HDC Foreign.conversion
125    and cHWND:      HWND Foreign.conversion
126    val cHMENUOPT:  HMENU option Foreign.conversion
127    and cHGDIOBJOPT: HGDIOBJ option Foreign.conversion
128    and cHWNDOPT: HWND option Foreign.conversion
129    and cHRSRC: HRSRC Foreign.conversion
130    and cHUPDATE: HUPDATE Foreign.conversion
131
132    val hgdiObjNull:HGDIOBJ 
133    and isHgdiObjNull: HGDIOBJ -> bool
134    and hdcNull: HDC
135    and isHdcNull: HDC -> bool
136    and hmenuNull: HMENU
137    and isHmenuNull: HMENU -> bool
138    and hinstanceNull: HINSTANCE
139    and isHinstanceNull: HINSTANCE -> bool
140    and hwndNull: HWND
141
142    type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ
143    and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ
144    and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ
145
146    val cHPALETTE: HPALETTE Foreign.conversion
147    and cHFONT: HFONT Foreign.conversion
148    and cHPEN: HPEN Foreign.conversion
149    and cHBITMAP: HBITMAP Foreign.conversion
150    and cHRGN: HRGN Foreign.conversion
151    and cHBRUSH: HBRUSH Foreign.conversion
152    and cHENHMETAFILE: HENHMETAFILE Foreign.conversion
153    and cHMETAFILE: HMETAFILE Foreign.conversion
154
155    
156    type HICON = HGDIOBJ and HCURSOR = HGDIOBJ
157    val cHICON: HICON Foreign.conversion
158    and cHCURSOR: HCURSOR Foreign.conversion
159    
160    val absConversion:
161        {abs: 'a -> 'b, rep: 'b -> 'a} -> 'a Foreign.conversion -> 'b Foreign.conversion
162
163    val tableLookup:
164        (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option -> (''a -> ''b) * (''b -> ''a)
165    and tableSetLookup:
166        (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option ->
167            (''a list -> Word32.word) * (Word32.word -> ''a list)
168
169    val tableConversion:
170        (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option ->
171            ''b Foreign.conversion -> ''a Foreign.conversion
172    (* tableSetConversion is always a cUint *)
173    and tableSetConversion:
174        (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option ->
175            ''a list Foreign.conversion
176    
177    val list2Vector: 'a Foreign.conversion -> 'a list -> Foreign.Memory.voidStar * int
178    
179    datatype ClassType = NamedClass of string | ClassAtom of int
180    val cCLASS: ClassType Foreign.conversion
181
182    datatype ClipboardFormat =
183        CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF |
184        CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT |
185        CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT |
186        CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int |
187        CF_HDROP | CF_LOCALE
188    val clipLookup: (ClipboardFormat -> int) * (int -> ClipboardFormat)
189    
190    datatype RESID = IdAsInt of int | IdAsString of string
191    val cRESID: RESID Foreign.conversion
192    
193    val STRINGOPT: string option Foreign.conversion
194    val cCHARARRAY: int -> string Foreign.conversion
195    val fromCstring: Foreign.Memory.voidStar -> string
196    val toCstring: string -> Foreign.Memory.voidStar (* Memory must be freed *)
197    val copyStringToMem: Foreign.Memory.voidStar * int * string -> unit
198    val fromCWord8vec: Foreign.Memory.voidStar * int -> Word8Vector.vector
199    val toCWord8vec: Word8Vector.vector -> Foreign.Memory.voidStar (* Memory must be freed *)
200    
201    val getStringCall: (Foreign.Memory.voidStar * int -> int) -> string
202    val getStringWithNullIsLength: (Foreign.Memory.voidStar * int -> int) -> string
203    val getVectorResult:
204        'a Foreign.conversion -> (Foreign.Memory.voidStar * int -> int) -> int -> 'a vector
205
206    eqtype HGLOBAL
207    val cHGLOBAL: HGLOBAL Foreign.conversion
208    val GlobalAlloc: int * int -> HGLOBAL
209    val GlobalLock: HGLOBAL -> Foreign.Memory.voidStar
210    val GlobalFree: HGLOBAL -> HGLOBAL
211    val GlobalSize: HGLOBAL -> int
212    val GlobalUnlock: HGLOBAL -> bool
213
214    val HIWORD: Word32.word -> Word.word
215    val LOWORD: Word32.word -> Word.word
216    val MAKELONG: Word.word * Word.word -> Word32.word
217    val HIBYTE: Word.word -> Word8.word
218    val LOBYTE: Word.word -> Word8.word
219    
220    val unicodeToString: Word8Vector.vector -> string
221    val stringToUnicode: string -> Word8Vector.vector
222    
223    val GetLastError: unit -> OS.syserror
224    
225    val checkResult: bool -> unit
226    val raiseSysErr: unit -> 'a
227    
228    structure FindReplaceFlags:
229    sig
230        include BIT_FLAGS
231        val FR_DIALOGTERM : flags
232        val FR_DOWN : flags
233        val FR_FINDNEXT : flags
234        val FR_HIDEMATCHCASE : flags
235        val FR_HIDEUPDOWN : flags
236        val FR_HIDEWHOLEWORD : flags
237        val FR_MATCHCASE : flags
238        val FR_NOMATCHCASE : flags
239        val FR_NOUPDOWN : flags
240        val FR_NOWHOLEWORD : flags
241        val FR_REPLACE : flags
242        val FR_REPLACEALL : flags
243        val FR_SHOWHELP : flags
244        val FR_WHOLEWORD : flags
245        val cFindReplaceFlags: flags Foreign.conversion
246    end
247
248end =
249struct
250    open Foreign
251(*    val System_isShort : vol -> bool =
252        RunCall.run_call1 RuntimeCalls.POLY_SYS_is_short*)
253
254    fun absConversion {abs: 'a -> 'b, rep: 'b -> 'a} (c: 'a conversion) : 'b conversion =
255    let
256        val { load=loadI, store=storeI, ctype } = breakConversion c
257        fun load m = abs(loadI m)
258        fun store(m, v) = storeI(m, rep v)
259    in
260        makeConversion { load = load, store = store, ctype = ctype }
261    end
262
263    (* In many cases we can pass a set of options as a bit set. *)
264    (*
265    fun bitsetConversion {abs, rep} =
266    let
267        val (fromC, toC, Ctype) = breakConversion INT
268        val fromList = List.foldl (fn(i, n) => IntInf.orb(rep i, n)) 0
269        fun toList n = [abs n] (* This is a bit of a mess. *)
270    in
271        mkConversion (toList o fromCuint) (toCuint o fromList) Cuint
272    end*)
273
274    (* Conversions between Word/Word32/LargeWord etc. *)
275    local
276        open Memory LowLevel
277        fun noFree () = ()
278    in
279        local
280            fun load(m: voidStar): Word8.word = get8(m, 0w0)
281            fun store(m: voidStar, i: Word8.word) = (set8(m, 0w0, i); noFree)
282        in
283            val cUint8w: Word8.word conversion =
284                makeConversion{ load=load, store=store, ctype = cTypeUint8 }
285        end
286        local
287            fun load(m: voidStar): Word.word = get16(m, 0w0)
288            fun store(m: voidStar, i: Word.word) = (set16(m, 0w0, i); noFree)
289        in
290            val cUint16w: Word.word conversion =
291                makeConversion{ load=load, store=store, ctype = cTypeInt16 }
292        end
293        local
294            fun load(m: voidStar): Word32.word = get32(m, 0w0)
295            fun store(m: voidStar, i: Word32.word) = (set32(m, 0w0, i); noFree)
296        in
297            val cUint32w: Word32.word conversion =
298                makeConversion{ load=load, store=store, ctype = cTypeUint32 }
299            
300        end
301        val cUintw = cUint32w
302        (* Int should be 32-bits on Windows. *)
303        val _ = #size LowLevel.cTypeUint = #size LowLevel.cTypeUint32
304                    orelse raise Fail "unsigned int is not 32-bits"
305        val cUlongw = cUint32w
306        val _ = #size LowLevel.cTypeUlong = #size LowLevel.cTypeUint32
307                    orelse raise Fail "unsigned long is not 32-bits"
308    end
309
310    val cDWORD = cUint32 (* Defined to be 32-bit unsigned *)
311    and cWORD = cUint16 (* Defined to be 16-bit unsigned *)
312    
313    val cDWORDw = cUint32w
314    and cWORDw = cUint16w
315    
316    (* For some reason Windows has both INT_PTR and LONG_PTR and they
317       are slightly different. *)
318    val cLONG_PTR =
319        if #size LowLevel.cTypePointer = 0w4
320        then cLong
321        else cInt64
322    
323    val cINT_PTR =
324        if #size LowLevel.cTypePointer = 0w4
325        then cInt
326        else cInt64
327
328    val cULONG_PTR =
329        if #size LowLevel.cTypePointer = 0w4
330        then cUlong
331        else cUint64
332
333    val cUINT_PTR =
334        if #size LowLevel.cTypePointer = 0w4
335        then cUint
336        else cUint64
337
338    val cLPARAM = cLONG_PTR
339    val cSIZE_T = cULONG_PTR (* Probably. *)
340    val cDWORD_PTR = cULONG_PTR (* Defined to be the same so I'm not sure why it's there .*)
341    
342    val cUINT_PTRw = absConversion{abs=Memory.voidStar2Sysword, rep=Memory.sysWord2VoidStar} cPointer
343
344    (* These are called XXX32.DLL on both 32-bit and 64-bit. *)
345    fun kernel name = getSymbol(loadLibrary "kernel32.dll") name
346    and user sym = getSymbol(loadLibrary "user32.DLL") sym
347    and commdlg sym = getSymbol(loadLibrary "comdlg32.DLL") sym
348    and gdi sym = getSymbol(loadLibrary "gdi32.DLL") sym
349    and shell sym = getSymbol(loadLibrary "shell32.DLL") sym
350    and comctl sym = getSymbol(loadLibrary "comctl32.DLL") sym
351
352    (* We need to use the Pascal calling convention on 32-bit Windows. *)
353    val winAbi =
354        case List.find (fn ("stdcall", _) => true | _ => false) LibFFI.abiList of
355            SOME(_, abi) => abi
356        |   NONE => LibFFI.abiDefault
357
358    (* As well as setting the abi we can also use the old argument order. *)
359    fun winCall0 sym argConv resConv = buildCall0withAbi(winAbi, sym, argConv, resConv)
360    and winCall1 sym argConv resConv = buildCall1withAbi(winAbi, sym, argConv, resConv)
361    and winCall2 sym argConv resConv = buildCall2withAbi(winAbi, sym, argConv, resConv)
362    and winCall3 sym argConv resConv = buildCall3withAbi(winAbi, sym, argConv, resConv)
363    and winCall4 sym argConv resConv = buildCall4withAbi(winAbi, sym, argConv, resConv)
364    and winCall5 sym argConv resConv = buildCall5withAbi(winAbi, sym, argConv, resConv)
365    and winCall6 sym argConv resConv = buildCall6withAbi(winAbi, sym, argConv, resConv)
366    and winCall7 sym argConv resConv = buildCall7withAbi(winAbi, sym, argConv, resConv)
367    and winCall8 sym argConv resConv = buildCall8withAbi(winAbi, sym, argConv, resConv)
368    and winCall9 sym argConv resConv = buildCall9withAbi(winAbi, sym, argConv, resConv)
369    and winCall10 sym argConv resConv = buildCall10withAbi(winAbi, sym, argConv, resConv)
370    and winCall11 sym argConv resConv = buildCall11withAbi(winAbi, sym, argConv, resConv)
371    and winCall12 sym argConv resConv = buildCall12withAbi(winAbi, sym, argConv, resConv)
372    and winCall13 sym argConv resConv = buildCall13withAbi(winAbi, sym, argConv, resConv)
373    and winCall14 sym argConv resConv = buildCall14withAbi(winAbi, sym, argConv, resConv)
374
375    (* Previously we had a specific call to do this.  The error state is
376       no longer set by the new FFI. *)
377(*
378    fun GetLastError(): OS.syserror =
379        RunCall.run_call2 RuntimeCalls.POLY_SYS_os_specific (1100, ())
380*)
381    local
382        val getLastError = winCall0 (kernel "GetLastError") () cDWORD
383    in
384        fun GetLastError(): OS.syserror =
385            (* Windows error codes are negative values in OS.syserror. *)
386            RunCall.unsafeCast (~ (getLastError()))
387    end
388
389    (* The string argument of the SysErr exception is supposed to match the result of OS.errMsg. *)
390    fun raiseSysErr () = let val err = GetLastError() in raise OS.SysErr(OS.errorMsg err, SOME err) end
391
392    (* Many system calls return bool.  If the result is false we raise an exception. *)
393    fun checkResult true = () | checkResult false = raiseSysErr ()
394    
395    val cBool: bool conversion =
396        absConversion{abs = fn 0 => false | _ => true, rep = fn false => 0 | true => 1} cInt
397
398    fun successState name: unit conversion =
399         absConversion { abs = checkResult, rep = fn _ => raise Fail ("successState:" ^ name) } cBool
400
401
402    type POINT = { x: int, y: int }
403
404    local
405        fun breakPoint ({x,y}: POINT) = (x,y)
406        fun mkPoint (x,y): POINT = {x=x, y=y}
407    in
408        val cPoint = absConversion {abs=mkPoint, rep=breakPoint} (cStruct2 (cLong, cLong))
409    end
410
411    type RECT =  { left: int, top: int, right: int, bottom: int }
412
413    local
414        fun breakRect ({left,top,right,bottom}: RECT) = (left,top,right,bottom)
415        fun mkRect (left,top,right,bottom): RECT =
416            {left=left,top=top,right=right,bottom=bottom}
417    in
418        val cRect = absConversion {abs=mkRect, rep=breakRect} (cStruct4 (cLong,cLong,cLong,cLong))
419    end
420
421    type SIZE = { cx: int, cy: int }
422    local
423        fun breakSize ({cx,cy}: SIZE) = (cx,cy)
424        fun mkSize (cx,cy): SIZE = {cx=cx, cy=cy}
425    in
426        val cSize = absConversion {abs=mkSize, rep=breakSize} (cStruct2 (cLong,cLong))
427    end
428
429    (* Handles are generally opaque values.  We want them to be eqtypes, though. *)
430    local
431        structure HandStruct :>
432            sig
433                eqtype 'a HANDLE
434                val hNull: 'a HANDLE
435                val isHNull: 'a HANDLE -> bool
436                val handleOfVoidStar: Memory.voidStar -> 'a HANDLE
437                and voidStarOfHandle: 'a HANDLE -> Memory.voidStar
438            end =
439        struct
440            type 'a HANDLE = Memory.voidStar
441            val hNull = Memory.null
442            fun isHNull h = h = hNull
443        
444            (* We sometimes need the next two functions internally.
445               They're needed externally unless we change the result type
446               of SendMessage to allow us to return a handle for certain
447               messages. *)
448            fun handleOfVoidStar h = h
449            and voidStarOfHandle h = h
450        end
451    in
452        open HandStruct
453    end
454
455    (* We just need these as placeholders. We never create values of
456       these types.  They are used simply as a way of creating different
457       handle types. *)
458    (* Don't use abstype - we want them to eqtypes *)
459    datatype GdiObj = GdiObj
460    and Instance = Instance
461    and Drop = Drop
462    and DeviceContext = DeviceContext
463    and Menu = Menu
464    and Window = Window
465    and Global = Global
466    and Src = Src
467    and Update = Update
468
469    (* HINSTANCE is used as an instance of a module. *)
470    type HINSTANCE = Instance HANDLE
471    and  HDROP = Drop HANDLE
472    and  HGDIOBJ = GdiObj HANDLE
473    and  HDC = DeviceContext HANDLE
474    and  HMENU = Menu HANDLE
475    and  HWND = Window HANDLE
476    and  HGLOBAL = Global HANDLE
477    and  HRSRC = Src HANDLE
478    and  HUPDATE = Update HANDLE
479
480    local
481        fun cHANDLE() =
482            absConversion {abs=handleOfVoidStar, rep=voidStarOfHandle} cPointer
483        fun hoptOfvs n =
484            if Memory.voidStar2Sysword n = 0w0 then NONE else SOME(handleOfVoidStar n)
485        
486        fun cHANDLEOPT() =
487            absConversion {abs=hoptOfvs, rep=fn v => voidStarOfHandle(getOpt(v, hNull)) } cPointer
488    in
489        val cHGDIOBJ:   HGDIOBJ conversion = cHANDLE()
490        and cHDROP:     HDROP conversion = cHANDLE()
491        and cHMENU:     HMENU conversion = cHANDLE()
492        and cHINSTANCE: HINSTANCE conversion = cHANDLE()
493        and cHDC:       HDC conversion = cHANDLE()
494        and cHWND:      HWND conversion = cHANDLE()
495
496        val cHMENUOPT:  HMENU option conversion = cHANDLEOPT()
497        and cHGDIOBJOPT: HGDIOBJ option conversion = cHANDLEOPT()
498        and cHWNDOPT: HWND option conversion = cHANDLEOPT()
499        
500        val cHGLOBAL: HGLOBAL conversion = cHANDLE()
501        and cHRSRC: HRSRC conversion = cHANDLE()
502        and cHUPDATE: HUPDATE conversion = cHANDLE()
503    end
504
505    (* Temporary declarations. *)
506    val hgdiObjNull:HGDIOBJ  = hNull
507    and isHgdiObjNull: HGDIOBJ -> bool = isHNull
508    and hdcNull: HDC = hNull
509    and isHdcNull: HDC -> bool = isHNull
510    and hmenuNull: HMENU = hNull
511    and isHmenuNull: HMENU -> bool = isHNull
512    and hinstanceNull: HINSTANCE = hNull
513    and isHinstanceNull: HINSTANCE -> bool = isHNull
514    and hwndNull: HWND = hNull
515
516    (* All these are various kinds of HGDIOBJ.  It's too complicated to try
517       to use different types for them. *)
518    type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ
519    and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ
520    and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ
521
522    val cHPALETTE: HPALETTE conversion = cHGDIOBJ
523    and cHFONT: HFONT conversion = cHGDIOBJ
524    and cHPEN: HPEN conversion = cHGDIOBJ
525    and cHBITMAP: HBITMAP conversion = cHGDIOBJ
526    and cHRGN: HRGN conversion = cHGDIOBJ
527    and cHBRUSH: HBRUSH conversion = cHGDIOBJ
528    and cHENHMETAFILE: HENHMETAFILE conversion = cHGDIOBJ
529    and cHMETAFILE: HMETAFILE conversion = cHGDIOBJ
530
531    (* I'm not so happy about treating these as HGDIOBJ but it makes the
532       types of messages such as BM_SETIMAGE simpler. *)
533    type HICON = HGDIOBJ and HCURSOR = HGDIOBJ
534    val cHICON = cHGDIOBJ and cHCURSOR = cHGDIOBJ
535
536    (* The easiest way to deal with datatypes is often by way of a table. *)
537    fun tableLookup (table: (''a * ''b) list, default) =
538    let
539        fun toInt [] x =
540            (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x)
541         |  toInt ((y, i) :: tl) x = if x = y then i else toInt tl x
542
543        fun fromInt [] x =
544            (case default of
545                NONE => raise Fail ("tableLookup: not found")
546             |  SOME (d, _) => d x)
547         |  fromInt ((y, i) :: tl) x = if x = i then y else fromInt tl x
548    in
549        (toInt table, fromInt table)
550    end
551
552    fun tableConversion (table: (''a * ''b) list, default) (conv: ''b conversion): ''a conversion  =
553    let
554        val (toInt, fromInt) = tableLookup(table, default)
555    in
556        absConversion {abs = fromInt, rep = toInt} conv
557    end
558
559    (* In other cases we have sets of options.  We represent them by a list.
560       The order of the elements in the table is significant if we are to be
561       able to handle multiple bits.  Patterns with more than one bit set
562       MUST be placed later than those with a subset of those bits. *)
563    fun tableSetLookup (table: (''a * Word32.word) list, default) =
564    let
565        open Word32
566        (* Conversion to integer - just fold the values. *)
567        fun toInt' [] x =
568            (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x)
569         |  toInt' ((y, i) :: tl) x = if x = y then i else toInt' tl x
570
571        val toInt = List.foldl (fn (a, b) => orb(toInt' table a, b)) 0w0
572
573        (* It would speed up the searches if we ordered the list so that multiple
574           bit entries preceded those with fewer bits but it's much easier to lay
575           out the tables if we do it this way. *)
576        fun fromInt _ _ 0w0 = [] (* Zero is an empty list. *)
577
578         |  fromInt [] NONE x = (* Not found *)
579                (case default of
580                    NONE => raise Fail ("tableLookup: not found" ^ Word32.toString x)
581                  | SOME (d, _) => [d x])
582
583         |  fromInt [] (SOME(res, bits)) x = (* Found something - remove it from the set. *)
584                (res :: fromInt table NONE (andb(x, notb bits)))
585
586         |  fromInt ((res, bits)::tl) sofar x =
587                if bits <> 0w0 andalso andb(x, bits) = bits
588                then (* Matches *) fromInt tl (SOME(res, bits)) x
589                else (* Doesn't match *) fromInt tl sofar x
590    in
591        (toInt, fromInt table NONE)
592    end
593
594    fun tableSetConversion (table: (''a * Word32.word) list, default): ''a list conversion  =
595    let
596        val (toInt, fromInt) = tableSetLookup(table, default)
597    in
598        absConversion {abs = fromInt, rep = toInt} cUintw
599    end
600
601    
602    structure FindReplaceFlags:>
603    sig
604        include BIT_FLAGS
605        val FR_DIALOGTERM : flags
606        val FR_DOWN : flags
607        val FR_FINDNEXT : flags
608        val FR_HIDEMATCHCASE : flags
609        val FR_HIDEUPDOWN : flags
610        val FR_HIDEWHOLEWORD : flags
611        val FR_MATCHCASE : flags
612        val FR_NOMATCHCASE : flags
613        val FR_NOUPDOWN : flags
614        val FR_NOWHOLEWORD : flags
615        val FR_REPLACE : flags
616        val FR_REPLACEALL : flags
617        val FR_SHOWHELP : flags
618        val FR_WHOLEWORD : flags
619        val cFindReplaceFlags: flags conversion
620    end =
621    struct
622        open Word32
623        type flags = word
624        val toWord = toLargeWord
625        and fromWord = fromLargeWord
626        val flags = List.foldl (fn (a, b) => orb(a,b)) 0w0
627        fun allSet (fl1, fl2) = andb(fl1, fl2) = fl1
628        fun anySet (fl1, fl2) = andb(fl1, fl2) <> 0w0
629        fun clear (fl1, fl2) = andb(notb fl1, fl2)
630
631        val FR_DOWN                       = 0wx00000001
632        val FR_WHOLEWORD                  = 0wx00000002
633        val FR_MATCHCASE                  = 0wx00000004
634        val FR_FINDNEXT                   = 0wx00000008
635        val FR_REPLACE                    = 0wx00000010
636        val FR_REPLACEALL                 = 0wx00000020
637        val FR_DIALOGTERM                 = 0wx00000040
638        val FR_SHOWHELP                   = 0wx00000080
639        val FR_NOUPDOWN                   = 0wx00000400
640        val FR_NOMATCHCASE                = 0wx00000800
641        val FR_NOWHOLEWORD                = 0wx00001000
642        val FR_HIDEUPDOWN                 = 0wx00004000
643        val FR_HIDEMATCHCASE              = 0wx00008000
644        val FR_HIDEWHOLEWORD              = 0wx00010000
645
646        val all = flags[FR_DOWN, FR_WHOLEWORD, FR_MATCHCASE, FR_FINDNEXT, FR_REPLACE,
647                        FR_REPLACEALL, FR_DIALOGTERM, FR_NOUPDOWN, FR_NOMATCHCASE,
648                        FR_NOWHOLEWORD, FR_HIDEUPDOWN, FR_HIDEMATCHCASE, FR_HIDEWHOLEWORD]
649
650        val intersect = List.foldl (fn (a, b) => andb(a,b)) all
651        
652        val cFindReplaceFlags = cDWORDw
653    end
654
655    (* The class "string" may be a name or an atom. *)
656    datatype ClassType = NamedClass of string | ClassAtom of int
657
658    local
659        open Memory
660        val {store=storeS, load=loadS, ctype} = breakConversion cString
661
662        fun storeClass(m, ClassAtom i) =
663            if i >= 0 andalso i < 0xC000
664            then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ())
665            else raise Fail "atom out of range"
666        |   storeClass(m, NamedClass s) = storeS(m, s)
667
668        fun loadClass m =
669        let
670            val v = getAddress(m, 0w0)
671        in
672            if voidStar2Sysword v < 0wxC000
673            then ClassAtom(SysWord.toInt(voidStar2Sysword v))
674            else NamedClass(loadS m)
675        end
676
677    in
678        val cCLASS = makeConversion { load = loadClass, store = storeClass, ctype = ctype }
679    end
680
681    (* Clipboard formats.  I've added CF_NONE, CF_PRIVATE, CF_GDIOBJ and CF_REGISTERED.
682       This is here because it is used in both Clipboard and Message (WM_RENDERFORMAT) *)
683    datatype ClipboardFormat =
684        CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF |
685        CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT |
686        CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT |
687        CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int |
688        CF_HDROP | CF_LOCALE
689
690    local
691        val tab = [
692            (CF_NONE,                  0),
693            (CF_TEXT,                  1),
694            (CF_BITMAP,                2),
695            (CF_METAFILEPICT,          3),
696            (CF_SYLK,                  4),
697            (CF_DIF,                   5),
698            (CF_TIFF,                  6),
699            (CF_OEMTEXT,               7),
700            (CF_DIB,                   8),
701            (CF_PALETTE,               9),
702            (CF_PENDATA,               10),
703            (CF_RIFF,                  11),
704            (CF_WAVE,                  12),
705            (CF_UNICODETEXT,           13),
706            (CF_ENHMETAFILE,           14),
707            (CF_HDROP,                 15),
708            (CF_LOCALE,                16),
709            (CF_OWNERDISPLAY,          0x0080),
710            (CF_DSPTEXT,               0x0081),
711            (CF_DSPBITMAP,             0x0082),
712            (CF_DSPMETAFILEPICT,       0x0083),
713            (CF_DSPENHMETAFILE,        0x008E)
714            ]
715        fun toInt (CF_PRIVATE i) =
716                if i >= 0 andalso i < 0xff then 0x0200 + i else raise Size
717        |   toInt (CF_GDIOBJ i) =
718                if i >= 0 andalso i < 0xff then 0x0300 + i else raise Size
719        |   toInt (CF_REGISTERED i) = i
720        |   toInt _ = raise Match
721
722        fun fromInt i =
723            if i >= 0x0200 andalso i <= 0x02ff then CF_PRIVATE(i-0x0200)
724            else if i >= 0x0300 andalso i <= 0x03ff then CF_GDIOBJ(i-0x0300)
725            else if i >= 0xC000 andalso i < 0xFFFF then CF_REGISTERED i
726            else raise Match
727    in
728        val clipLookup = tableLookup (tab, SOME(fromInt, toInt))
729    end
730
731    (* Resources may be specified by strings or by ints. *)
732    datatype RESID = IdAsInt of int | IdAsString of string
733
734    local
735        open Memory
736        val {store=storeS, load=loadS, ctype} = breakConversion cString
737
738        fun storeResid(m, IdAsInt i) =
739            if i >= 0 andalso i < 65536
740            then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ())
741            else raise Fail "resource id out of range"
742        |   storeResid(m, IdAsString s) = storeS(m, s)
743
744        fun loadResid m =
745        let
746            val v = getAddress(m, 0w0)
747        in
748            if voidStar2Sysword v < 0w65536
749            then IdAsInt(SysWord.toInt(voidStar2Sysword v))
750            else IdAsString(loadS m)
751        end
752    in
753        val cRESID =
754            makeConversion { load = loadResid, store = storeResid, ctype = ctype }
755    end
756
757    (*datatype HelpContext =
758        HelpInfo_MenuItem of
759    |   HelpInfo_Window of
760
761    type HELPINFO = {
762    }*)
763
764
765    (* Useful conversions. *)
766    (* Various functions return zero if error.  This conversion checks for that. *)
767    fun cPOSINT _ =
768        absConversion {abs = fn 0 => raiseSysErr() | n => n, rep = fn i => i} cInt
769
770    (* Conversion between string option and C strings.  NONE is converted to NULL. *)
771    val STRINGOPT = cOptionPtr cString
772
773    (* Convert a C string to ML. *)
774    fun fromCstring buff =
775    let
776        open Memory
777        (* We can't use #load cString because the argument is the address of
778           the address of the string. *)
779        fun sLen i = if get8(buff, i) = 0w0 then i else sLen(i+0w1)
780        val length = sLen 0w0
781        fun loadChar i =
782            Char.chr(Word8.toInt(get8(buff, Word.fromInt i)))
783    in
784        CharVector.tabulate(Word.toInt length, loadChar)
785    end
786
787    (* Copy a string to a particular offset in a buffer and
788       add a null terminator. *)
789    fun copyStringToMem (buf, n, s) =
790    let
791        open Memory
792        infix 6 ++
793        fun copyToBuf (i, v) = set8(buf, Word.fromInt(i+n), Byte.charToByte v)
794    in
795        CharVector.appi copyToBuf s;
796        set8(buf, Word.fromInt(n + size s), 0w0)
797    end
798
799    fun toCstring s =
800    let
801        open Memory
802        val sLen = Word.fromInt(String.size s)
803        val sMem = malloc(sLen + 0w1)
804        val () = copyStringToMem(sMem, 0, s)
805    in
806        sMem
807    end
808
809    (* When getting a string it is often the case that passing NULL returns the
810       length required.  Then a second call will actually retrieve the string. *)
811    fun getStringWithNullIsLength(f: Memory.voidStar*int -> int): string =
812    let
813        open Memory
814        val realLength = f(null, 0)
815        val buff = malloc (Word.fromInt(realLength+1))
816        val _ = f(buff, realLength) handle ex => (free buff; raise ex)
817    in
818        fromCstring buff before free buff
819    end
820
821    (* In several cases when extracting a string it is not possible in advance
822       to know how big to make the buffer.  This function loops until all the
823       string has been extracted. *)
824    (* This is at least needed for GetClassName *)
825    fun getStringCall(f: Memory.voidStar*int -> int): string =
826    let
827        open Memory
828        
829        fun doCall initialSize =
830        let
831            (* Allocate a buffer to receive the result.  For safety we make it
832               one character longer than we actually say because it's not always
833               clear whether the length we pass is the size including the NULL.
834               Equally we are only certain we have read the whole string if
835               the return value is less than initialSize-1 because the return
836               value could be the number of real characters copied to the buffer. *)
837            val buff = malloc (Word.fromInt(initialSize+1))
838            val resultSize =
839                f(buff, initialSize) handle ex => (free buff; raise ex)
840        in
841            if resultSize < initialSize-1
842            then (* We've got it all. *)
843                fromCstring buff before free buff
844            else ( free buff; doCall(initialSize + initialSize div 2) )
845        end
846    in
847        doCall (*1024*) 3 (* Use a small size initially for testing. *)
848    end
849
850    (* We have a number of calls that extract a vector of results.  They
851       are called with an initial size, set the vector to the results and
852       return a count of the number actually assigned.  *)
853    fun getVectorResult(element: 'a conversion) =
854    let
855        val { load=loadElem, ctype={size=sizeElem, ...}, ...} = breakConversion element
856        fun run f initialCount =
857        let
858            open Memory
859            infix 6 ++ --
860            val vec = malloc(Word.fromInt initialCount * sizeElem)
861            fun getElement i = loadElem(vec ++ Word.fromInt i * sizeElem)
862            val resultCount =
863                f (vec, initialCount) handle ex => (free vec; raise ex)
864        in
865            Vector.tabulate(resultCount, getElement) before free vec
866        end
867    in
868        run 
869    end
870
871    (* Some C functions take a vector of values to allow a variable number of
872       elements to be passed.  We use a list for this in ML. *)
873    (* TODO: This discards the result of any store function so if we
874       store strings we'll leak store. *)
875    fun list2Vector (conv: 'a conversion) (l:'a list): Memory.voidStar * int =
876    let
877        val count = List.length l
878        val {store=storea, ctype={size=sizea, ...}, ...} = breakConversion conv
879        open Memory
880        infix 6 ++
881        val vec = malloc(Word.fromInt count * sizea)
882        fun setItem(item, v) = (ignore(storea(v, item)); v ++ sizea)
883        val _ = List.foldl setItem vec l 
884    in
885        (vec, count)
886    end
887
888    val GlobalAlloc = winCall2 (kernel "GlobalAlloc") (cInt, cSIZE_T) cHGLOBAL
889    val GlobalLock = winCall1 (kernel "GlobalLock") (cHGLOBAL) cPointer
890    val GlobalFree = winCall1 (kernel "GlobalFree") (cHGLOBAL) cHGLOBAL
891    val GlobalSize = winCall1 (kernel "GlobalSize") (cHGLOBAL) cSIZE_T
892    val GlobalUnlock = winCall1 (kernel "GlobalUnlock") (cHGLOBAL) cBool
893
894    (* Conversion for Word8Vector.  We can't do this as a general conversion because
895       we can't find out how big the C vector is. *)
896    fun fromCWord8vec (buff, length) =
897        Word8Vector.tabulate(length, fn i => Memory.get8(buff, Word.fromInt i))
898
899    fun toCWord8vec(s: Word8Vector.vector): Memory.voidStar =
900    let
901        open Memory Word8Vector
902        val sLen = Word.fromInt(length s)
903        val sMem = malloc sLen
904        val () = appi(fn(i, b) => set8(sMem, Word.fromInt i, b)) s
905    in
906        sMem
907    end
908    
909(*
910    (* Conversion for a fixed size byte array. *)
911    fun BYTEARRAY n =
912    let
913        val base = Cstruct (List.tabulate (n, fn _ => Cchar))
914        fun from v = toWord8vec(address v, n)
915        fun to w =
916            if Word8Vector.length w <> n then raise Size else deref(fromWord8vec w)
917    in
918        mkConversion from to base
919    end *)
920
921    (* Conversion for a fixed size char array. *)
922    fun cCHARARRAY n : string conversion =
923    let
924        (* Make it a struct of chars *)
925        val { size=sizeC, align=alignC, ffiType=ffiTypeC } = LowLevel.cTypeChar
926        val arraySize = sizeC * Word.fromInt n
927        fun ffiType () =
928            LibFFI.createFFItype {
929                size = arraySize, align = alignC, typeCode=LibFFI.ffiTypeCodeStruct,
930                elements = List.tabulate (n, fn _ => ffiTypeC()) }
931        val arrayType: LowLevel.ctype =
932            { size = arraySize, align = alignC, ffiType = ffiType }
933
934        open Memory
935
936        fun load(v: voidStar): string =
937        let
938            (* It should be null-terminated but just in case... *)
939            fun sLen i = if i = Word.fromInt n orelse get8(v, i) = 0w0 then i else sLen(i+0w1)
940            val length = sLen 0w0
941            fun loadChar i =
942                Char.chr(Word8.toInt(get8(v, Word.fromInt i)))
943        in
944            CharVector.tabulate(Word.toInt length, loadChar)
945        end
946
947        fun store(v: voidStar, s: string) =
948        let
949            (* The length must be less than the size to allow for the null *)
950            val sLen = size s
951            val _ = sLen < n orelse raise Fail "string too long"
952        in
953            CharVector.appi(fn(i, ch) => set8(v, Word.fromInt i, Word8.fromInt(Char.ord ch))) s;
954            set8(v, Word.fromInt sLen, 0w0);
955            fn () => ()
956        end
957    in
958        makeConversion { load = load, store = store, ctype = arrayType }
959    end
960
961    (* These should always be UNSIGNED values. *)
962    local
963        open Word32
964        infix << >> orb andb
965        val w32ToW = Word.fromLargeWord o Word32.toLargeWord
966        and wTow32 = Word32.fromLargeWord o Word.toLargeWord
967    in
968        fun LOWORD(l) = w32ToW(l andb 0wxFFFF)
969        fun HIWORD(l) = w32ToW((l >> 0w16) andb 0wxFFFF)
970    
971        fun MAKELONG(a, b) = (wTow32 b << 0w16) orb (wTow32 a andb 0wxFFFF)
972    end
973
974    local
975        open Word
976        infix << >> orb andb
977        val wToW8 = Word8.fromLargeWord o Word.toLargeWord
978    in
979        fun HIBYTE(w) = wToW8((w >> 0w8) andb 0wxFF)
980        fun LOBYTE(w) = wToW8(w andb 0wxFF)
981    end
982
983    (* Convert between strings and vectors containing Unicode characters.
984       N.B.  These are not null terminated. *)
985    local
986        val CP_ACP = 0 (* Default *)
987        val WideCharToMultiByte = winCall8 (kernel "WideCharToMultiByte")
988            (cUint, cDWORD, cByteArray, cInt, cPointer, cInt, cPointer, cPointer) cInt
989        val MultiByteToWideChar =
990            winCall6 (kernel "MultiByteToWideChar") (cUint, cDWORD, cString, cInt, cPointer, cInt) cInt
991    in
992        fun unicodeToString(w: Word8Vector.vector): string =
993        let
994            open Memory
995            val inputLength = Word8Vector.length w  div 2 (* Number of unicode chars *)
996            val outputLength =
997                WideCharToMultiByte(CP_ACP, 0, w, inputLength, null, 0, null, null)
998            val outputBuf = malloc(Word.fromInt outputLength)
999
1000            val conv = WideCharToMultiByte(CP_ACP, 0, w, inputLength, outputBuf, outputLength, null, null)
1001
1002            fun loadChar i =
1003                Char.chr(Word8.toInt(get8(outputBuf, Word.fromInt i)))
1004        in
1005            (* We can't use fromCstring here because it's not necessarily null terminated. *)
1006            CharVector.tabulate(conv, loadChar) before free outputBuf
1007        end
1008
1009        fun stringToUnicode(s: string): Word8Vector.vector =
1010        let
1011            open Memory
1012            val inputLength = size s (* This does not include a terminating NULL *)
1013            (* The lengths returned by MultiByteToWideChar are the number of Unicode chars *)
1014            val outputLength = MultiByteToWideChar(CP_ACP, 0, s, inputLength, null, 0)
1015            val outputBuf = malloc(Word.fromInt outputLength * 0w2)
1016            val conv = MultiByteToWideChar(CP_ACP, 0, s, inputLength, outputBuf, outputLength)
1017            fun loadByte i = get8(outputBuf, Word.fromInt i)
1018        in
1019            Word8Vector.tabulate(conv*2, loadByte) before free outputBuf
1020        end
1021    end
1022
1023end;
1024