File: Base.sml

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (1023 lines) | stat: -rw-r--r-- 42,782 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
(*
    Copyright (c) 2001, 2015
        David C.J. Matthews

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)
(* This contains various types and other values which are needed in various
   modules.  All the exported types are contained in other structures. *)
structure Base:
sig
    val winCall0: Foreign.symbol -> unit -> 'a Foreign.conversion -> unit -> 'a
    val winCall1: Foreign.symbol -> 'a Foreign.conversion -> 'b Foreign.conversion -> 'a -> 'b
    val winCall2: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion -> 'c Foreign.conversion -> 'a * 'b -> 'c
    val winCall3: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion -> 'd Foreign.conversion -> 'a * 'b * 'c -> 'd
    val winCall4: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion -> 'e Foreign.conversion ->
            'a * 'b * 'c * 'd -> 'e
    val winCall5:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion *  'e Foreign.conversion ->
            'f Foreign.conversion -> 'a * 'b * 'c * 'd * 'e -> 'f
    val winCall6:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion -> 'g Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f -> 'g
    val winCall7:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion -> 'h Foreign.conversion ->
             'a * 'b * 'c * 'd * 'e * 'f * 'g -> 'h
    val winCall8:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion -> 'i Foreign.conversion ->
             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h -> 'i
    val winCall9:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion ->
             'j Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i -> 'j
    val winCall10:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion ->
             'k Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j -> 'k
    val winCall11:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion ->
             'l Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k -> 'l
    val winCall12:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
             'l Foreign.conversion -> 'm Foreign.conversion ->
             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l -> 'm
    val winCall13:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
             'l Foreign.conversion * 'm Foreign.conversion -> 'n Foreign.conversion ->
             'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm -> 'n
    val winCall14:
        Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion *
             'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion *
             'l Foreign.conversion * 'm Foreign.conversion * 'n Foreign.conversion ->
            'o Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm * 'n -> 'o
            
    val winAbi: Foreign.LibFFI.abi

    val kernel: string -> Foreign.symbol
    and user: string -> Foreign.symbol
    and commdlg: string -> Foreign.symbol
    and gdi: string -> Foreign.symbol
    and shell: string -> Foreign.symbol
    and comctl: string -> Foreign.symbol
    
    val cSIZE_T: int Foreign.conversion
    and cLPARAM: int Foreign.conversion
    and cLONG_PTR: int Foreign.conversion
    and cULONG_PTR: int Foreign.conversion
    and cINT_PTR: int Foreign.conversion
    and cUINT_PTR: int Foreign.conversion
    and cDWORD: int Foreign.conversion
    and cWORD: int Foreign.conversion
    and cDWORD_PTR: int Foreign.conversion
    and cUINT_PTRw: SysWord.word Foreign.conversion

    val cUint8w: Word8.word Foreign.conversion
    and cUint16w: Word.word Foreign.conversion
    and cUint32w: Word32.word Foreign.conversion
    and cUintw: Word32.word Foreign.conversion
    and cUlongw: Word32.word Foreign.conversion

    val cDWORDw: Word32.word Foreign.conversion
    and cWORDw: Word.word Foreign.conversion

    val cBool: bool Foreign.conversion
    
    val successState: string -> unit Foreign.conversion
    val cPOSINT: string -> int Foreign.conversion
    
    type POINT = { x: int, y: int }
    val cPoint: POINT Foreign.conversion
    type RECT =  { left: int, top: int, right: int, bottom: int }
    val cRect: RECT Foreign.conversion
    type SIZE = { cx: int, cy: int }
    val cSize: SIZE Foreign.conversion

    eqtype 'a HANDLE
    val hNull: 'a HANDLE
    val isHNull: 'a HANDLE -> bool
    val handleOfVoidStar: Foreign.Memory.voidStar -> 'a HANDLE
    and voidStarOfHandle: 'a HANDLE -> Foreign.Memory.voidStar

    eqtype HMENU and HDC and HWND and HINSTANCE and HGDIOBJ
    and HDROP and HRSRC and HUPDATE

    val cHGDIOBJ:   HGDIOBJ Foreign.conversion
    and cHDROP:     HDROP Foreign.conversion
    and cHMENU:     HMENU Foreign.conversion
    and cHINSTANCE: HINSTANCE Foreign.conversion
    and cHDC:       HDC Foreign.conversion
    and cHWND:      HWND Foreign.conversion
    val cHMENUOPT:  HMENU option Foreign.conversion
    and cHGDIOBJOPT: HGDIOBJ option Foreign.conversion
    and cHWNDOPT: HWND option Foreign.conversion
    and cHRSRC: HRSRC Foreign.conversion
    and cHUPDATE: HUPDATE Foreign.conversion

    val hgdiObjNull:HGDIOBJ 
    and isHgdiObjNull: HGDIOBJ -> bool
    and hdcNull: HDC
    and isHdcNull: HDC -> bool
    and hmenuNull: HMENU
    and isHmenuNull: HMENU -> bool
    and hinstanceNull: HINSTANCE
    and isHinstanceNull: HINSTANCE -> bool
    and hwndNull: HWND

    type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ
    and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ
    and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ

    val cHPALETTE: HPALETTE Foreign.conversion
    and cHFONT: HFONT Foreign.conversion
    and cHPEN: HPEN Foreign.conversion
    and cHBITMAP: HBITMAP Foreign.conversion
    and cHRGN: HRGN Foreign.conversion
    and cHBRUSH: HBRUSH Foreign.conversion
    and cHENHMETAFILE: HENHMETAFILE Foreign.conversion
    and cHMETAFILE: HMETAFILE Foreign.conversion

    
    type HICON = HGDIOBJ and HCURSOR = HGDIOBJ
    val cHICON: HICON Foreign.conversion
    and cHCURSOR: HCURSOR Foreign.conversion
    
    val absConversion:
        {abs: 'a -> 'b, rep: 'b -> 'a} -> 'a Foreign.conversion -> 'b Foreign.conversion

    val tableLookup:
        (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option -> (''a -> ''b) * (''b -> ''a)
    and tableSetLookup:
        (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option ->
            (''a list -> Word32.word) * (Word32.word -> ''a list)

    val tableConversion:
        (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option ->
            ''b Foreign.conversion -> ''a Foreign.conversion
    (* tableSetConversion is always a cUint *)
    and tableSetConversion:
        (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option ->
            ''a list Foreign.conversion
    
    val list2Vector: 'a Foreign.conversion -> 'a list -> Foreign.Memory.voidStar * int
    
    datatype ClassType = NamedClass of string | ClassAtom of int
    val cCLASS: ClassType Foreign.conversion

    datatype ClipboardFormat =
        CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF |
        CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT |
        CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT |
        CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int |
        CF_HDROP | CF_LOCALE
    val clipLookup: (ClipboardFormat -> int) * (int -> ClipboardFormat)
    
    datatype RESID = IdAsInt of int | IdAsString of string
    val cRESID: RESID Foreign.conversion
    
    val STRINGOPT: string option Foreign.conversion
    val cCHARARRAY: int -> string Foreign.conversion
    val fromCstring: Foreign.Memory.voidStar -> string
    val toCstring: string -> Foreign.Memory.voidStar (* Memory must be freed *)
    val copyStringToMem: Foreign.Memory.voidStar * int * string -> unit
    val fromCWord8vec: Foreign.Memory.voidStar * int -> Word8Vector.vector
    val toCWord8vec: Word8Vector.vector -> Foreign.Memory.voidStar (* Memory must be freed *)
    
    val getStringCall: (Foreign.Memory.voidStar * int -> int) -> string
    val getStringWithNullIsLength: (Foreign.Memory.voidStar * int -> int) -> string
    val getVectorResult:
        'a Foreign.conversion -> (Foreign.Memory.voidStar * int -> int) -> int -> 'a vector

    eqtype HGLOBAL
    val cHGLOBAL: HGLOBAL Foreign.conversion
    val GlobalAlloc: int * int -> HGLOBAL
    val GlobalLock: HGLOBAL -> Foreign.Memory.voidStar
    val GlobalFree: HGLOBAL -> HGLOBAL
    val GlobalSize: HGLOBAL -> int
    val GlobalUnlock: HGLOBAL -> bool

    val HIWORD: Word32.word -> Word.word
    val LOWORD: Word32.word -> Word.word
    val MAKELONG: Word.word * Word.word -> Word32.word
    val HIBYTE: Word.word -> Word8.word
    val LOBYTE: Word.word -> Word8.word
    
    val unicodeToString: Word8Vector.vector -> string
    val stringToUnicode: string -> Word8Vector.vector
    
    val GetLastError: unit -> OS.syserror
    
    val checkResult: bool -> unit
    val raiseSysErr: unit -> 'a
    
    structure FindReplaceFlags:
    sig
        include BIT_FLAGS
        val FR_DIALOGTERM : flags
        val FR_DOWN : flags
        val FR_FINDNEXT : flags
        val FR_HIDEMATCHCASE : flags
        val FR_HIDEUPDOWN : flags
        val FR_HIDEWHOLEWORD : flags
        val FR_MATCHCASE : flags
        val FR_NOMATCHCASE : flags
        val FR_NOUPDOWN : flags
        val FR_NOWHOLEWORD : flags
        val FR_REPLACE : flags
        val FR_REPLACEALL : flags
        val FR_SHOWHELP : flags
        val FR_WHOLEWORD : flags
        val cFindReplaceFlags: flags Foreign.conversion
    end

end =
struct
    open Foreign
(*    val System_isShort : vol -> bool =
        RunCall.run_call1 RuntimeCalls.POLY_SYS_is_short*)

    fun absConversion {abs: 'a -> 'b, rep: 'b -> 'a} (c: 'a conversion) : 'b conversion =
    let
        val { load=loadI, store=storeI, ctype } = breakConversion c
        fun load m = abs(loadI m)
        fun store(m, v) = storeI(m, rep v)
    in
        makeConversion { load = load, store = store, ctype = ctype }
    end

    (* In many cases we can pass a set of options as a bit set. *)
    (*
    fun bitsetConversion {abs, rep} =
    let
        val (fromC, toC, Ctype) = breakConversion INT
        val fromList = List.foldl (fn(i, n) => IntInf.orb(rep i, n)) 0
        fun toList n = [abs n] (* This is a bit of a mess. *)
    in
        mkConversion (toList o fromCuint) (toCuint o fromList) Cuint
    end*)

    (* Conversions between Word/Word32/LargeWord etc. *)
    local
        open Memory LowLevel
        fun noFree () = ()
    in
        local
            fun load(m: voidStar): Word8.word = get8(m, 0w0)
            fun store(m: voidStar, i: Word8.word) = (set8(m, 0w0, i); noFree)
        in
            val cUint8w: Word8.word conversion =
                makeConversion{ load=load, store=store, ctype = cTypeUint8 }
        end
        local
            fun load(m: voidStar): Word.word = get16(m, 0w0)
            fun store(m: voidStar, i: Word.word) = (set16(m, 0w0, i); noFree)
        in
            val cUint16w: Word.word conversion =
                makeConversion{ load=load, store=store, ctype = cTypeInt16 }
        end
        local
            fun load(m: voidStar): Word32.word = get32(m, 0w0)
            fun store(m: voidStar, i: Word32.word) = (set32(m, 0w0, i); noFree)
        in
            val cUint32w: Word32.word conversion =
                makeConversion{ load=load, store=store, ctype = cTypeUint32 }
            
        end
        val cUintw = cUint32w
        (* Int should be 32-bits on Windows. *)
        val _ = #size LowLevel.cTypeUint = #size LowLevel.cTypeUint32
                    orelse raise Fail "unsigned int is not 32-bits"
        val cUlongw = cUint32w
        val _ = #size LowLevel.cTypeUlong = #size LowLevel.cTypeUint32
                    orelse raise Fail "unsigned long is not 32-bits"
    end

    val cDWORD = cUint32 (* Defined to be 32-bit unsigned *)
    and cWORD = cUint16 (* Defined to be 16-bit unsigned *)
    
    val cDWORDw = cUint32w
    and cWORDw = cUint16w
    
    (* For some reason Windows has both INT_PTR and LONG_PTR and they
       are slightly different. *)
    val cLONG_PTR =
        if #size LowLevel.cTypePointer = 0w4
        then cLong
        else cInt64
    
    val cINT_PTR =
        if #size LowLevel.cTypePointer = 0w4
        then cInt
        else cInt64

    val cULONG_PTR =
        if #size LowLevel.cTypePointer = 0w4
        then cUlong
        else cUint64

    val cUINT_PTR =
        if #size LowLevel.cTypePointer = 0w4
        then cUint
        else cUint64

    val cLPARAM = cLONG_PTR
    val cSIZE_T = cULONG_PTR (* Probably. *)
    val cDWORD_PTR = cULONG_PTR (* Defined to be the same so I'm not sure why it's there .*)
    
    val cUINT_PTRw = absConversion{abs=Memory.voidStar2Sysword, rep=Memory.sysWord2VoidStar} cPointer

    (* These are called XXX32.DLL on both 32-bit and 64-bit. *)
    fun kernel name = getSymbol(loadLibrary "kernel32.dll") name
    and user sym = getSymbol(loadLibrary "user32.DLL") sym
    and commdlg sym = getSymbol(loadLibrary "comdlg32.DLL") sym
    and gdi sym = getSymbol(loadLibrary "gdi32.DLL") sym
    and shell sym = getSymbol(loadLibrary "shell32.DLL") sym
    and comctl sym = getSymbol(loadLibrary "comctl32.DLL") sym

    (* We need to use the Pascal calling convention on 32-bit Windows. *)
    val winAbi =
        case List.find (fn ("stdcall", _) => true | _ => false) LibFFI.abiList of
            SOME(_, abi) => abi
        |   NONE => LibFFI.abiDefault

    (* As well as setting the abi we can also use the old argument order. *)
    fun winCall0 sym argConv resConv = buildCall0withAbi(winAbi, sym, argConv, resConv)
    and winCall1 sym argConv resConv = buildCall1withAbi(winAbi, sym, argConv, resConv)
    and winCall2 sym argConv resConv = buildCall2withAbi(winAbi, sym, argConv, resConv)
    and winCall3 sym argConv resConv = buildCall3withAbi(winAbi, sym, argConv, resConv)
    and winCall4 sym argConv resConv = buildCall4withAbi(winAbi, sym, argConv, resConv)
    and winCall5 sym argConv resConv = buildCall5withAbi(winAbi, sym, argConv, resConv)
    and winCall6 sym argConv resConv = buildCall6withAbi(winAbi, sym, argConv, resConv)
    and winCall7 sym argConv resConv = buildCall7withAbi(winAbi, sym, argConv, resConv)
    and winCall8 sym argConv resConv = buildCall8withAbi(winAbi, sym, argConv, resConv)
    and winCall9 sym argConv resConv = buildCall9withAbi(winAbi, sym, argConv, resConv)
    and winCall10 sym argConv resConv = buildCall10withAbi(winAbi, sym, argConv, resConv)
    and winCall11 sym argConv resConv = buildCall11withAbi(winAbi, sym, argConv, resConv)
    and winCall12 sym argConv resConv = buildCall12withAbi(winAbi, sym, argConv, resConv)
    and winCall13 sym argConv resConv = buildCall13withAbi(winAbi, sym, argConv, resConv)
    and winCall14 sym argConv resConv = buildCall14withAbi(winAbi, sym, argConv, resConv)

    (* Previously we had a specific call to do this.  The error state is
       no longer set by the new FFI. *)
(*
    fun GetLastError(): OS.syserror =
        RunCall.run_call2 RuntimeCalls.POLY_SYS_os_specific (1100, ())
*)
    local
        val getLastError = winCall0 (kernel "GetLastError") () cDWORD
    in
        fun GetLastError(): OS.syserror =
            (* Windows error codes are negative values in OS.syserror. *)
            RunCall.unsafeCast (~ (getLastError()))
    end

    (* The string argument of the SysErr exception is supposed to match the result of OS.errMsg. *)
    fun raiseSysErr () = let val err = GetLastError() in raise OS.SysErr(OS.errorMsg err, SOME err) end

    (* Many system calls return bool.  If the result is false we raise an exception. *)
    fun checkResult true = () | checkResult false = raiseSysErr ()
    
    val cBool: bool conversion =
        absConversion{abs = fn 0 => false | _ => true, rep = fn false => 0 | true => 1} cInt

    fun successState name: unit conversion =
         absConversion { abs = checkResult, rep = fn _ => raise Fail ("successState:" ^ name) } cBool


    type POINT = { x: int, y: int }

    local
        fun breakPoint ({x,y}: POINT) = (x,y)
        fun mkPoint (x,y): POINT = {x=x, y=y}
    in
        val cPoint = absConversion {abs=mkPoint, rep=breakPoint} (cStruct2 (cLong, cLong))
    end

    type RECT =  { left: int, top: int, right: int, bottom: int }

    local
        fun breakRect ({left,top,right,bottom}: RECT) = (left,top,right,bottom)
        fun mkRect (left,top,right,bottom): RECT =
            {left=left,top=top,right=right,bottom=bottom}
    in
        val cRect = absConversion {abs=mkRect, rep=breakRect} (cStruct4 (cLong,cLong,cLong,cLong))
    end

    type SIZE = { cx: int, cy: int }
    local
        fun breakSize ({cx,cy}: SIZE) = (cx,cy)
        fun mkSize (cx,cy): SIZE = {cx=cx, cy=cy}
    in
        val cSize = absConversion {abs=mkSize, rep=breakSize} (cStruct2 (cLong,cLong))
    end

    (* Handles are generally opaque values.  We want them to be eqtypes, though. *)
    local
        structure HandStruct :>
            sig
                eqtype 'a HANDLE
                val hNull: 'a HANDLE
                val isHNull: 'a HANDLE -> bool
                val handleOfVoidStar: Memory.voidStar -> 'a HANDLE
                and voidStarOfHandle: 'a HANDLE -> Memory.voidStar
            end =
        struct
            type 'a HANDLE = Memory.voidStar
            val hNull = Memory.null
            fun isHNull h = h = hNull
        
            (* We sometimes need the next two functions internally.
               They're needed externally unless we change the result type
               of SendMessage to allow us to return a handle for certain
               messages. *)
            fun handleOfVoidStar h = h
            and voidStarOfHandle h = h
        end
    in
        open HandStruct
    end

    (* We just need these as placeholders. We never create values of
       these types.  They are used simply as a way of creating different
       handle types. *)
    (* Don't use abstype - we want them to eqtypes *)
    datatype GdiObj = GdiObj
    and Instance = Instance
    and Drop = Drop
    and DeviceContext = DeviceContext
    and Menu = Menu
    and Window = Window
    and Global = Global
    and Src = Src
    and Update = Update

    (* HINSTANCE is used as an instance of a module. *)
    type HINSTANCE = Instance HANDLE
    and  HDROP = Drop HANDLE
    and  HGDIOBJ = GdiObj HANDLE
    and  HDC = DeviceContext HANDLE
    and  HMENU = Menu HANDLE
    and  HWND = Window HANDLE
    and  HGLOBAL = Global HANDLE
    and  HRSRC = Src HANDLE
    and  HUPDATE = Update HANDLE

    local
        fun cHANDLE() =
            absConversion {abs=handleOfVoidStar, rep=voidStarOfHandle} cPointer
        fun hoptOfvs n =
            if Memory.voidStar2Sysword n = 0w0 then NONE else SOME(handleOfVoidStar n)
        
        fun cHANDLEOPT() =
            absConversion {abs=hoptOfvs, rep=fn v => voidStarOfHandle(getOpt(v, hNull)) } cPointer
    in
        val cHGDIOBJ:   HGDIOBJ conversion = cHANDLE()
        and cHDROP:     HDROP conversion = cHANDLE()
        and cHMENU:     HMENU conversion = cHANDLE()
        and cHINSTANCE: HINSTANCE conversion = cHANDLE()
        and cHDC:       HDC conversion = cHANDLE()
        and cHWND:      HWND conversion = cHANDLE()

        val cHMENUOPT:  HMENU option conversion = cHANDLEOPT()
        and cHGDIOBJOPT: HGDIOBJ option conversion = cHANDLEOPT()
        and cHWNDOPT: HWND option conversion = cHANDLEOPT()
        
        val cHGLOBAL: HGLOBAL conversion = cHANDLE()
        and cHRSRC: HRSRC conversion = cHANDLE()
        and cHUPDATE: HUPDATE conversion = cHANDLE()
    end

    (* Temporary declarations. *)
    val hgdiObjNull:HGDIOBJ  = hNull
    and isHgdiObjNull: HGDIOBJ -> bool = isHNull
    and hdcNull: HDC = hNull
    and isHdcNull: HDC -> bool = isHNull
    and hmenuNull: HMENU = hNull
    and isHmenuNull: HMENU -> bool = isHNull
    and hinstanceNull: HINSTANCE = hNull
    and isHinstanceNull: HINSTANCE -> bool = isHNull
    and hwndNull: HWND = hNull

    (* All these are various kinds of HGDIOBJ.  It's too complicated to try
       to use different types for them. *)
    type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ
    and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ
    and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ

    val cHPALETTE: HPALETTE conversion = cHGDIOBJ
    and cHFONT: HFONT conversion = cHGDIOBJ
    and cHPEN: HPEN conversion = cHGDIOBJ
    and cHBITMAP: HBITMAP conversion = cHGDIOBJ
    and cHRGN: HRGN conversion = cHGDIOBJ
    and cHBRUSH: HBRUSH conversion = cHGDIOBJ
    and cHENHMETAFILE: HENHMETAFILE conversion = cHGDIOBJ
    and cHMETAFILE: HMETAFILE conversion = cHGDIOBJ

    (* I'm not so happy about treating these as HGDIOBJ but it makes the
       types of messages such as BM_SETIMAGE simpler. *)
    type HICON = HGDIOBJ and HCURSOR = HGDIOBJ
    val cHICON = cHGDIOBJ and cHCURSOR = cHGDIOBJ

    (* The easiest way to deal with datatypes is often by way of a table. *)
    fun tableLookup (table: (''a * ''b) list, default) =
    let
        fun toInt [] x =
            (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x)
         |  toInt ((y, i) :: tl) x = if x = y then i else toInt tl x

        fun fromInt [] x =
            (case default of
                NONE => raise Fail ("tableLookup: not found")
             |  SOME (d, _) => d x)
         |  fromInt ((y, i) :: tl) x = if x = i then y else fromInt tl x
    in
        (toInt table, fromInt table)
    end

    fun tableConversion (table: (''a * ''b) list, default) (conv: ''b conversion): ''a conversion  =
    let
        val (toInt, fromInt) = tableLookup(table, default)
    in
        absConversion {abs = fromInt, rep = toInt} conv
    end

    (* In other cases we have sets of options.  We represent them by a list.
       The order of the elements in the table is significant if we are to be
       able to handle multiple bits.  Patterns with more than one bit set
       MUST be placed later than those with a subset of those bits. *)
    fun tableSetLookup (table: (''a * Word32.word) list, default) =
    let
        open Word32
        (* Conversion to integer - just fold the values. *)
        fun toInt' [] x =
            (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x)
         |  toInt' ((y, i) :: tl) x = if x = y then i else toInt' tl x

        val toInt = List.foldl (fn (a, b) => orb(toInt' table a, b)) 0w0

        (* It would speed up the searches if we ordered the list so that multiple
           bit entries preceded those with fewer bits but it's much easier to lay
           out the tables if we do it this way. *)
        fun fromInt _ _ 0w0 = [] (* Zero is an empty list. *)

         |  fromInt [] NONE x = (* Not found *)
                (case default of
                    NONE => raise Fail ("tableLookup: not found" ^ Word32.toString x)
                  | SOME (d, _) => [d x])

         |  fromInt [] (SOME(res, bits)) x = (* Found something - remove it from the set. *)
                (res :: fromInt table NONE (andb(x, notb bits)))

         |  fromInt ((res, bits)::tl) sofar x =
                if bits <> 0w0 andalso andb(x, bits) = bits
                then (* Matches *) fromInt tl (SOME(res, bits)) x
                else (* Doesn't match *) fromInt tl sofar x
    in
        (toInt, fromInt table NONE)
    end

    fun tableSetConversion (table: (''a * Word32.word) list, default): ''a list conversion  =
    let
        val (toInt, fromInt) = tableSetLookup(table, default)
    in
        absConversion {abs = fromInt, rep = toInt} cUintw
    end

    
    structure FindReplaceFlags:>
    sig
        include BIT_FLAGS
        val FR_DIALOGTERM : flags
        val FR_DOWN : flags
        val FR_FINDNEXT : flags
        val FR_HIDEMATCHCASE : flags
        val FR_HIDEUPDOWN : flags
        val FR_HIDEWHOLEWORD : flags
        val FR_MATCHCASE : flags
        val FR_NOMATCHCASE : flags
        val FR_NOUPDOWN : flags
        val FR_NOWHOLEWORD : flags
        val FR_REPLACE : flags
        val FR_REPLACEALL : flags
        val FR_SHOWHELP : flags
        val FR_WHOLEWORD : flags
        val cFindReplaceFlags: flags conversion
    end =
    struct
        open Word32
        type flags = word
        val toWord = toLargeWord
        and fromWord = fromLargeWord
        val flags = List.foldl (fn (a, b) => orb(a,b)) 0w0
        fun allSet (fl1, fl2) = andb(fl1, fl2) = fl1
        fun anySet (fl1, fl2) = andb(fl1, fl2) <> 0w0
        fun clear (fl1, fl2) = andb(notb fl1, fl2)

        val FR_DOWN                       = 0wx00000001
        val FR_WHOLEWORD                  = 0wx00000002
        val FR_MATCHCASE                  = 0wx00000004
        val FR_FINDNEXT                   = 0wx00000008
        val FR_REPLACE                    = 0wx00000010
        val FR_REPLACEALL                 = 0wx00000020
        val FR_DIALOGTERM                 = 0wx00000040
        val FR_SHOWHELP                   = 0wx00000080
        val FR_NOUPDOWN                   = 0wx00000400
        val FR_NOMATCHCASE                = 0wx00000800
        val FR_NOWHOLEWORD                = 0wx00001000
        val FR_HIDEUPDOWN                 = 0wx00004000
        val FR_HIDEMATCHCASE              = 0wx00008000
        val FR_HIDEWHOLEWORD              = 0wx00010000

        val all = flags[FR_DOWN, FR_WHOLEWORD, FR_MATCHCASE, FR_FINDNEXT, FR_REPLACE,
                        FR_REPLACEALL, FR_DIALOGTERM, FR_NOUPDOWN, FR_NOMATCHCASE,
                        FR_NOWHOLEWORD, FR_HIDEUPDOWN, FR_HIDEMATCHCASE, FR_HIDEWHOLEWORD]

        val intersect = List.foldl (fn (a, b) => andb(a,b)) all
        
        val cFindReplaceFlags = cDWORDw
    end

    (* The class "string" may be a name or an atom. *)
    datatype ClassType = NamedClass of string | ClassAtom of int

    local
        open Memory
        val {store=storeS, load=loadS, ctype} = breakConversion cString

        fun storeClass(m, ClassAtom i) =
            if i >= 0 andalso i < 0xC000
            then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ())
            else raise Fail "atom out of range"
        |   storeClass(m, NamedClass s) = storeS(m, s)

        fun loadClass m =
        let
            val v = getAddress(m, 0w0)
        in
            if voidStar2Sysword v < 0wxC000
            then ClassAtom(SysWord.toInt(voidStar2Sysword v))
            else NamedClass(loadS m)
        end

    in
        val cCLASS = makeConversion { load = loadClass, store = storeClass, ctype = ctype }
    end

    (* Clipboard formats.  I've added CF_NONE, CF_PRIVATE, CF_GDIOBJ and CF_REGISTERED.
       This is here because it is used in both Clipboard and Message (WM_RENDERFORMAT) *)
    datatype ClipboardFormat =
        CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF |
        CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT |
        CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT |
        CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int |
        CF_HDROP | CF_LOCALE

    local
        val tab = [
            (CF_NONE,                  0),
            (CF_TEXT,                  1),
            (CF_BITMAP,                2),
            (CF_METAFILEPICT,          3),
            (CF_SYLK,                  4),
            (CF_DIF,                   5),
            (CF_TIFF,                  6),
            (CF_OEMTEXT,               7),
            (CF_DIB,                   8),
            (CF_PALETTE,               9),
            (CF_PENDATA,               10),
            (CF_RIFF,                  11),
            (CF_WAVE,                  12),
            (CF_UNICODETEXT,           13),
            (CF_ENHMETAFILE,           14),
            (CF_HDROP,                 15),
            (CF_LOCALE,                16),
            (CF_OWNERDISPLAY,          0x0080),
            (CF_DSPTEXT,               0x0081),
            (CF_DSPBITMAP,             0x0082),
            (CF_DSPMETAFILEPICT,       0x0083),
            (CF_DSPENHMETAFILE,        0x008E)
            ]
        fun toInt (CF_PRIVATE i) =
                if i >= 0 andalso i < 0xff then 0x0200 + i else raise Size
        |   toInt (CF_GDIOBJ i) =
                if i >= 0 andalso i < 0xff then 0x0300 + i else raise Size
        |   toInt (CF_REGISTERED i) = i
        |   toInt _ = raise Match

        fun fromInt i =
            if i >= 0x0200 andalso i <= 0x02ff then CF_PRIVATE(i-0x0200)
            else if i >= 0x0300 andalso i <= 0x03ff then CF_GDIOBJ(i-0x0300)
            else if i >= 0xC000 andalso i < 0xFFFF then CF_REGISTERED i
            else raise Match
    in
        val clipLookup = tableLookup (tab, SOME(fromInt, toInt))
    end

    (* Resources may be specified by strings or by ints. *)
    datatype RESID = IdAsInt of int | IdAsString of string

    local
        open Memory
        val {store=storeS, load=loadS, ctype} = breakConversion cString

        fun storeResid(m, IdAsInt i) =
            if i >= 0 andalso i < 65536
            then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ())
            else raise Fail "resource id out of range"
        |   storeResid(m, IdAsString s) = storeS(m, s)

        fun loadResid m =
        let
            val v = getAddress(m, 0w0)
        in
            if voidStar2Sysword v < 0w65536
            then IdAsInt(SysWord.toInt(voidStar2Sysword v))
            else IdAsString(loadS m)
        end
    in
        val cRESID =
            makeConversion { load = loadResid, store = storeResid, ctype = ctype }
    end

    (*datatype HelpContext =
        HelpInfo_MenuItem of
    |   HelpInfo_Window of

    type HELPINFO = {
    }*)


    (* Useful conversions. *)
    (* Various functions return zero if error.  This conversion checks for that. *)
    fun cPOSINT _ =
        absConversion {abs = fn 0 => raiseSysErr() | n => n, rep = fn i => i} cInt

    (* Conversion between string option and C strings.  NONE is converted to NULL. *)
    val STRINGOPT = cOptionPtr cString

    (* Convert a C string to ML. *)
    fun fromCstring buff =
    let
        open Memory
        (* We can't use #load cString because the argument is the address of
           the address of the string. *)
        fun sLen i = if get8(buff, i) = 0w0 then i else sLen(i+0w1)
        val length = sLen 0w0
        fun loadChar i =
            Char.chr(Word8.toInt(get8(buff, Word.fromInt i)))
    in
        CharVector.tabulate(Word.toInt length, loadChar)
    end

    (* Copy a string to a particular offset in a buffer and
       add a null terminator. *)
    fun copyStringToMem (buf, n, s) =
    let
        open Memory
        infix 6 ++
        fun copyToBuf (i, v) = set8(buf, Word.fromInt(i+n), Byte.charToByte v)
    in
        CharVector.appi copyToBuf s;
        set8(buf, Word.fromInt(n + size s), 0w0)
    end

    fun toCstring s =
    let
        open Memory
        val sLen = Word.fromInt(String.size s)
        val sMem = malloc(sLen + 0w1)
        val () = copyStringToMem(sMem, 0, s)
    in
        sMem
    end

    (* When getting a string it is often the case that passing NULL returns the
       length required.  Then a second call will actually retrieve the string. *)
    fun getStringWithNullIsLength(f: Memory.voidStar*int -> int): string =
    let
        open Memory
        val realLength = f(null, 0)
        val buff = malloc (Word.fromInt(realLength+1))
        val _ = f(buff, realLength) handle ex => (free buff; raise ex)
    in
        fromCstring buff before free buff
    end

    (* In several cases when extracting a string it is not possible in advance
       to know how big to make the buffer.  This function loops until all the
       string has been extracted. *)
    (* This is at least needed for GetClassName *)
    fun getStringCall(f: Memory.voidStar*int -> int): string =
    let
        open Memory
        
        fun doCall initialSize =
        let
            (* Allocate a buffer to receive the result.  For safety we make it
               one character longer than we actually say because it's not always
               clear whether the length we pass is the size including the NULL.
               Equally we are only certain we have read the whole string if
               the return value is less than initialSize-1 because the return
               value could be the number of real characters copied to the buffer. *)
            val buff = malloc (Word.fromInt(initialSize+1))
            val resultSize =
                f(buff, initialSize) handle ex => (free buff; raise ex)
        in
            if resultSize < initialSize-1
            then (* We've got it all. *)
                fromCstring buff before free buff
            else ( free buff; doCall(initialSize + initialSize div 2) )
        end
    in
        doCall (*1024*) 3 (* Use a small size initially for testing. *)
    end

    (* We have a number of calls that extract a vector of results.  They
       are called with an initial size, set the vector to the results and
       return a count of the number actually assigned.  *)
    fun getVectorResult(element: 'a conversion) =
    let
        val { load=loadElem, ctype={size=sizeElem, ...}, ...} = breakConversion element
        fun run f initialCount =
        let
            open Memory
            infix 6 ++ --
            val vec = malloc(Word.fromInt initialCount * sizeElem)
            fun getElement i = loadElem(vec ++ Word.fromInt i * sizeElem)
            val resultCount =
                f (vec, initialCount) handle ex => (free vec; raise ex)
        in
            Vector.tabulate(resultCount, getElement) before free vec
        end
    in
        run 
    end

    (* Some C functions take a vector of values to allow a variable number of
       elements to be passed.  We use a list for this in ML. *)
    (* TODO: This discards the result of any store function so if we
       store strings we'll leak store. *)
    fun list2Vector (conv: 'a conversion) (l:'a list): Memory.voidStar * int =
    let
        val count = List.length l
        val {store=storea, ctype={size=sizea, ...}, ...} = breakConversion conv
        open Memory
        infix 6 ++
        val vec = malloc(Word.fromInt count * sizea)
        fun setItem(item, v) = (ignore(storea(v, item)); v ++ sizea)
        val _ = List.foldl setItem vec l 
    in
        (vec, count)
    end

    val GlobalAlloc = winCall2 (kernel "GlobalAlloc") (cInt, cSIZE_T) cHGLOBAL
    val GlobalLock = winCall1 (kernel "GlobalLock") (cHGLOBAL) cPointer
    val GlobalFree = winCall1 (kernel "GlobalFree") (cHGLOBAL) cHGLOBAL
    val GlobalSize = winCall1 (kernel "GlobalSize") (cHGLOBAL) cSIZE_T
    val GlobalUnlock = winCall1 (kernel "GlobalUnlock") (cHGLOBAL) cBool

    (* Conversion for Word8Vector.  We can't do this as a general conversion because
       we can't find out how big the C vector is. *)
    fun fromCWord8vec (buff, length) =
        Word8Vector.tabulate(length, fn i => Memory.get8(buff, Word.fromInt i))

    fun toCWord8vec(s: Word8Vector.vector): Memory.voidStar =
    let
        open Memory Word8Vector
        val sLen = Word.fromInt(length s)
        val sMem = malloc sLen
        val () = appi(fn(i, b) => set8(sMem, Word.fromInt i, b)) s
    in
        sMem
    end
    
(*
    (* Conversion for a fixed size byte array. *)
    fun BYTEARRAY n =
    let
        val base = Cstruct (List.tabulate (n, fn _ => Cchar))
        fun from v = toWord8vec(address v, n)
        fun to w =
            if Word8Vector.length w <> n then raise Size else deref(fromWord8vec w)
    in
        mkConversion from to base
    end *)

    (* Conversion for a fixed size char array. *)
    fun cCHARARRAY n : string conversion =
    let
        (* Make it a struct of chars *)
        val { size=sizeC, align=alignC, ffiType=ffiTypeC } = LowLevel.cTypeChar
        val arraySize = sizeC * Word.fromInt n
        fun ffiType () =
            LibFFI.createFFItype {
                size = arraySize, align = alignC, typeCode=LibFFI.ffiTypeCodeStruct,
                elements = List.tabulate (n, fn _ => ffiTypeC()) }
        val arrayType: LowLevel.ctype =
            { size = arraySize, align = alignC, ffiType = ffiType }

        open Memory

        fun load(v: voidStar): string =
        let
            (* It should be null-terminated but just in case... *)
            fun sLen i = if i = Word.fromInt n orelse get8(v, i) = 0w0 then i else sLen(i+0w1)
            val length = sLen 0w0
            fun loadChar i =
                Char.chr(Word8.toInt(get8(v, Word.fromInt i)))
        in
            CharVector.tabulate(Word.toInt length, loadChar)
        end

        fun store(v: voidStar, s: string) =
        let
            (* The length must be less than the size to allow for the null *)
            val sLen = size s
            val _ = sLen < n orelse raise Fail "string too long"
        in
            CharVector.appi(fn(i, ch) => set8(v, Word.fromInt i, Word8.fromInt(Char.ord ch))) s;
            set8(v, Word.fromInt sLen, 0w0);
            fn () => ()
        end
    in
        makeConversion { load = load, store = store, ctype = arrayType }
    end

    (* These should always be UNSIGNED values. *)
    local
        open Word32
        infix << >> orb andb
        val w32ToW = Word.fromLargeWord o Word32.toLargeWord
        and wTow32 = Word32.fromLargeWord o Word.toLargeWord
    in
        fun LOWORD(l) = w32ToW(l andb 0wxFFFF)
        fun HIWORD(l) = w32ToW((l >> 0w16) andb 0wxFFFF)
    
        fun MAKELONG(a, b) = (wTow32 b << 0w16) orb (wTow32 a andb 0wxFFFF)
    end

    local
        open Word
        infix << >> orb andb
        val wToW8 = Word8.fromLargeWord o Word.toLargeWord
    in
        fun HIBYTE(w) = wToW8((w >> 0w8) andb 0wxFF)
        fun LOBYTE(w) = wToW8(w andb 0wxFF)
    end

    (* Convert between strings and vectors containing Unicode characters.
       N.B.  These are not null terminated. *)
    local
        val CP_ACP = 0 (* Default *)
        val WideCharToMultiByte = winCall8 (kernel "WideCharToMultiByte")
            (cUint, cDWORD, cByteArray, cInt, cPointer, cInt, cPointer, cPointer) cInt
        val MultiByteToWideChar =
            winCall6 (kernel "MultiByteToWideChar") (cUint, cDWORD, cString, cInt, cPointer, cInt) cInt
    in
        fun unicodeToString(w: Word8Vector.vector): string =
        let
            open Memory
            val inputLength = Word8Vector.length w  div 2 (* Number of unicode chars *)
            val outputLength =
                WideCharToMultiByte(CP_ACP, 0, w, inputLength, null, 0, null, null)
            val outputBuf = malloc(Word.fromInt outputLength)

            val conv = WideCharToMultiByte(CP_ACP, 0, w, inputLength, outputBuf, outputLength, null, null)

            fun loadChar i =
                Char.chr(Word8.toInt(get8(outputBuf, Word.fromInt i)))
        in
            (* We can't use fromCstring here because it's not necessarily null terminated. *)
            CharVector.tabulate(conv, loadChar) before free outputBuf
        end

        fun stringToUnicode(s: string): Word8Vector.vector =
        let
            open Memory
            val inputLength = size s (* This does not include a terminating NULL *)
            (* The lengths returned by MultiByteToWideChar are the number of Unicode chars *)
            val outputLength = MultiByteToWideChar(CP_ACP, 0, s, inputLength, null, 0)
            val outputBuf = malloc(Word.fromInt outputLength * 0w2)
            val conv = MultiByteToWideChar(CP_ACP, 0, s, inputLength, outputBuf, outputLength)
            fun loadByte i = get8(outputBuf, Word.fromInt i)
        in
            Word8Vector.tabulate(conv*2, loadByte) before free outputBuf
        end
    end

end;