/quick-test/call.r

http://github.com/dockimbel/Red · R · 330 lines · 282 code · 48 blank · 0 comment · 24 complexity · 4e8a4b1a3fda465430e3f73bbffbf08a MD5 · raw file

  1. REBOL [
  2. Name: "CALL for Win32"
  3. Author: "Nenad Rakocevic"
  4. Purpose: "Blocking execution of external commands for Windows OS"
  5. ]
  6. context [
  7. kernel32: load/library %kernel32.dll
  8. SECURITY_ATTRIBUTES: make struct! [
  9. nLength [integer!]
  10. lpSecurityDescriptor [integer!]
  11. bInheritHandle [integer!]
  12. ] none
  13. STARTUPINFO: make struct! startup-info-struct: [
  14. cb [integer!]
  15. lpReserved [integer!]
  16. lpDesktop [integer!]
  17. lpTitle [integer!]
  18. dwX [integer!]
  19. dwY [integer!]
  20. dwXSize [integer!]
  21. dwYSize [integer!]
  22. dwXCountChars [integer!]
  23. dwYCountChars [integer!]
  24. dwFillAttribute [integer!]
  25. dwFlags [integer!]
  26. wShowWindow [short]
  27. cbReserved2 [short]
  28. lpReserved2 [integer!]
  29. hStdInput [integer!]
  30. hStdOutput [integer!]
  31. hStdError [integer!]
  32. ] none
  33. PROCESS_INFORMATION: make struct! [
  34. hProcess [integer!]
  35. hThread [integer!]
  36. dwProcessID [integer!]
  37. dwThreadID [integer!]
  38. ] none
  39. CreatePipe: make routine! [
  40. phReadPipe [struct! [num [integer!]]]
  41. phWritePipe [struct! [num [integer!]]]
  42. lpPipeAttributes [struct! [a [integer!] b [integer!] c [integer!]]]
  43. nSize [integer!]
  44. return: [integer!]
  45. ] kernel32 "CreatePipe"
  46. ReadFile: make routine! [
  47. hFile [integer!]
  48. lpBuffer [string!]
  49. nNumberOfBytesToRead [integer!]
  50. lpNumberOfBytesRead [struct! [num [integer!]]]
  51. lpOverlapped [integer!]
  52. return: [integer!]
  53. ] kernel32 "ReadFile"
  54. PeekNamedPipe: make routine! [
  55. hNamedPipe [integer!]
  56. lpBuffer [integer!]
  57. nBufferSize [integer!]
  58. lpBytesRead [integer!]
  59. lpTotalBytesAvail [struct! [cnt [integer!]]]
  60. lpBytesLeftThisMessage [integer!]
  61. return: [integer!]
  62. ] kernel32 "PeekNamedPipe"
  63. WriteFile: make routine! [
  64. hFile [integer!]
  65. lpBuffer [string!]
  66. nNumberOfBytesToWrite [integer!]
  67. lpNumberOfBytesWritten [struct! [num [integer!]]]
  68. lpOverlapped [integer!]
  69. return: [integer!]
  70. ] kernel32 "WriteFile"
  71. SetHandleInformation: make routine! [
  72. hObject [integer!]
  73. dwMask [integer!]
  74. dwFlags [integer!]
  75. return: [integer!]
  76. ] kernel32 "SetHandleInformation"
  77. GetEnvironmentStrings: make routine! [
  78. return: [integer!]
  79. ] kernel32 "GetEnvironmentStringsA"
  80. unless all [value? 'set-env native? :set-env][
  81. set 'set-env make routine! [
  82. name [string!]
  83. value [string!]
  84. return: [integer!]
  85. ] kernel32 "SetEnvironmentVariableA"
  86. ]
  87. CreateProcess: make routine! compose/deep [
  88. lpApplicationName [integer!]
  89. lpCommandLine [string!]
  90. lpProcessAttributes [struct! [a [integer!] b [integer!] c [integer!]]]
  91. lpThreadAttributes [struct! [a [integer!] b [integer!] c [integer!]]]
  92. bInheritHandles [char!]
  93. dwCreationFlags [integer!]
  94. lpEnvironment [integer!]
  95. lpCurrentDirectory [integer!]
  96. lpStartupInfo [struct! [(startup-info-struct)]]
  97. lpProcessInformation [struct! [a [integer!] b [integer!] c [integer!] d [integer!]]]
  98. return: [integer!]
  99. ] kernel32 "CreateProcessA"
  100. CloseHandle: make routine! [
  101. hObject [integer!]
  102. return: [integer!]
  103. ] kernel32 "CloseHandle"
  104. GetExitCodeProcess: make routine! [
  105. hProcess [integer!]
  106. lpExitCode [struct! [int [integer!]]]
  107. return: [integer!]
  108. ] kernel32 "GetExitCodeProcess"
  109. Sleep: make routine! [
  110. dwMilliseconds [long]
  111. ] kernel32 "Sleep"
  112. FORMAT_MESSAGE_FROM_SYSTEM: to-integer #{00001000}
  113. FORMAT_MESSAGE_IGNORE_INSERTS: to-integer #{00000200}
  114. fmt-msg-flags: FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS
  115. GetLastError: make routine! [
  116. return: [integer!]
  117. ] kernel32 "GetLastError"
  118. FormatMessage: make routine! [
  119. dwFlags [integer!]
  120. lpSource [integer!]
  121. dwMessageId [integer!]
  122. dwLanguageId [integer!]
  123. lpBuffer [string!]
  124. nSize [integer!]
  125. Arguments [integer!]
  126. return: [integer!]
  127. ] kernel32 "FormatMessageA"
  128. STARTF_USESTDHANDLES: to-integer #{00000100}
  129. STARTF_USESHOWWINDOW: 1
  130. SW_HIDE: 0
  131. STILL_ACTIVE: 259
  132. null: to-char 0
  133. lpDWORD: make struct! [int [integer!]] none
  134. make-lpDWORD: does [make struct! lpDWORD [0]]
  135. sa: make struct! SECURITY_ATTRIBUTES [0 0 1]
  136. sa/nLength: length? third sa
  137. start-info: make struct! STARTUPINFO none
  138. start-info/cb: length? third start-info
  139. start-info/dwFlags: STARTF_USESTDHANDLES
  140. make-null-string!: func [len [integer!]][
  141. head insert/dup make string! len null len
  142. ]
  143. get-error-msg: has [out][
  144. out: make-null-string! 256
  145. FormatMessage fmt-msg-flags 0 last-error: GetLastError 0 out 256 0
  146. trim/tail out
  147. ]
  148. try*: func [body [block!] /local out err][
  149. if error? set/any 'err try body [
  150. out: get-error-msg
  151. err: disarm err
  152. if string? get in err 'arg1 [insert out rejoin [err/arg1 ": "]]
  153. return out
  154. ]
  155. none
  156. ]
  157. cmd: context [
  158. output: error: none
  159. show?: input?: no
  160. pipe-size: 10'000
  161. pipe-buffer: make-null-string! pipe-size
  162. si: make struct! start-info second start-info
  163. pi: make struct! PROCESS_INFORMATION none
  164. in-hRead: make-lpDWORD
  165. in-hWrite: make-lpDWORD
  166. out-hRead: make-lpDWORD
  167. out-hWrite: make-lpDWORD
  168. err-hRead: make-lpDWORD
  169. err-hWrite: make-lpDWORD
  170. bytes-avail: make-lpDWORD
  171. bytes-read: make-lpDWORD
  172. bytes-written: make-lpDWORD
  173. exit-code: make-lpDWORD
  174. ]
  175. launch-call: func [cmd-line [string!] /local ret env][
  176. cmd-line: join cmd-line null
  177. change/dup cmd/pipe-buffer null cmd/pipe-size
  178. ret: catch [
  179. ;-- Create STDOUT pipe and ensure the read handle is not inherited
  180. if zero? CreatePipe cmd/out-hRead cmd/out-hWrite sa 0 [throw 1]
  181. if zero? SetHandleInformation cmd/out-hRead/int 1 0 [throw 3]
  182. cmd/si/hStdOutput: cmd/out-hWrite/int
  183. ;-- Create STDERR pipe and ensure the read handle is not inherited
  184. if zero? CreatePipe cmd/err-hRead cmd/err-hWrite sa 0 [throw 1]
  185. if zero? SetHandleInformation cmd/err-hRead/int 1 0 [throw 3]
  186. cmd/si/hStdError: cmd/err-hWrite/int
  187. if cmd/input? [
  188. ;-- Create STDIN pipe and ensure the write handle is not inherited
  189. if zero? CreatePipe cmd/in-hRead cmd/in-hWrite sa 0 [throw 1]
  190. if zero? SetHandleInformation cmd/in-hWrite/int 1 0 [throw 3]
  191. cmd/si/hStdInput: cmd/in-hRead/int
  192. ]
  193. unless cmd/show? [cmd/si/dwFlags: cmd/si/dwFlags or STARTF_USESHOWWINDOW]
  194. env: GetEnvironmentStrings
  195. if zero? CreateProcess 0 cmd-line sa sa to char! 1 0 env 0 cmd/si cmd/pi [throw 2]
  196. ret: none
  197. ]
  198. if integer? ret [
  199. make error! join pick [
  200. "CreatePipe"
  201. "CreateProcess"
  202. "SetHandleInformation"
  203. ] ret " failed!"
  204. ]
  205. ]
  206. read-pipe: func [buffer pipe /local remain][
  207. if zero? PeekNamedPipe pipe/int 0 0 0 cmd/bytes-avail 0 [throw 1]
  208. unless zero? remain: cmd/bytes-avail/int [
  209. until [
  210. if zero? ReadFile pipe/int cmd/pipe-buffer cmd/pipe-size cmd/bytes-read 0 [throw 2]
  211. insert/part tail buffer cmd/pipe-buffer cmd/bytes-read/int
  212. change/dup cmd/pipe-buffer null cmd/pipe-size
  213. remain: remain - cmd/bytes-read/int
  214. zero? remain
  215. ]
  216. ]
  217. ]
  218. write-pipe: func [buffer pipe][
  219. until [
  220. if zero? WriteFile pipe/int buffer length? buffer cmd/bytes-written 0 [throw 4]
  221. tail? buffer: skip buffer cmd/bytes-written/int
  222. ]
  223. ;-- Close the pipe handles so the child process stops reading
  224. CloseHandle cmd/in-hRead/int
  225. CloseHandle cmd/in-hWrite/int
  226. ]
  227. get-process-info: has [ret][
  228. ;unless zero? cmd/pi/hProcess [
  229. ret: catch [
  230. if zero? GetExitCodeProcess cmd/pi/hProcess cmd/exit-code [throw 3]
  231. if cmd/output [read-pipe cmd/output cmd/out-hRead]
  232. if cmd/error [read-pipe cmd/error cmd/err-hRead]
  233. if cmd/exit-code/int <> STILL_ACTIVE [
  234. CloseHandle cmd/pi/hProcess
  235. CloseHandle cmd/pi/hThread
  236. CloseHandle cmd/out-hRead/int
  237. CloseHandle cmd/out-hWrite/int
  238. CloseHandle cmd/err-hRead/int
  239. CloseHandle cmd/err-hWrite/int
  240. cmd/pi/hProcess: 0
  241. return true
  242. ]
  243. ret: none
  244. ]
  245. if integer? ret [
  246. make error! join pick [
  247. "PeekNamedPipe"
  248. "ReadFile"
  249. "GetExitCodeProcess"
  250. "WriteFile"
  251. ] ret " failed!"
  252. ]
  253. ;]
  254. false
  255. ]
  256. set 'win-call func [
  257. command [string!]
  258. /input
  259. in [string! binary!]
  260. /output
  261. out [string! binary!]
  262. /error
  263. err [string! binary!]
  264. /wait ;-- placeholder, win-call is always waiting
  265. /show
  266. /local
  267. res msg
  268. ][
  269. cmd/input?: to-logic input
  270. cmd/show?: to-logic show
  271. cmd/output: out
  272. cmd/error: err
  273. if msg: try* [launch-call command][return msg]
  274. if input [write-pipe in cmd/in-hWrite]
  275. until [
  276. Sleep 10
  277. if msg: try* [res: get-process-info][return msg]
  278. res
  279. ]
  280. none
  281. ]
  282. ]