1(* 2 Title: Standard Basis Library: Windows signature and structure 3 Author: David Matthews 4 Copyright David Matthews 2000, 2005, 2012 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19*) 20 21signature WINDOWS = 22sig 23 structure Key : 24 sig 25 include BIT_FLAGS 26 val allAccess : flags 27 val createLink : flags 28 val createSubKey : flags 29 val enumerateSubKeys : flags 30 val execute : flags 31 val notify : flags 32 val queryValue : flags 33 val read : flags 34 val setValue : flags 35 val write : flags 36 end 37 structure Reg : 38 sig 39 eqtype hkey 40 val classesRoot : hkey 41 val currentUser : hkey 42 val localMachine : hkey 43 val users : hkey 44 val performanceData : hkey 45 val currentConfig : hkey 46 val dynData : hkey 47 48 datatype create_result = 49 CREATED_NEW_KEY of hkey 50 | OPENED_EXISTING_KEY of hkey 51 val createKeyEx : hkey * string * Key.flags -> create_result 52 val openKeyEx : hkey * string * Key.flags -> hkey 53 val closeKey : hkey -> unit 54 val deleteKey : hkey * string -> unit 55 val deleteValue : hkey * string -> unit 56 val enumKeyEx : hkey * int -> string option 57 val enumValue : hkey * int -> string option 58 datatype value = 59 SZ of string 60 | DWORD of SysWord.word 61 | BINARY of Word8Vector.vector 62 | MULTI_SZ of string list 63 | EXPAND_SZ of string 64 val queryValueEx : hkey * string -> value option 65 val setValueEx : hkey * string * value -> unit 66 end 67 68 structure Config: 69 sig 70 val platformWin32s : SysWord.word 71 val platformWin32Windows : SysWord.word 72 val platformWin32NT : SysWord.word 73 val platformWin32CE : SysWord.word 74 75 val getVersionEx: unit -> 76 { majorVersion: SysWord.word, minorVersion: SysWord.word, 77 buildNumber: SysWord.word, platformId: SysWord.word, 78 csdVersion: string } 79 80 val getWindowsDirectory: unit -> string 81 val getSystemDirectory: unit -> string 82 val getComputerName: unit -> string 83 val getUserName: unit -> string 84 end 85 86 structure DDE : 87 sig 88 type info 89 val startDialog : string * string -> info 90 val executeString : info * string * int * Time.time -> unit 91 val stopDialog : info -> unit 92 end 93 94 val getVolumeInformation : 95 string -> { 96 volumeName : string, 97 systemName : string, 98 serialNumber : SysWord.word, 99 maximumComponentLength : int 100 } 101 102 val findExecutable : string -> string option 103 val launchApplication : string * string -> unit 104 val openDocument : string -> unit 105 val simpleExecute : string * string -> OS.Process.status 106 type ('a,'b) proc 107 val execute : string * string -> ('a, 'b) proc 108 val textInstreamOf : (TextIO.instream, 'a) proc -> TextIO.instream 109 val binInstreamOf : (BinIO.instream, 'a) proc -> BinIO.instream 110 val textOutstreamOf : ('a, TextIO.outstream) proc -> TextIO.outstream 111 val binOutstreamOf : ('a, BinIO.outstream) proc -> BinIO.outstream 112 val reap : ('a, 'b) proc -> OS.Process.status 113 114 structure Status : 115 sig 116 type status = SysWord.word 117 val accessViolation : status 118 val arrayBoundsExceeded : status 119 val breakpoint : status 120 val controlCExit : status 121 val datatypeMisalignment : status 122 val floatDenormalOperand : status 123 val floatDivideByZero : status 124 val floatInexactResult : status 125 val floatInvalidOperation : status 126 val floatOverflow : status 127 val floatStackCheck : status 128 val floatUnderflow : status 129 val guardPageViolation : status 130 val integerDivideByZero : status 131 val integerOverflow : status 132 val illegalInstruction : status 133 val invalidDisposition : status 134 val invalidHandle : status 135 val inPageError : status 136 val noncontinuableException: status 137 val pending : status 138 val privilegedInstruction : status 139 val singleStep : status 140 val stackOverflow : status 141 val timeout : status 142 val userAPC : status 143 end 144 val fromStatus : OS.Process.status -> Status.status 145 val exit : Status.status -> 'a 146 147end 148 149structure Windows :> WINDOWS = 150struct 151 local 152 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 153 in 154 fun getConst i = SysWord.fromInt(winCall (1006, i)) 155 end 156 157 structure Key = 158 struct 159 type flags = SysWord.word 160 fun toWord f = f 161 fun fromWord f = f 162 val flags = List.foldl (fn (a, b) => SysWord.orb(a,b)) 0w0 163 fun allSet (fl1, fl2) = SysWord.andb(fl1, fl2) = fl1 164 fun anySet (fl1, fl2) = SysWord.andb(fl1, fl2) <> 0w0 165 fun clear (fl1, fl2) = SysWord.andb(SysWord.notb fl1, fl2) 166 167 val allAccess : flags = getConst 0 168 val createLink : flags = getConst 1 169 val createSubKey : flags = getConst 2 170 val enumerateSubKeys : flags = getConst 3 171 val execute : flags = getConst 4 172 val notify : flags = getConst 5 173 val queryValue : flags = getConst 6 174 val read : flags = getConst 7 175 val setValue : flags = getConst 8 176 val write : flags = getConst 9 177 178 (* all is probably equivalent to allAccess. *) 179 val all = flags[allAccess, createLink, createSubKey, enumerateSubKeys, 180 execute, notify, queryValue, read, setValue, write] 181 182 val intersect = List.foldl (fn (a, b) => SysWord.andb(a,b)) all 183 end 184 185 structure Reg = 186 struct 187 188 datatype hkey = 189 PREDEFINED of int 190 | SUBKEY of int (* Actually abstract. *) 191 val classesRoot = PREDEFINED 0 192 val currentUser = PREDEFINED 1 193 val localMachine = PREDEFINED 2 194 val users = PREDEFINED 3 195 val performanceData = PREDEFINED 4 196 val currentConfig = PREDEFINED 5 197 val dynData = PREDEFINED 6 198 datatype create_result = 199 CREATED_NEW_KEY of hkey 200 | OPENED_EXISTING_KEY of hkey 201 datatype value = 202 SZ of string 203 | DWORD of SysWord.word 204 | BINARY of Word8Vector.vector 205 | MULTI_SZ of string list 206 | EXPAND_SZ of string 207 208 local 209 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 210 (* Open one of the root keys. *) 211 (* QUESTION: Why is this an option? The definition asks 212 the same question. I've removed the option type. *) 213 fun openRoot args = 214 SUBKEY(winCall(1007, args)) 215 (* Open a sub-key. *) 216 and openSubKey args = 217 SUBKEY(winCall(1008, args)) 218 in 219 fun openKeyEx(PREDEFINED i, s, f) = 220 openRoot(i, s, SysWord.toInt f) 221 | openKeyEx(SUBKEY i, s, f) = 222 openSubKey(i, s, SysWord.toInt f) 223 end 224 225 local 226 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 227 228 fun pairToResult (0, k) = CREATED_NEW_KEY (SUBKEY k) 229 | pairToResult (_, k) = OPENED_EXISTING_KEY (SUBKEY k) 230 231 (* Open one of the root keys. *) 232 fun createRoot args = 233 pairToResult(winCall(1009, args)) 234 (* Open a sub-key. *) 235 and createSubKey args = 236 pairToResult(winCall(1010, args)) 237 238 in 239 (* I've retained the third argument in this interface 240 which used to be used for VOLATILE (1) or 241 NON_VOLATILE (0). Keys are now always non-volatile. *) 242 fun createKeyEx(PREDEFINED i, s, f) = 243 createRoot(i, s, 0, SysWord.toInt f) 244 | createKeyEx(SUBKEY i, s, f) = 245 createSubKey(i, s, 0, SysWord.toInt f) 246 end 247 248 local 249 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 250 in 251 (* TODO: We wouldn't normally expect to close a 252 predefined key but it looks as though we might 253 have to be able to close HKEY_PERFORMANCE_DATA. *) 254 fun closeKey(PREDEFINED _) = () 255 | closeKey(SUBKEY i) = 256 winCall(1011, i) 257 end 258 259 local 260 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 261 262 fun unpackString v = 263 let 264 val len = Word8Vector.length v 265 in 266 if len = 0 then "" 267 else Byte.unpackStringVec(Word8VectorSlice.slice(v, 0, SOME(len -1))) 268 end 269 270 fun unpackStringList v = 271 let 272 val len = Word8Vector.length v 273 fun unpack start i = 274 if i >= len orelse Word8Vector.sub(v, i) = 0w0 275 then if i = start then [] 276 else Byte.unpackStringVec(Word8VectorSlice.slice(v, start, SOME(i - start))) :: 277 unpack (i+1) (i+1) 278 else unpack start (i+1) 279 in 280 unpack 0 0 281 end 282 283 fun queryResultToValues(t, v) = 284 (* Decode the type code and the value. Strings are null terminated so 285 the last character must be removed. *) 286 case t of 287 1 => SZ(unpackString v) 288 | 4 => DWORD(PackWord32Little.subVec(v, 0)) 289 | 2 => EXPAND_SZ(unpackString v) 290 | 7 => MULTI_SZ(unpackStringList v) 291 | _ => BINARY v 292 293 val errorFileNotFound = valOf(OS.syserror "ERROR_FILE_NOT_FOUND") 294 in 295 (* The queryValue functions simply return a type and a vector of bytes. 296 The type code is decoded and the bytes unpacked appropriately. *) 297 fun queryValueEx(key, s) = 298 SOME(queryResultToValues( 299 case key of 300 PREDEFINED i => winCall(1012, (i, s)) 301 | SUBKEY i => winCall(1013, (i, s)) 302 )) 303 handle ex as OS.SysErr(_, SOME err) => 304 if err = errorFileNotFound 305 then NONE 306 else raise ex 307 end 308 309 local 310 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 311 in 312 fun deleteValue(PREDEFINED i, s) = 313 (winCall(1022, (i, s))) 314 | deleteValue(SUBKEY i, s) = 315 (winCall(1023, (i, s))) 316 end 317 318 local 319 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 320 fun packString s = 321 let 322 val len = String.size s 323 val arr = Word8Array.array(len+1, 0w0) 324 in 325 Byte.packString(arr, 0, Substring.full s); 326 Word8Array.vector arr 327 end 328 329 fun packStringList sl = 330 let 331 (* The string list is packed as a set of null-terminated strings 332 with a final null at the end. *) 333 (* TODO: Check for nulls in the strings themselves? *) 334 fun totalSize n [] = n 335 | totalSize n (s::sl) = totalSize (n + String.size s + 1) sl 336 val len = totalSize 1 sl 337 val arr = Word8Array.array(len, 0w0) 338 fun pack _ [] = () 339 | pack n (s::sl) = 340 ( 341 Byte.packString(arr, n, Substring.full s); 342 pack (n + String.size s + 1) sl 343 ) 344 in 345 pack 0 sl; 346 Word8Array.vector arr 347 end 348 349 fun valuesToTypeVal(SZ s) = (1, packString s) 350 | valuesToTypeVal(EXPAND_SZ s) = (2, packString s) 351 | valuesToTypeVal(BINARY s) = (3, s) 352 | valuesToTypeVal(DWORD n) = 353 let 354 (* Pack the 32 bit value into an array, then extract that. *) 355 val arr = Word8Array.array(4, 0w0) 356 in 357 PackWord32Little.update(arr, 0, n); 358 (4, Word8Array.vector arr) 359 end 360 | valuesToTypeVal(MULTI_SZ s) = (7, packStringList s) 361 in 362 fun setValueEx(key, name, v) = 363 let 364 val (t, s) = valuesToTypeVal v 365 val (call, k) = 366 case key of 367 PREDEFINED i => (1016, i) 368 | SUBKEY i => (1017, i) 369 in 370 (winCall(call, (k, name, t, s))) 371 end 372 end 373 374 local 375 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 376 in 377 fun enumKeyEx(PREDEFINED i, n) = 378 (winCall(1018, (i, n))) 379 | enumKeyEx(SUBKEY i, n) = 380 (winCall(1019, (i, n))) 381 382 fun enumValue(PREDEFINED i, n) = 383 (winCall(1020, (i, n))) 384 | enumValue(SUBKEY i, n) = 385 (winCall(1021, (i, n))) 386 end 387 388 local 389 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 390 (* In Windows NT RegDeleteKey will fail if the key has subkeys. 391 To give the same behaviour in both Windows 95 and NT we have 392 to recursively delete any subkeys. *) 393 fun basicDeleteKey(PREDEFINED i, s) = 394 (winCall(1014, (i, s))) 395 | basicDeleteKey(SUBKEY i, s) = 396 (winCall(1015, (i, s))) 397 in 398 fun deleteKey(k, s) = 399 let 400 val sk = openKeyEx(k, s, Key.enumerateSubKeys) 401 fun deleteSubKeys () = 402 case enumKeyEx(sk, 0) of 403 NONE => () 404 | SOME name => (deleteKey(sk, name); deleteSubKeys()) 405 in 406 deleteSubKeys() handle exn => (closeKey sk; raise exn); 407 closeKey sk; 408 basicDeleteKey(k, s) 409 end 410 end 411 end 412 413 structure DDE = 414 struct 415 type info = int (* Actually abstract. *) 416 417 local 418 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 419 in 420 fun startDialog (service, topic) = 421 winCall(1038, (service, topic)) 422 end 423 424 local 425 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 426 in 427 (* The timeout and retry count apply only in the case of 428 a busy result. The Windows call takes a timeout parameter 429 as the length of time to wait for a response and maybe we 430 should use it for that as well. *) 431 fun executeString (info, cmd, retry, delay) = 432 let 433 fun try n = 434 if winCall(1039, (info, cmd)) 435 then () (* Succeeded. *) 436 else if n = 0 437 then raise OS.SysErr("DDE Server busy", NONE) 438 else 439 ( 440 OS.IO.poll([], SOME delay); 441 try (n-1) 442 ) 443 in 444 try retry 445 end 446 end 447 448 local 449 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 450 in 451 fun stopDialog (info) = winCall(1040, info) 452 end 453 end (* DDE *) 454 455 (* No (longer?) in Basis library 456 local 457 val winCall = RunCall.run_call2 POLY_SYS_os_specific 458 in 459 fun fileTimeToLocalFileTime t = winCall(1030, t) 460 fun localFileTimeToFileTime t = winCall(1031, t) 461 end 462 *) 463 464 local 465 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 466 in 467 fun getVolumeInformation root = 468 let 469 val (vol, sys, serial, max) = 470 winCall(1032, root) 471 in 472 { volumeName = vol, systemName = sys, 473 serialNumber = SysWord.fromInt serial, 474 maximumComponentLength = max } 475 end 476 end 477 478 local 479 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 480 in 481 fun findExecutable s = SOME(winCall(1033, s)) handle OS.SysErr _ => NONE 482 end 483 484 local 485 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 486 in 487 fun openDocument s = winCall(1034, s) 488 end 489 490 local 491 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 492 in 493 fun launchApplication (command, arg) = 494 winCall(1035, (command, arg)) 495 end 496 497 abstype ('a,'b) proc = ABS of int with end; 498 499 (* Run a process and return a proces object which will 500 allow us to extract the input and output streams. *) 501 local 502 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 503 in 504 fun execute(command, arg): ('a,'b) proc = RunCall.unsafeCast(winCall (1000, (command, arg))) 505 end 506 507 local 508 val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral" 509 in 510 fun sys_get_buffsize (strm: OS.IO.iodesc): int = doIo(15, strm, 0) 511 end 512 513 local 514 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 515 in 516 fun textInstreamOf p = 517 let 518 (* Get the underlying file descriptor. *) 519 val n = winCall (1001, RunCall.unsafeCast p) 520 val textPrimRd = 521 LibraryIOSupport.wrapInFileDescr 522 {fd=n, name="TextPipeInput", initBlkMode=true} 523 val streamIo = TextIO.StreamIO.mkInstream(textPrimRd, "") 524 in 525 TextIO.mkInstream streamIo 526 end 527 528 fun textOutstreamOf p = 529 let 530 val n = winCall (1002, RunCall.unsafeCast p) 531 val buffSize = sys_get_buffsize n 532 val textPrimWr = 533 LibraryIOSupport.wrapOutFileDescr{fd=n, name="TextPipeOutput", 534 appendMode=false, initBlkMode=true, chunkSize=buffSize} 535 (* Construct a stream. *) 536 val streamIo = TextIO.StreamIO.mkOutstream(textPrimWr, IO.LINE_BUF) 537 in 538 TextIO.mkOutstream streamIo 539 end 540 541 fun binInstreamOf p = 542 let 543 (* Get the underlying file descriptor. *) 544 val n = winCall (1003, RunCall.unsafeCast p) 545 val binPrimRd = 546 LibraryIOSupport.wrapBinInFileDescr 547 {fd=n, name="BinPipeInput", initBlkMode=true} 548 val streamIo = 549 BinIO.StreamIO.mkInstream(binPrimRd, Word8Vector.fromList []) 550 in 551 BinIO.mkInstream streamIo 552 end 553 554 fun binOutstreamOf p = 555 let 556 val n = winCall (1004, RunCall.unsafeCast p) 557 val buffSize = sys_get_buffsize n 558 val binPrimWr = 559 LibraryIOSupport.wrapBinOutFileDescr{fd=n, name="BinPipeOutput", 560 appendMode=false, initBlkMode=true, chunkSize=buffSize} 561 (* Construct a stream. *) 562 val streamIo = BinIO.StreamIO.mkOutstream(binPrimWr, IO.LINE_BUF) 563 in 564 BinIO.mkOutstream streamIo 565 end 566 end 567 568 (* reap - wait until the process finishes and get the result. 569 Note: this is defined to be able to return the result repeatedly. 570 At present that's done by not closing the handle except in the 571 garbage collector. That could cause us to run out of handles. *) 572 local 573 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 574 in 575 fun reap p = winCall (1005, RunCall.unsafeCast p) 576 end 577 578 local 579 val winCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 580 in 581 (* Run a process and wait for the result. Rather than do the 582 whole thing as a single RTS call we first start the process 583 and then call "reap" to get the result. This allows this 584 to be run as a separate ML process if necessary without 585 blocking everything. 586 This is similar to OS.Process.system but differs in that the 587 streams are directed to NUL and this runs the executable directly, 588 not via cmd.exe/command.com so cannot run DOS commands. 589 OS.Process.system waits for the result within the RTS call so 590 the whole of ML will be blocked until the process completes. *) 591 fun simpleExecute (command, arg) = 592 let 593 val process = 594 winCall(1037, (command, arg)) 595 in 596 reap process 597 end 598 end 599 600 601 structure Status = 602 struct 603 type status = SysWord.word 604 605 val accessViolation = getConst 10 606 val arrayBoundsExceeded = getConst 11 607 val breakpoint = getConst 12 608 val controlCExit = getConst 13 609 val datatypeMisalignment = getConst 14 610 val floatDenormalOperand = getConst 15 611 val floatDivideByZero = getConst 16 612 val floatInexactResult = getConst 17 613 val floatInvalidOperation = getConst 18 614 val floatOverflow = getConst 19 615 val floatStackCheck = getConst 20 616 val floatUnderflow = getConst 21 617 val guardPageViolation = getConst 22 618 val integerDivideByZero = getConst 23 619 val integerOverflow = getConst 24 620 val illegalInstruction = getConst 25 621 val invalidDisposition = getConst 26 622 val invalidHandle = getConst 27 623 val inPageError = getConst 28 624 (* This was given as nocontinuableException *) 625 val noncontinuableException= getConst 29 626 val pending = getConst 30 627 val privilegedInstruction = getConst 31 628 val singleStep = getConst 32 629 val stackOverflow = getConst 33 630 val timeout = getConst 34 631 val userAPC = getConst 35 632 end 633 634 (* The status is implemented as an integer. *) 635 fun fromStatus (s: OS.Process.status): Status.status = 636 SysWord.fromInt(RunCall.unsafeCast s); 637 638 fun exit (s: Status.status) = 639 OS.Process.exit(RunCall.unsafeCast(SysWord.toInt s)) 640 641 structure Config = 642 struct 643 local 644 val winCall: int*unit->int*int*int*int*string = 645 RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 646 in 647 fun getVersionEx () = 648 let 649 val (major, minor, build, platform, version) = 650 winCall(1050, ()) 651 in 652 { majorVersion = SysWord.fromInt major, 653 minorVersion = SysWord.fromInt minor, 654 buildNumber = SysWord.fromInt build, 655 platformId = SysWord.fromInt platform, 656 csdVersion = version } 657 end 658 end 659 660 local 661 val winCall: int*unit->string = 662 RunCall.rtsCallFull2 "PolyOSSpecificGeneral" 663 in 664 fun getWindowsDirectory () = winCall(1051, ()) 665 and getSystemDirectory () = winCall(1052, ()) 666 and getComputerName () = winCall(1053, ()) 667 and getUserName () = winCall(1054, ()) 668 end 669 670 val platformWin32s = getConst 36 671 val platformWin32Windows = getConst 37 672 val platformWin32NT = getConst 38 673 val platformWin32CE = getConst 39 674 end 675end; 676 677local 678 (* Add pretty printers to hide internals. *) 679 fun prettyRegKey _ _ (_: Windows.Reg.hkey) = PolyML.PrettyString "?" 680 and prettyDDEInfo _ _ (_: Windows.DDE.info) = PolyML.PrettyString "?" 681 and prettyProc _ _ (_: ('a, 'b) Windows.proc) = PolyML.PrettyString "?" 682in 683 val () = PolyML.addPrettyPrinter prettyRegKey 684 and () = PolyML.addPrettyPrinter prettyDDEInfo 685 and () = PolyML.addPrettyPrinter prettyProc 686end; 687