cr-lf madness, mostly

This commit is contained in:
steve donovan
2011-06-30 15:32:48 +02:00
parent 4f0e6dd02e
commit 1403354fc7
2 changed files with 608 additions and 503 deletions
+373 -373
View File
@@ -1,373 +1,373 @@
## winapi A useful Windows API subset for Lua
This module provides some basic tools for working with Windows systems, finding out system resources, and gives you more control over process creation. In this introduction any plain reference is in the `winapi` table, so that `find_window` means `winapi.find_window`. Normally `winapi` works with the current Windows code page, but can be told to use UTF-8 with `set_encoding`; interally string operations are in Unicode.
### Creating and working with Processes
An irritating fact is that Lua GUI applications (such as IUP or wxLua) cannot use `os.execute` without the infamous 'flashing black box' of console creation. And `io.popen` may in fact not work at all.
`execute` provides a _quiet_ method to call a shell command. It returns the result code (like `os.execute`) but also any text generated from the command. So for many common applications it will do as a `io.popen` replacement as well. This function is blocking, but `winapi` provides more general ways of launching processes in the background and even capturing their output asynchronously. This will be discussed later with `spawn`.
Apart from `execute`, `shell_exec` is the Swiss-Army-Knife of Windows process creation. The first parameter is the 'action' or 'verb' to apply to the path; common actions are 'open', 'edit' and 'print'. Notice that these are the actions defined in Explorer (hence the word 'shell'). So to open a document in Word (or whatever application is registered for this extension):
winapi.shell_exec('open','myold.doc')
Or an explorer window for a directory:
winapi.shell_exec('open','\\users\\steve\\lua')
Note that this function launches the process and does not block. The path may be an explicit program to use, and then we can also specify the command-line parameters:
winapi.shell_exec(nil,'scite','wina.lua')
The fourth parameter is the working directory for the process, and the fifth indicates how the program's window is to be opened. For instance, you can open a file in Notepad already minimized:
winapi.shell_exec(nil,'notepad','wina.lua',nil,winapi.SW_MINIMIZE)
For fine control over console programs, use `winapi.spawn` - you pass it the command-line, and receive two values; a process object and a file object. You monitor the process with the first, and can read from or write to the second.
> proc,file = winapi.spawn 'cmd /c dir /b'
> = file:read()
bonzo.lc
cexport.lua
class1.c
...
> = proc:wait()
userdata: 0000000000539608 OK
> = proc:exit_code()
0
If the command is invalid, then you will get an error message instead:
> = winapi.spawn 'frodo'
nil The system cannot find the file specified.
This is what `execute` does under the hood, but doing it explicitly gives you more control. For instance, the `wait` method of the process object can take an optional time-out parameter; if you wait too long for the process, it will return the process object and the string 'TIMEOUT'.
local _,status = proc:wait(500)
if status == 'TIMEOUT' then
proc:kill()
end
The file object is unfortunately not a Lua file object, since it is not possible to _portably_ re-use the existing Lua implementation without copying large chunks of `liolib.c` into this library. So `read` grabs what's available, unbuffered. But I feel that it's easy enough for Lua code to parse the result into separate lines, if needed.
Having a `write` method means that, yes, you can capture an interactive process, send it commands and read the result. The caveat is that this process must not buffer standard output. For instance, launch interactive Lua with a command-line like this:
> proc,file = winapi.spawn [[lua -e "io.stdout:setvbuf('no')" -i]]
> = file:read() -- always read the program banner first!
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
>
> = file:write 'print "hello"\n'
14
> = file:read()
hello
>
> proc:kill()
(We also found it necessary in the [Lua for Windows](http://code.google.com/p/luaforwindows/) project to switch off buffering for using Lua in SciTE)
Note that reading the result also returns the prompt '>', which isn't so obvious if we're running Lua from within Lua itself. It's clearer when using Python:
> proc,file = winapi.spawn [[python -i]]
> = file:read()
Python 2.6.2c1 (r262c1:71369, Apr 7 2009, 18:44:00) [MSC v.1500 32 bit (Intel)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
> file:write '40+2\n'
> = file:read()
42
>>>
This kind of interactive process capture is fine for a console application, but `read` is blocking and will freeze any GUI program. For this, you use `read_async` which returns the result through a callback.
> file:write '40+2\n'
> file:read_async(function(s) print('++',s) end)
> ++ 42
>>>
This can work nicely with Lua coroutines, allowing us to write pseudo-blocking code for interacting with processes.
The process object can provide more useful information:
> = proc:working_size()
200 1380
> = proc:run_times()
0 31
`working_size` gives you a lower and an upper bound on the process memory in kB; `run_times` gives you the time (in milliseconds) spent in the user process and in the kernel. So the time to calculate `40+2` twice is too fast to even register, and it has only spent 31 msec in the system.
It is possible to wait on more than one process at a time. Consider this simple time-wasting script:
for i = 1,1e8 do end
It takes me 0.743 seconds to do this. But running two such scripts in parallel is about the same speed (0.776):
require 'winapi'
local t = os.clock()
local P = {}
P[1] = winapi.spawn 'lua slow.lua'
P[2] = winapi.spawn 'lua slow.lua'
winapi.wait_for_processes(P,true)
print(os.clock() - t)
So my i3 is effectively a two-processor machine; four such processes take 1.325 seconds, just under twice as long. The second parameter means 'wait for all'; like the `wait` method, it has an optional timeout parameter.
### Working with Windows
The windows object provides methods for querying window properties. For instance, the desktop window fills the whole screen, so to find out the screen dimensions is straightforward:
> = winapi.desktop_window():get_bounds()
1600 900
Finding other windows is best done by iterating over all top-level windows and checking them for some desired property; (`find_window` is provided for completeness, but you really have to provide the exact window caption for the second parameter.)
`find_all_windows` returns all windows matching some function. For convenience, two useful matchers are provided, `match_name` and `match_class`. Once you have a group of related windows, you can do fun things like tile them:
> t = winapi.find_all_windows(winapi.match_name '- SciTE')
> = #t
2
> winapi.tile_windows(winapi.desktop_window(),false,t)
This call needs the parent window (we just use the desktop), whether to tile horizontally, and a table of window objects. There is an optional fourth parameter, which is the bounds to use for the tiling, specified like so `{left=0,top=0,right=600,bottom=900}`.
With tiling and the ability to hide windows with `w:show(winapi.SW_HIDE)` it is entirely possible to write a little 'virtual desktop' application.
`find_window_ex` also uses a matcher function; `find_window_match` is a shortcut for the operation of finding a window by its caption.
Every window has an associated text value. For top-level windows, this is the window caption:
> = winapi.foreground_window()
Command Prompt - lua -lwinapi
So the equivalent of the old DOS command `title` would here be:
winapi.foreground_window():set_text 'My new title'
Any top-level window will contain child windows. For example, Notepad has a simple structure revealed by `enum_children`:
> w = winapi.find_window_match 'Notepad'
> = w
Untitled - Notepad
> t = {}
> w:enum_children(function(w) table.insert(t,w) end)
> = #t
2
> = t[1]:get_class_name()
Edit
> = t[2]:get_class_name()
msctls_statusbar32
Windows controls like the 'Edit' control interact with the unverse via messages.
`EM_GETLINECOUNT` will tell the control to return the number of lines. Looking up the numerical value of this message, it's easy to query Notepad's edit control:
> = t[1]:send_message(186,0,0)
6
An entertaining way to automate some programs is to send virtual keystrokes to them. The function `send_input` sends characters to the current foreground window:
> winapi.send_input '= 20 + 10\n'
> = 20 + 10
30
After launching a window, you can make it the foreground window and send it text:
winapi.shell_exec(nil,'notepad')
winapi.sleep(100)
notew = winapi.find_window_match 'Untitled'
notew:set_foreground()
winapi.send_input 'Hello World!'
The little sleep is important: it gives the other process a chance to get going, and to create a new window which we can promote.
An important point is that you can choose to use UTF-8 encoding with winapi. This little program shows how:
local W = require 'winapi'
W.set_encoding(W.CP_UTF8)
win = W.foreground_window()
win:set_text 'ελληνική'
When run in SciTE, it successfully puts a little bit of Greek in the title bar.
### Working with Processes
`current_process` will give you a process object for the current program. It's also possible to get a process object from a program's window:
> w = winapi.foreground_window()
> = w
Command Prompt - lua -lwinapi
> p = w:get_process()
> = p:get_process_name()
cmd.exe
> = p:get_process_name(true)
C:\WINDOWS\system32\cmd.exe
(Note that the `get_process_name` method can optionally give you the full path to the process.)
To get all the current processes:
pids = winapi.get_processes()
for _,pid in ipairs(pids) do
local P = winapi.process(pid)
local name = P:get_process_name(true)
if name then print(pid,name) end
P:close()
end
### Drive and Directory Operations
There are functions for querying the filesystem: `get_logical_drives()` returns all available drives (in 'D:\\' format) and `get_drive_type()` will tell you whether these drives are fixed, remote, removable, etc. `get_disk_free_space()` will return the space used and the space available in kB as two results.
require 'winapi'
drives = winapi.get_logical_drives()
for _,drive in ipairs(drives) do
local free,avail = winapi.get_disk_free_space(drive)
if not free then -- call failed, avail is error
free = '('..avail..')'
else
free = math.ceil(free/1024) -- get Mb
end
local rname = ''
local dtype = winapi.get_drive_type(drive)
if dtype == 'remote' then -- note it wants the drive letter!
rname = winapi.get_disk_network_name(drive:gsub('\\$',''))
end
print(drive,dtype,free,rname)
end
This script gives the following output on my home machine:
C:\ fixed 218967
F:\ fixed 1517
G:\ cdrom (The device is not ready.)
Q:\ fixed (Access is denied.)
Or at work:
C:\ fixed 1455
D:\ fixed 49996
E:\ cdrom (The device is not ready.)
G:\ remote 33844 \\CARL-VFILE\SYS
I:\ remote 452789 \\CARL-VFILE\GROUPS
X:\ remote 12160 \\CARL-VFILE\APPS
Y:\ remote 33844 \\CARL-VFILE\SYS\PUBLIC
Z:\ remote 33844 \\CARL-VFILE\SYS\PUBLIC
A useful operation is watching directories for changes. You specify the directory, the kind of change to monitor and whether subdirectories should be checked. You also provide a function that will be called when something changes.
winapi.watch_for_file_changes(mydir,winapi.FILE_NOTIFY_CHANGE_LAST_WRITE,FALSE,
function(what,who)
-- 'what' will be winapi.FILE_ACTION_MODIFIED
-- 'who' will be the name of the file that changed
print(what,who)
end
)
Using a callback means that you can watch multiple directories and still respond to timers, etc.
Finally, `copy_file` and 'move_file` are indispensible operations which are surprisingly tricky to write correctly in pure Lua. For general filesystem operations like finding the contents of folders, I suggest a more portable library like [LuaFileSystem](). However, you can get pretty far with a well-behaved way to call system commands:
local status,output = winapi.execute('dir /B')
local files = {}
for f in output:gmatch '[^\r\n]+' do
table.insert(files,f)
end
### Output and Timers
GUI applications do not have a console so `print` does not work. `show_message` will put up a message box to bother users, and `output_debug_string` will write text quietly to the debug stream. A utility such as [DebugView](http://technet.microsoft.com/en-us/sysinternals/bb896647) can be used to view this output, which shows it with a timestamp.
Here is the old favourite, system message boxes:
print(winapi.show_message("Message","stuff\nand nonsense","yes-no","warning"))
The first parameter is the caption of the message box, the second is the text (which may contain line feeds); the third controls which buttons are to be shown, and the fourth is the icon to use. The function returns a string indicating which button has been pressed: 'ok','yes','no','cancel', etc.
Or you may prefer to irritate the user with a sound:
winapi.beep 'warning'
It is straightforward to create a timer. You could of course use `sleep` but then your application will do nothing but sleep most of the time. This callback-driven timer can run in the background:
winapi.timer(500,function()
text:append 'gotcha'
end)
Such callbacks can be made GUI-safe by first calling `use_gui` which ensures that any callback is called in the main GUI thread.
The basic rule for callbacks enforced by `winapi` is that only one may be active at a time; otherwise we would risk re-entering Lua using the same state. So be quick when responding to callbacks, since they effectively block Lua. For a console application, the best bet (after setting some timers and so forth) is just to sleep indefinitely:
winapi.sleep(-1)
To show what happens if you don't follow the rule:
> winapi.timer(500,function() end)
> = 23
nil nil return 23
In short: completely messed!
### Reading from the Registry
### Pipe Server
Interprocess communication (IPC) is one of those tangled, operating-system-dependent things that can be terribly useful. On Unix, _named pipes_ are special files which can be used for two processes to easily exchange information. One process opens the pipe for reading, and the other process opens it for writing; the first process will start reading, and this will block until the other process writes to the pipe. Since pipes are a regular part of the filesystem, two Lua scripts can use regular I/O to complete this transaction.
Life is more complicated on Windows (as usual) but with a little bit of help from the API you can get the equivalent mechanism from Windows named pipes. They do work a little differently; they are more like Unix domain sockets; a server waits for a client to connect ('accept') and then produces a handle for the new client to use; it then goes back to waiting for connections.
require 'winapi'
winapi.server(function(file)
file:read_async(function(s) print('['..s..']') end)
end)
winapi.sleep(-1)
Like timers and file notifications, this server runs in its own thread so we have to put the main thread to sleep. This function is passed a callback and a pipe name; pipe names must look like '\\\\.\\pipe\\NAME' and the default name is '\\\\.\\pipe\\luawinapi'. The callback receives a file object - in this case we use `read_async` to play nice with other Lua threads. Multiple clients can have open connections in this way, up to the number of available pipes.
The client can connect in a very straightforward way:
> f = io.open('\\\\.\\pipe\\luawinapi','w')
> f:write 'hello server!\n'
> f:flush()
> f:close()
and our server will say:
[hello server!
]
[]
(Note that `read` receives an _empty string_ when the handle is closed.)
However, we can't push 'standard' I/O very far here. So there is also a corresponding `open_pipe` which returns a file object, both readable and writeable. It's probably best to think of it as a kind of socket; each call to `read` and `write` are regarded as receive/send events.
The server can do something to the received string and pass it back:
winapi.server(function(file)
file:read_async(function(s)
if s == '' then
print 'client disconnected'
else
file:write (s:upper())
end
end)
end)
On the client side:
f = winapi.open_pipe()
f:write 'hello\n'
print(f:read()) -- HELLO
f:write 'dog\n'
print(f:read()) -- DOG
f:close()
Another simularity with sockets is that you can connect to remote pipes (see [pipe names](http://msdn.microsoft.com/en-us/library/aa365783(v=vs.85).aspx))
## winapi A useful Windows API subset for Lua
This module provides some basic tools for working with Windows systems, finding out system resources, and gives you more control over process creation. In this introduction any plain reference is in the `winapi` table, so that `find_window` means `winapi.find_window`. Normally `winapi` works with the current Windows code page, but can be told to use UTF-8 with `set_encoding`; interally string operations are in Unicode.
### Creating and working with Processes
An irritating fact is that Lua GUI applications (such as IUP or wxLua) cannot use `os.execute` without the infamous 'flashing black box' of console creation. And `io.popen` may in fact not work at all.
`execute` provides a _quiet_ method to call a shell command. It returns the result code (like `os.execute`) but also any text generated from the command. So for many common applications it will do as a `io.popen` replacement as well. This function is blocking, but `winapi` provides more general ways of launching processes in the background and even capturing their output asynchronously. This will be discussed later with `spawn`.
Apart from `execute`, `shell_exec` is the Swiss-Army-Knife of Windows process creation. The first parameter is the 'action' or 'verb' to apply to the path; common actions are 'open', 'edit' and 'print'. Notice that these are the actions defined in Explorer (hence the word 'shell'). So to open a document in Word (or whatever application is registered for this extension):
winapi.shell_exec('open','myold.doc')
Or an explorer window for a directory:
winapi.shell_exec('open','\\users\\steve\\lua')
Note that this function launches the process and does not block. The path may be an explicit program to use, and then we can also specify the command-line parameters:
winapi.shell_exec(nil,'scite','wina.lua')
The fourth parameter is the working directory for the process, and the fifth indicates how the program's window is to be opened. For instance, you can open a file in Notepad already minimized:
winapi.shell_exec(nil,'notepad','wina.lua',nil,winapi.SW_MINIMIZE)
For fine control over console programs, use `winapi.spawn` - you pass it the command-line, and receive two values; a process object and a file object. You monitor the process with the first, and can read from or write to the second.
> proc,file = winapi.spawn 'cmd /c dir /b'
> = file:read()
bonzo.lc
cexport.lua
class1.c
...
> = proc:wait()
userdata: 0000000000539608 OK
> = proc:exit_code()
0
If the command is invalid, then you will get an error message instead:
> = winapi.spawn 'frodo'
nil The system cannot find the file specified.
This is what `execute` does under the hood, but doing it explicitly gives you more control. For instance, the `wait` method of the process object can take an optional time-out parameter; if you wait too long for the process, it will return the process object and the string 'TIMEOUT'.
local _,status = proc:wait(500)
if status == 'TIMEOUT' then
proc:kill()
end
The file object is unfortunately not a Lua file object, since it is not possible to _portably_ re-use the existing Lua implementation without copying large chunks of `liolib.c` into this library. So `read` grabs what's available, unbuffered. But I feel that it's easy enough for Lua code to parse the result into separate lines, if needed.
Having a `write` method means that, yes, you can capture an interactive process, send it commands and read the result. The caveat is that this process must not buffer standard output. For instance, launch interactive Lua with a command-line like this:
> proc,file = winapi.spawn [[lua -e "io.stdout:setvbuf('no')" -i]]
> = file:read() -- always read the program banner first!
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
>
> = file:write 'print "hello"\n'
14
> = file:read()
hello
>
> proc:kill()
(We also found it necessary in the [Lua for Windows](http://code.google.com/p/luaforwindows/) project to switch off buffering for using Lua in SciTE)
Note that reading the result also returns the prompt '>', which isn't so obvious if we're running Lua from within Lua itself. It's clearer when using Python:
> proc,file = winapi.spawn [[python -i]]
> = file:read()
Python 2.6.2c1 (r262c1:71369, Apr 7 2009, 18:44:00) [MSC v.1500 32 bit (Intel)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
> file:write '40+2\n'
> = file:read()
42
>>>
This kind of interactive process capture is fine for a console application, but `read` is blocking and will freeze any GUI program. For this, you use `read_async` which returns the result through a callback.
> file:write '40+2\n'
> file:read_async(function(s) print('++',s) end)
> ++ 42
>>>
This can work nicely with Lua coroutines, allowing us to write pseudo-blocking code for interacting with processes.
The process object can provide more useful information:
> = proc:working_size()
200 1380
> = proc:run_times()
0 31
`working_size` gives you a lower and an upper bound on the process memory in kB; `run_times` gives you the time (in milliseconds) spent in the user process and in the kernel. So the time to calculate `40+2` twice is too fast to even register, and it has only spent 31 msec in the system.
It is possible to wait on more than one process at a time. Consider this simple time-wasting script:
for i = 1,1e8 do end
It takes me 0.743 seconds to do this. But running two such scripts in parallel is about the same speed (0.776):
require 'winapi'
local t = os.clock()
local P = {}
P[1] = winapi.spawn 'lua slow.lua'
P[2] = winapi.spawn 'lua slow.lua'
winapi.wait_for_processes(P,true)
print(os.clock() - t)
So my i3 is effectively a two-processor machine; four such processes take 1.325 seconds, just under twice as long. The second parameter means 'wait for all'; like the `wait` method, it has an optional timeout parameter.
### Working with Windows
The windows object provides methods for querying window properties. For instance, the desktop window fills the whole screen, so to find out the screen dimensions is straightforward:
> = winapi.desktop_window():get_bounds()
1600 900
Finding other windows is best done by iterating over all top-level windows and checking them for some desired property; (`find_window` is provided for completeness, but you really have to provide the exact window caption for the second parameter.)
`find_all_windows` returns all windows matching some function. For convenience, two useful matchers are provided, `match_name` and `match_class`. Once you have a group of related windows, you can do fun things like tile them:
> t = winapi.find_all_windows(winapi.match_name '- SciTE')
> = #t
2
> winapi.tile_windows(winapi.desktop_window(),false,t)
This call needs the parent window (we just use the desktop), whether to tile horizontally, and a table of window objects. There is an optional fourth parameter, which is the bounds to use for the tiling, specified like so `{left=0,top=0,right=600,bottom=900}`.
With tiling and the ability to hide windows with `w:show(winapi.SW_HIDE)` it is entirely possible to write a little 'virtual desktop' application.
`find_window_ex` also uses a matcher function; `find_window_match` is a shortcut for the operation of finding a window by its caption.
Every window has an associated text value. For top-level windows, this is the window caption:
> = winapi.foreground_window()
Command Prompt - lua -lwinapi
So the equivalent of the old DOS command `title` would here be:
winapi.foreground_window():set_text 'My new title'
Any top-level window will contain child windows. For example, Notepad has a simple structure revealed by `enum_children`:
> w = winapi.find_window_match 'Notepad'
> = w
Untitled - Notepad
> t = {}
> w:enum_children(function(w) table.insert(t,w) end)
> = #t
2
> = t[1]:get_class_name()
Edit
> = t[2]:get_class_name()
msctls_statusbar32
Windows controls like the 'Edit' control interact with the unverse via messages.
`EM_GETLINECOUNT` will tell the control to return the number of lines. Looking up the numerical value of this message, it's easy to query Notepad's edit control:
> = t[1]:send_message(186,0,0)
6
An entertaining way to automate some programs is to send virtual keystrokes to them. The function `send_input` sends characters to the current foreground window:
> winapi.send_input '= 20 + 10\n'
> = 20 + 10
30
After launching a window, you can make it the foreground window and send it text:
winapi.shell_exec(nil,'notepad')
winapi.sleep(100)
notew = winapi.find_window_match 'Untitled'
notew:set_foreground()
winapi.send_input 'Hello World!'
The little sleep is important: it gives the other process a chance to get going, and to create a new window which we can promote.
An important point is that you can choose to use UTF-8 encoding with winapi. This little program shows how:
local W = require 'winapi'
W.set_encoding(W.CP_UTF8)
win = W.foreground_window()
win:set_text 'ελληνική'
When run in SciTE, it successfully puts a little bit of Greek in the title bar.
### Working with Processes
`current_process` will give you a process object for the current program. It's also possible to get a process object from a program's window:
> w = winapi.foreground_window()
> = w
Command Prompt - lua -lwinapi
> p = w:get_process()
> = p:get_process_name()
cmd.exe
> = p:get_process_name(true)
C:\WINDOWS\system32\cmd.exe
(Note that the `get_process_name` method can optionally give you the full path to the process.)
To get all the current processes:
pids = winapi.get_processes()
for _,pid in ipairs(pids) do
local P = winapi.process(pid)
local name = P:get_process_name(true)
if name then print(pid,name) end
P:close()
end
### Drive and Directory Operations
There are functions for querying the filesystem: `get_logical_drives()` returns all available drives (in 'D:\\' format) and `get_drive_type()` will tell you whether these drives are fixed, remote, removable, etc. `get_disk_free_space()` will return the space used and the space available in kB as two results.
require 'winapi'
drives = winapi.get_logical_drives()
for _,drive in ipairs(drives) do
local free,avail = winapi.get_disk_free_space(drive)
if not free then -- call failed, avail is error
free = '('..avail..')'
else
free = math.ceil(free/1024) -- get Mb
end
local rname = ''
local dtype = winapi.get_drive_type(drive)
if dtype == 'remote' then -- note it wants the drive letter!
rname = winapi.get_disk_network_name(drive:gsub('\\$',''))
end
print(drive,dtype,free,rname)
end
This script gives the following output on my home machine:
C:\ fixed 218967
F:\ fixed 1517
G:\ cdrom (The device is not ready.)
Q:\ fixed (Access is denied.)
Or at work:
C:\ fixed 1455
D:\ fixed 49996
E:\ cdrom (The device is not ready.)
G:\ remote 33844 \\CARL-VFILE\SYS
I:\ remote 452789 \\CARL-VFILE\GROUPS
X:\ remote 12160 \\CARL-VFILE\APPS
Y:\ remote 33844 \\CARL-VFILE\SYS\PUBLIC
Z:\ remote 33844 \\CARL-VFILE\SYS\PUBLIC
A useful operation is watching directories for changes. You specify the directory, the kind of change to monitor and whether subdirectories should be checked. You also provide a function that will be called when something changes.
winapi.watch_for_file_changes(mydir,winapi.FILE_NOTIFY_CHANGE_LAST_WRITE,FALSE,
function(what,who)
-- 'what' will be winapi.FILE_ACTION_MODIFIED
-- 'who' will be the name of the file that changed
print(what,who)
end
)
Using a callback means that you can watch multiple directories and still respond to timers, etc.
Finally, `copy_file` and 'move_file` are indispensible operations which are surprisingly tricky to write correctly in pure Lua. For general filesystem operations like finding the contents of folders, I suggest a more portable library like [LuaFileSystem](). However, you can get pretty far with a well-behaved way to call system commands:
local status,output = winapi.execute('dir /B')
local files = {}
for f in output:gmatch '[^\r\n]+' do
table.insert(files,f)
end
### Output and Timers
GUI applications do not have a console so `print` does not work. `show_message` will put up a message box to bother users, and `output_debug_string` will write text quietly to the debug stream. A utility such as [DebugView](http://technet.microsoft.com/en-us/sysinternals/bb896647) can be used to view this output, which shows it with a timestamp.
Here is the old favourite, system message boxes:
print(winapi.show_message("Message","stuff\nand nonsense","yes-no","warning"))
The first parameter is the caption of the message box, the second is the text (which may contain line feeds); the third controls which buttons are to be shown, and the fourth is the icon to use. The function returns a string indicating which button has been pressed: 'ok','yes','no','cancel', etc.
Or you may prefer to irritate the user with a sound:
winapi.beep 'warning'
It is straightforward to create a timer. You could of course use `sleep` but then your application will do nothing but sleep most of the time. This callback-driven timer can run in the background:
winapi.timer(500,function()
text:append 'gotcha'
end)
Such callbacks can be made GUI-safe by first calling `use_gui` which ensures that any callback is called in the main GUI thread.
The basic rule for callbacks enforced by `winapi` is that only one may be active at a time; otherwise we would risk re-entering Lua using the same state. So be quick when responding to callbacks, since they effectively block Lua. For a console application, the best bet (after setting some timers and so forth) is just to sleep indefinitely:
winapi.sleep(-1)
To show what happens if you don't follow the rule:
> winapi.timer(500,function() end)
> = 23
nil nil return 23
In short: completely messed!
### Reading from the Registry
### Pipe Server
Interprocess communication (IPC) is one of those tangled, operating-system-dependent things that can be terribly useful. On Unix, _named pipes_ are special files which can be used for two processes to easily exchange information. One process opens the pipe for reading, and the other process opens it for writing; the first process will start reading, and this will block until the other process writes to the pipe. Since pipes are a regular part of the filesystem, two Lua scripts can use regular I/O to complete this transaction.
Life is more complicated on Windows (as usual) but with a little bit of help from the API you can get the equivalent mechanism from Windows named pipes. They do work a little differently; they are more like Unix domain sockets; a server waits for a client to connect ('accept') and then produces a handle for the new client to use; it then goes back to waiting for connections.
require 'winapi'
winapi.server(function(file)
file:read_async(function(s) print('['..s..']') end)
end)
winapi.sleep(-1)
Like timers and file notifications, this server runs in its own thread so we have to put the main thread to sleep. This function is passed a callback and a pipe name; pipe names must look like '\\\\.\\pipe\\NAME' and the default name is '\\\\.\\pipe\\luawinapi'. The callback receives a file object - in this case we use `read_async` to play nice with other Lua threads. Multiple clients can have open connections in this way, up to the number of available pipes.
The client can connect in a very straightforward way:
> f = io.open('\\\\.\\pipe\\luawinapi','w')
> f:write 'hello server!\n'
> f:flush()
> f:close()
and our server will say:
[hello server!
]
[]
(Note that `read` receives an _empty string_ when the handle is closed.)
However, we can't push 'standard' I/O very far here. So there is also a corresponding `open_pipe` which returns a file object, both readable and writeable. It's probably best to think of it as a kind of socket; each call to `read` and `write` are regarded as receive/send events.
The server can do something to the received string and pass it back:
winapi.server(function(file)
file:read_async(function(s)
if s == '' then
print 'client disconnected'
else
file:write (s:upper())
end
end)
end)
On the client side:
f = winapi.open_pipe()
f:write 'hello\n'
print(f:read()) -- HELLO
f:write 'dog\n'
print(f:read()) -- DOG
f:close()
Another similarity with sockets is that you can connect to remote pipes (see [pipe names](http://msdn.microsoft.com/en-us/library/aa365783(v=vs.85).aspx))
+235 -130
View File
@@ -84,9 +84,9 @@ static int l_get_encoding(lua_State *L) {
}
/// encode a string in another encoding.
// Note: currently there's a limit of about 1K on the string buffer.
// @param e_in `CP_ACP` or `CP_UTF8`
// @param e_out `CP_UTF8` or `CP_ACP`
// Note: currently there's a limit of about 2K on the string buffer.
// @param e_in `CP_ACP`, `CP_UTF8` or `CP_UTF16`
// @param e_out likewise
// @param text the string
// @function encode
static int l_encode(lua_State *L) {
@@ -106,18 +106,59 @@ static int l_encode(lua_State *L) {
set_encoding(e_out);
push_wstring(L,ws);
} else {
lua_pushlstring(L,(LPCSTR)ws,wcslen(ws));
lua_pushlstring(L,(LPCSTR)ws,wcslen(ws)*sizeof(WCHAR));
}
set_encoding(ce);
return 1;
}
/// expand unicode escapes in a string.
// @param text ASCII text with %uXXXX, where XXXX is four hex digits. %% means % itself.
// @return text as UTF-8
// @function uexpand
static int l_uexpand(lua_State *L) {
const char *text = luaL_checklstring(L,1,NULL);
#line 98 "winapi.l.c"
int len = strlen(text), i = 0, enc = get_encoding();
WCHAR wch;
LPWSTR P = wbuff;
if (len > sizeof(wbuff)) {
return push_error_msg(L,"string too big");
}
while (i <= len) {
if (text[i] == '%') {
++i;
if (text[i] == '%') {
wch = '%';
} else
if (text[i] == 'u' && len-i > 4) {
char hexnum[5];
strncpy(hexnum,text+i+1,4);
hexnum[4] = '\0';
wch = strtol(hexnum,NULL,16);
i += 4;
} else {
return push_error_msg(L,"bad % escape");
}
} else {
wch = (WCHAR)text[i];
}
*P++ = wch;
++i;
}
*P++ = 0;
set_encoding(CP_UTF8);
push_wstring(L,wbuff);
set_encoding(enc);
return 1;
}
// forward reference to Process constructor
static int push_new_Process(lua_State *L,Int pid, HANDLE ph);
/// a class representing a Window.
// @type Window
#line 101 "winapi.l.c"
#line 140 "winapi.l.c"
typedef struct {
HWND hwnd;
@@ -145,7 +186,7 @@ static int push_new_Window(lua_State *L,HWND h) {
static void Window_ctor(lua_State *L, Window *this, HWND h) {
#line 102 "winapi.l.c"
#line 141 "winapi.l.c"
this->hwnd = h;
}
@@ -162,7 +203,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function handle
static int l_Window_handle(lua_State *L) {
Window *this = Window_arg(L,1);
#line 117 "winapi.l.c"
#line 156 "winapi.l.c"
lua_pushnumber(L,(DWORD_PTR)this->hwnd);
return 1;
}
@@ -171,7 +212,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_text
static int l_Window_get_text(lua_State *L) {
Window *this = Window_arg(L,1);
#line 124 "winapi.l.c"
#line 163 "winapi.l.c"
GetWindowTextW(this->hwnd,wbuff,sizeof(wbuff));
return push_wstring(L,wbuff);
}
@@ -181,7 +222,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
static int l_Window_set_text(lua_State *L) {
Window *this = Window_arg(L,1);
const char *text = luaL_checklstring(L,2,NULL);
#line 131 "winapi.l.c"
#line 170 "winapi.l.c"
SetWindowTextW(this->hwnd,wstring(text));
return 0;
}
@@ -192,7 +233,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
static int l_Window_show(lua_State *L) {
Window *this = Window_arg(L,1);
int flags = luaL_optinteger(L,2,SW_SHOW);
#line 139 "winapi.l.c"
#line 178 "winapi.l.c"
ShowWindow(this->hwnd,flags);
return 0;
}
@@ -203,7 +244,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_position
static int l_Window_get_position(lua_State *L) {
Window *this = Window_arg(L,1);
#line 148 "winapi.l.c"
#line 187 "winapi.l.c"
RECT rect;
GetWindowRect(this->hwnd,&rect);
lua_pushinteger(L,rect.left);
@@ -217,7 +258,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_bounds
static int l_Window_get_bounds(lua_State *L) {
Window *this = Window_arg(L,1);
#line 160 "winapi.l.c"
#line 199 "winapi.l.c"
RECT rect;
GetWindowRect(this->hwnd,&rect);
lua_pushinteger(L,rect.right - rect.left);
@@ -229,7 +270,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function is_visible
static int l_Window_is_visible(lua_State *L) {
Window *this = Window_arg(L,1);
#line 170 "winapi.l.c"
#line 209 "winapi.l.c"
lua_pushboolean(L,IsWindowVisible(this->hwnd));
return 1;
}
@@ -238,7 +279,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function destroy
static int l_Window_destroy(lua_State *L) {
Window *this = Window_arg(L,1);
#line 177 "winapi.l.c"
#line 216 "winapi.l.c"
DestroyWindow(this->hwnd);
return 0;
}
@@ -255,7 +296,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
int y0 = luaL_checkinteger(L,3);
int w = luaL_checkinteger(L,4);
int h = luaL_checkinteger(L,5);
#line 188 "winapi.l.c"
#line 227 "winapi.l.c"
MoveWindow(this->hwnd,x0,y0,w,h,TRUE);
return 0;
}
@@ -271,7 +312,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
int msg = luaL_checkinteger(L,2);
double wparam = luaL_checknumber(L,3);
double lparam = luaL_checknumber(L,4);
#line 199 "winapi.l.c"
#line 238 "winapi.l.c"
lua_pushinteger(L,SendMessage(this->hwnd,msg,(WPARAM)wparam,(LPARAM)lparam));
return 1;
}
@@ -282,7 +323,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
static int l_Window_enum_children(lua_State *L) {
Window *this = Window_arg(L,1);
int callback = 2;
#line 207 "winapi.l.c"
#line 246 "winapi.l.c"
Ref ref;
sL = L;
ref = make_ref(L,callback);
@@ -295,7 +336,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_parent
static int l_Window_get_parent(lua_State *L) {
Window *this = Window_arg(L,1);
#line 218 "winapi.l.c"
#line 257 "winapi.l.c"
return push_new_Window(L,GetParent(this->hwnd));
}
@@ -303,7 +344,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_module_filename
static int l_Window_get_module_filename(lua_State *L) {
Window *this = Window_arg(L,1);
#line 224 "winapi.l.c"
#line 263 "winapi.l.c"
int sz = GetWindowModuleFileNameW(this->hwnd,wbuff,sizeof(wbuff));
wbuff[sz] = 0;
return push_wstring(L,wbuff);
@@ -315,7 +356,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_class_name
static int l_Window_get_class_name(lua_State *L) {
Window *this = Window_arg(L,1);
#line 234 "winapi.l.c"
#line 273 "winapi.l.c"
GetClassNameW(this->hwnd,wbuff,sizeof(wbuff));
return push_wstring(L,wbuff);
}
@@ -324,7 +365,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function set_foreground
static int l_Window_set_foreground(lua_State *L) {
Window *this = Window_arg(L,1);
#line 241 "winapi.l.c"
#line 280 "winapi.l.c"
lua_pushboolean(L,SetForegroundWindow(this->hwnd));
return 1;
}
@@ -333,7 +374,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function get_process
static int l_Window_get_process(lua_State *L) {
Window *this = Window_arg(L,1);
#line 248 "winapi.l.c"
#line 287 "winapi.l.c"
DWORD pid;
DWORD res = GetWindowThreadProcessId(this->hwnd,&pid);
return push_new_Process(L,pid,NULL);
@@ -343,7 +384,7 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
// @function __tostring
static int l_Window___tostring(lua_State *L) {
Window *this = Window_arg(L,1);
#line 256 "winapi.l.c"
#line 295 "winapi.l.c"
int ret;
int sz = GetWindowTextW(this->hwnd,wbuff,sizeof(wbuff));
if (sz > MAX_SHOW) {
@@ -359,12 +400,12 @@ static void Window_ctor(lua_State *L, Window *this, HWND h) {
static int l_Window___eq(lua_State *L) {
Window *this = Window_arg(L,1);
Window *other = Window_arg(L,2);
#line 269 "winapi.l.c"
#line 308 "winapi.l.c"
lua_pushboolean(L,this->hwnd == other->hwnd);
return 1;
}
#line 273 "winapi.l.c"
#line 312 "winapi.l.c"
static const struct luaL_reg Window_methods [] = {
{"handle",l_Window_handle},
@@ -397,7 +438,7 @@ static void Window_register (lua_State *L) {
}
#line 275 "winapi.l.c"
#line 314 "winapi.l.c"
/// Manipulating Windows.
// @section Windows
@@ -410,7 +451,7 @@ static void Window_register (lua_State *L) {
static int l_find_window(lua_State *L) {
const char *cname = lua_tostring(L,1);
const char *wname = lua_tostring(L,2);
#line 284 "winapi.l.c"
#line 323 "winapi.l.c"
HWND hwnd;
hwnd = FindWindow(cname,wname);
return push_new_Window(L,hwnd);
@@ -459,7 +500,7 @@ static int l_desktop_window(lua_State *L) {
// @function enum_windows
static int l_enum_windows(lua_State *L) {
int callback = 1;
#line 331 "winapi.l.c"
#line 370 "winapi.l.c"
Ref ref;
sL = L;
ref = make_ref(L,callback);
@@ -548,7 +589,7 @@ static int l_tile_windows(lua_State *L) {
int horiz = lua_toboolean(L,2);
int kids = 3;
int bounds = 4;
#line 415 "winapi.l.c"
#line 454 "winapi.l.c"
RECT rt;
HWND *kids_arr;
int i,n_kids;
@@ -575,15 +616,15 @@ static int l_tile_windows(lua_State *L) {
return 0;
}
/// Miscelaneous functions.
// @section Miscelaneous
/// Miscellaneous functions.
// @section miscellaneous
/// sleep and use no processing time.
// @param millisec sleep period
// @function sleep
static int l_sleep(lua_State *L) {
int millisec = luaL_checkinteger(L,1);
#line 448 "winapi.l.c"
#line 487 "winapi.l.c"
Sleep(millisec);
return 0;
}
@@ -602,7 +643,7 @@ static int l_show_message(lua_State *L) {
const char *msg = luaL_checklstring(L,2,NULL);
const char *btns = luaL_optlstring(L,3,"ok",NULL);
const char *icon = luaL_optlstring(L,4,"information",NULL);
#line 462 "winapi.l.c"
#line 501 "winapi.l.c"
int res, type;
WCHAR capb [512];
type = mb_const(btns) | mb_const(icon);
@@ -617,7 +658,7 @@ static int l_show_message(lua_State *L) {
// @function beep
static int l_beep(lua_State *L) {
const char *icon = luaL_optlstring(L,1,"ok",NULL);
#line 475 "winapi.l.c"
#line 514 "winapi.l.c"
return push_bool(L, MessageBeep(mb_const(icon)));
}
@@ -630,7 +671,7 @@ static int l_copy_file(lua_State *L) {
const char *src = luaL_checklstring(L,1,NULL);
const char *dest = luaL_checklstring(L,2,NULL);
int fail_if_exists = luaL_optinteger(L,3,0);
#line 484 "winapi.l.c"
#line 523 "winapi.l.c"
return push_bool(L, CopyFile(src,dest,fail_if_exists));
}
@@ -641,7 +682,7 @@ static int l_copy_file(lua_State *L) {
static int l_move_file(lua_State *L) {
const char *src = luaL_checklstring(L,1,NULL);
const char *dest = luaL_checklstring(L,2,NULL);
#line 492 "winapi.l.c"
#line 531 "winapi.l.c"
return push_bool(L, MoveFile(src,dest));
}
@@ -660,34 +701,18 @@ static int l_shell_exec(lua_State *L) {
const char *parms = lua_tostring(L,3);
const char *dir = lua_tostring(L,4);
int show = luaL_optinteger(L,5,SW_SHOWNORMAL);
#line 505 "winapi.l.c"
#line 544 "winapi.l.c"
WCHAR wverb[128], wfile[MAX_WPATH], wdir[MAX_WPATH], wparms[MAX_WPATH];
int res = (DWORD_PTR)ShellExecuteW(NULL,wconv(verb),wconv(file),wconv(parms),wconv(dir),show) > 32;
return push_bool(L, res);
}
/// set an environment variable for this process.
// Any child process inherits the environment.
// Note that this can't affect any system environment variables, see
// [here](http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx)
// for how to set these.
// @param name name of variable
// @param value value to set
// @function setenv
static int l_setenv(lua_State *L) {
const char *name = luaL_checklstring(L,1,NULL);
const char *value = luaL_checklstring(L,2,NULL);
#line 519 "winapi.l.c"
WCHAR wname[256],wvalue[MAX_WPATH];
return push_bool(L, SetEnvironmentVariableW(wconv(name),wconv(value)));
}
/// Copy text onto the clipboard.
// @param text the text
// @function set_clipboard
static int l_set_clipboard(lua_State *L) {
const char *text = luaL_checklstring(L,1,NULL);
#line 527 "winapi.l.c"
#line 553 "winapi.l.c"
HGLOBAL glob;
LPWSTR p;
int bufsize = 3*strlen(text);
@@ -728,22 +753,10 @@ static int l_get_clipboard(lua_State *L) {
return 1;
}
static int l_short_path_name(lua_State *L) {
const char *path = luaL_checklstring(L,1,NULL);
#line 568 "winapi.l.c"
WCHAR wpath[MAX_WPATH];
int res = GetShortPathNameW(wconv(path),wbuff,sizeof(wbuff));
if (res > 0) {
return push_wstring(L,wbuff);
} else {
return push_error(L);
}
}
/// A class representing a Windows process.
// this example was [helpful](http://msdn.microsoft.com/en-us/library/ms682623%28VS.85%29.aspx)
// @type Process
#line 584 "winapi.l.c"
#line 600 "winapi.l.c"
typedef struct {
HANDLE hProcess;
@@ -772,7 +785,7 @@ static int push_new_Process(lua_State *L,Int pid, HANDLE ph) {
static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
#line 585 "winapi.l.c"
#line 601 "winapi.l.c"
if (ph) {
this->pid = pid;
this->hProcess = ph;
@@ -790,7 +803,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
static int l_Process_get_process_name(lua_State *L) {
Process *this = Process_arg(L,1);
int full = lua_toboolean(L,2);
#line 600 "winapi.l.c"
#line 616 "winapi.l.c"
HMODULE hMod;
DWORD cbNeeded;
wchar_t modname[MAX_PATH];
@@ -811,7 +824,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function kill
static int l_Process_kill(lua_State *L) {
Process *this = Process_arg(L,1);
#line 619 "winapi.l.c"
#line 635 "winapi.l.c"
TerminateProcess(this->hProcess,0);
return 0;
}
@@ -822,7 +835,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function working_size
static int l_Process_working_size(lua_State *L) {
Process *this = Process_arg(L,1);
#line 628 "winapi.l.c"
#line 644 "winapi.l.c"
SIZE_T minsize, maxsize;
GetProcessWorkingSetSize(this->hProcess,&minsize,&maxsize);
lua_pushnumber(L,minsize/1024);
@@ -835,7 +848,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function start_time
static int l_Process_start_time(lua_State *L) {
Process *this = Process_arg(L,1);
#line 639 "winapi.l.c"
#line 655 "winapi.l.c"
FILETIME create,exit,kernel,user,local;
SYSTEMTIME time;
GetProcessTimes(this->hProcess,&create,&exit,&kernel,&user);
@@ -868,7 +881,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function run_times
static int l_Process_run_times(lua_State *L) {
Process *this = Process_arg(L,1);
#line 670 "winapi.l.c"
#line 686 "winapi.l.c"
FILETIME create,exit,kernel,user;
GetProcessTimes(this->hProcess,&create,&exit,&kernel,&user);
lua_pushnumber(L,fileTimeToMillisec(&user));
@@ -898,7 +911,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
static int l_Process_wait(lua_State *L) {
Process *this = Process_arg(L,1);
int timeout = luaL_optinteger(L,2,0);
#line 697 "winapi.l.c"
#line 713 "winapi.l.c"
return push_wait_result(L, WaitForSingleObject(this->hProcess, TIMEOUT(timeout)));
}
@@ -911,7 +924,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
static int l_Process_wait_for_input_idle(lua_State *L) {
Process *this = Process_arg(L,1);
int timeout = luaL_optinteger(L,2,0);
#line 707 "winapi.l.c"
#line 723 "winapi.l.c"
return push_wait_result(L, WaitForInputIdle(this->hProcess, TIMEOUT(timeout)));
}
@@ -921,7 +934,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function exit_code
static int l_Process_exit_code(lua_State *L) {
Process *this = Process_arg(L,1);
#line 715 "winapi.l.c"
#line 731 "winapi.l.c"
DWORD code;
GetExitCodeProcess(this->hProcess, &code);
lua_pushinteger(L,code);
@@ -932,7 +945,7 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
// @function close
static int l_Process_close(lua_State *L) {
Process *this = Process_arg(L,1);
#line 724 "winapi.l.c"
#line 740 "winapi.l.c"
CloseHandle(this->hProcess);
this->hProcess = NULL;
return 0;
@@ -940,12 +953,12 @@ static void Process_ctor(lua_State *L, Process *this, Int pid, HANDLE ph) {
static int l_Process___gc(lua_State *L) {
Process *this = Process_arg(L,1);
#line 730 "winapi.l.c"
#line 746 "winapi.l.c"
if (this->hProcess != NULL)
CloseHandle(this->hProcess);
return 0;
}
#line 734 "winapi.l.c"
#line 750 "winapi.l.c"
static const struct luaL_reg Process_methods [] = {
{"get_process_name",l_Process_get_process_name},
@@ -970,7 +983,7 @@ static void Process_register (lua_State *L) {
}
#line 736 "winapi.l.c"
#line 752 "winapi.l.c"
/// Working with processes.
// @section Processes
@@ -980,7 +993,7 @@ static void Process_register (lua_State *L) {
// @function process
static int l_process(lua_State *L) {
int pid = luaL_checkinteger(L,1);
#line 743 "winapi.l.c"
#line 759 "winapi.l.c"
return push_new_Process(L,pid,NULL);
}
@@ -1028,7 +1041,7 @@ static int l_wait_for_processes(lua_State *L) {
int processes = 1;
int all = lua_toboolean(L,2);
int timeout = luaL_optinteger(L,3,0);
#line 787 "winapi.l.c"
#line 803 "winapi.l.c"
int status, i;
Process *p;
int n = lua_objlen(L,processes);
@@ -1103,7 +1116,7 @@ void lcb_free(void *data) {
// `server` and `watch_for_file_changes` functions. Useful to kill a thread
// and free associated resources.
// @type Thread
#line 865 "winapi.l.c"
#line 881 "winapi.l.c"
typedef struct {
LuaCallback *lcb;
@@ -1132,7 +1145,7 @@ static int push_new_Thread(lua_State *L,PLuaCallback lcb, HANDLE thread) {
static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thread) {
#line 866 "winapi.l.c"
#line 882 "winapi.l.c"
this->lcb = lcb;
this->thread = thread;
}
@@ -1141,7 +1154,7 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
// @function suspend
static int l_Thread_suspend(lua_State *L) {
Thread *this = Thread_arg(L,1);
#line 873 "winapi.l.c"
#line 889 "winapi.l.c"
return push_bool(L, SuspendThread(this->thread) >= 0);
}
@@ -1149,7 +1162,7 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
// @function resume
static int l_Thread_resume(lua_State *L) {
Thread *this = Thread_arg(L,1);
#line 879 "winapi.l.c"
#line 895 "winapi.l.c"
return push_bool(L, ResumeThread(this->thread) >= 0);
}
@@ -1159,7 +1172,7 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
// @function kill
static int l_Thread_kill(lua_State *L) {
Thread *this = Thread_arg(L,1);
#line 887 "winapi.l.c"
#line 903 "winapi.l.c"
lcb_free(this->lcb);
return push_bool(L, TerminateThread(this->thread,1));
}
@@ -1170,7 +1183,7 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
static int l_Thread_set_priority(lua_State *L) {
Thread *this = Thread_arg(L,1);
int p = luaL_checkinteger(L,2);
#line 895 "winapi.l.c"
#line 911 "winapi.l.c"
return push_bool(L, SetThreadPriority(this->thread,p));
}
@@ -1178,7 +1191,7 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
// @function get_priority
static int l_Thread_get_priority(lua_State *L) {
Thread *this = Thread_arg(L,1);
#line 901 "winapi.l.c"
#line 917 "winapi.l.c"
int res = GetThreadPriority(this->thread);
if (res != THREAD_PRIORITY_ERROR_RETURN) {
lua_pushinteger(L,res);
@@ -1190,12 +1203,12 @@ static void Thread_ctor(lua_State *L, Thread *this, PLuaCallback lcb, HANDLE thr
static int l_Thread___gc(lua_State *L) {
Thread *this = Thread_arg(L,1);
#line 911 "winapi.l.c"
#line 927 "winapi.l.c"
// lcb_free(this->lcb); concerned that this cd kick in prematurely!
CloseHandle(this->thread);
return 0;
}
#line 915 "winapi.l.c"
#line 931 "winapi.l.c"
static const struct luaL_reg Thread_methods [] = {
{"suspend",l_Thread_suspend},
@@ -1216,7 +1229,7 @@ static void Thread_register (lua_State *L) {
}
#line 917 "winapi.l.c"
#line 933 "winapi.l.c"
typedef LPTHREAD_START_ROUTINE TCB;
@@ -1233,7 +1246,7 @@ int lcb_new_thread(TCB fun, void *data) {
/// this represents a raw Windows file handle.
// The write handle may be distinct from the read handle.
// @type File
#line 936 "winapi.l.c"
#line 952 "winapi.l.c"
typedef struct {
callback_data_
@@ -1262,7 +1275,7 @@ static int push_new_File(lua_State *L,HANDLE hread, HANDLE hwrite) {
static void File_ctor(lua_State *L, File *this, HANDLE hread, HANDLE hwrite) {
#line 937 "winapi.l.c"
#line 953 "winapi.l.c"
lcb_handle(this) = hread;
this->hWrite = hwrite;
this->L = L;
@@ -1276,7 +1289,7 @@ static void File_ctor(lua_State *L, File *this, HANDLE hread, HANDLE hwrite) {
static int l_File_write(lua_State *L) {
File *this = File_arg(L,1);
const char *s = luaL_checklstring(L,2,NULL);
#line 948 "winapi.l.c"
#line 964 "winapi.l.c"
DWORD bytesWrote;
WriteFile(this->hWrite, s, lua_objlen(L,2), &bytesWrote, NULL);
lua_pushinteger(L,bytesWrote);
@@ -1297,7 +1310,7 @@ static void File_ctor(lua_State *L, File *this, HANDLE hread, HANDLE hwrite) {
// @function read
static int l_File_read(lua_State *L) {
File *this = File_arg(L,1);
#line 967 "winapi.l.c"
#line 983 "winapi.l.c"
if (raw_read(this)) {
lua_pushstring(L,lcb_buf(this));
return 1;
@@ -1323,14 +1336,14 @@ static void File_ctor(lua_State *L, File *this, HANDLE hread, HANDLE hwrite) {
static int l_File_read_async(lua_State *L) {
File *this = File_arg(L,1);
int callback = 2;
#line 990 "winapi.l.c"
#line 1006 "winapi.l.c"
this->callback = make_ref(L,callback);
return lcb_new_thread((TCB)&file_reader,this);
}
static int l_File_close(lua_State *L) {
File *this = File_arg(L,1);
#line 995 "winapi.l.c"
#line 1011 "winapi.l.c"
if (this->hWrite != lcb_handle(this))
CloseHandle(this->hWrite);
lcb_free(this);
@@ -1339,11 +1352,11 @@ static void File_ctor(lua_State *L, File *this, HANDLE hread, HANDLE hwrite) {
static int l_File___gc(lua_State *L) {
File *this = File_arg(L,1);
#line 1002 "winapi.l.c"
#line 1018 "winapi.l.c"
free(this->buf);
return 0;
}
#line 1005 "winapi.l.c"
#line 1021 "winapi.l.c"
static const struct luaL_reg File_methods [] = {
{"write",l_File_write},
@@ -1363,11 +1376,26 @@ static void File_register (lua_State *L) {
}
#line 1007 "winapi.l.c"
#line 1023 "winapi.l.c"
/// Launching processes.
// @section Launch
/// set an environment variable for any child processes.
// Note that this can't affect any system environment variables, see
// [here](http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx)
// for how to set these.
// @param name name of variable
// @param value value to set
// @function setenv
static int l_setenv(lua_State *L) {
const char *name = luaL_checklstring(L,1,NULL);
const char *value = luaL_checklstring(L,2,NULL);
#line 1034 "winapi.l.c"
WCHAR wname[256],wvalue[MAX_WPATH];
return push_bool(L, SetEnvironmentVariableW(wconv(name),wconv(value)));
}
/// Spawn a process.
// @param program the command-line (program + parameters)
// @param dir the working directory for the process (optional)
@@ -1378,7 +1406,7 @@ static void File_register (lua_State *L) {
static int l_spawn(lua_State *L) {
const char *program = luaL_checklstring(L,1,NULL);
const char *dir = lua_tostring(L,2);
#line 1018 "winapi.l.c"
#line 1046 "winapi.l.c"
WCHAR wdir [MAX_WPATH];
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
SECURITY_DESCRIPTOR sd;
@@ -1447,6 +1475,12 @@ static int l_spawn(lua_State *L) {
// @return program output
// @function execute
/// execute a system command in Unicode mode.
// @param cmd a built-in command like dir
// @return return code
// @return text as UTF-8
// @function execute_unicode
// Timer support //////////
typedef struct {
callback_data_
@@ -1473,7 +1507,7 @@ static void timer_thread(TimerData *data) { // background timer thread
static int l_timer(lua_State *L) {
int msec = luaL_checkinteger(L,1);
int callback = 2;
#line 1110 "winapi.l.c"
#line 1144 "winapi.l.c"
TimerData *data = (TimerData *)malloc(sizeof(TimerData));
data->msec = msec;
lcb_callback(data,L,callback);
@@ -1527,7 +1561,7 @@ static void pipe_server_thread(PipeServerParms *parms) {
// @function open_pipe
static int l_open_pipe(lua_State *L) {
const char *pipename = luaL_optlstring(L,1,"\\\\.\\pipe\\luawinapi",NULL);
#line 1162 "winapi.l.c"
#line 1196 "winapi.l.c"
HANDLE hPipe = CreateFile(
pipename,
GENERIC_READ | // read and write access
@@ -1556,7 +1590,7 @@ static int l_open_pipe(lua_State *L) {
static int l_server(lua_State *L) {
int callback = 1;
const char *pipename = luaL_optlstring(L,2,"\\\\.\\pipe\\luawinapi",NULL);
#line 1188 "winapi.l.c"
#line 1222 "winapi.l.c"
PipeServerParms *psp = (PipeServerParms*)malloc(sizeof(PipeServerParms));
lcb_callback(psp,L,callback);
psp->pipename = pipename;
@@ -1603,9 +1637,74 @@ static void file_change_thread(FileChangeParms *fc) { // background file monitor
}
}
/// Drive information and watching directories.
/// Drive information and directories.
// @section Directories
/// the short path name of a directory or file.
// This is always in ASCII, 8.3 format. This function will create the
// file first if it does not exist; the result can be used to open
// files with unicode names.
// @param path multibyte encoded file path
// @return ASCII 8.3 format file path
// @function short_path_name
static int l_short_path_name(lua_State *L) {
const char *path = luaL_checklstring(L,1,NULL);
#line 1279 "winapi.l.c"
WCHAR wpath[MAX_WPATH];
HANDLE hFile;
int res;
wconv(path);
// if the file doesn't exist, then force its creation
hFile = CreateFileW(wpath,
GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
if (GetLastError() != ERROR_FILE_EXISTS) // that error is fine!
return push_error(L);
} else { // if we created it successfully, then close.
CloseHandle(hFile);
}
res = GetShortPathNameW(wpath,wbuff,sizeof(wbuff));
if (res > 0) {
return push_wstring(L,wbuff);
} else {
return push_error(L);
}
}
/// get a temporary filename.
// (Don't use os.tmpname)
// @return full path within temporary files directory.
// @function tmpname
/// delete a file or directory.
// @param file may be a wildcard
// @function delete
/// make a directory.
// Will make necessary subpaths if command extensions are enabled.
// @function mkdir
/// remove a directory.
// @param dir the directory
// @param tree if true, clean out the directory tree
// @function rmdir
/// iterator over directory contents.
// @param mask a file mask like "*.txt"
// @param subdirs iterate over subdirectories (default no)
// @param attrib iterate over items with given attribute (as in dir /A:)
// @function get_files
/// iterate over subdirectories
// @param file mask like "mydirs\\t*"
// @param subdirs iterate over subdirectories (default no)
// @see get_files
// @function get_dirs
/// get all the drives on this computer.
// @return a table of drive names
// @function get_logical_drives
@@ -1633,7 +1732,7 @@ static int l_get_logical_drives(lua_State *L) {
// @function get_drive_type
static int l_get_drive_type(lua_State *L) {
const char *root = luaL_checklstring(L,1,NULL);
#line 1263 "winapi.l.c"
#line 1360 "winapi.l.c"
UINT res = GetDriveType(root);
const char *type;
switch(res) {
@@ -1656,7 +1755,7 @@ static int l_get_drive_type(lua_State *L) {
// @function get_disk_free_space
static int l_get_disk_free_space(lua_State *L) {
const char *root = luaL_checklstring(L,1,NULL);
#line 1284 "winapi.l.c"
#line 1381 "winapi.l.c"
ULARGE_INTEGER freebytes, totalbytes;
if (! GetDiskFreeSpaceEx(root,&freebytes,&totalbytes,NULL)) {
return push_error(L);
@@ -1672,7 +1771,7 @@ static int l_get_disk_free_space(lua_State *L) {
// @function get_disk_network_name
static int l_get_disk_network_name(lua_State *L) {
const char *root = luaL_checklstring(L,1,NULL);
#line 1298 "winapi.l.c"
#line 1395 "winapi.l.c"
DWORD size = sizeof(wbuff);
DWORD res = WNetGetConnectionW(wstring(root),wbuff,&size);
if (res == NO_ERROR) {
@@ -1707,7 +1806,7 @@ static int l_watch_for_file_changes(lua_State *L) {
int how = luaL_checkinteger(L,2);
int subdirs = lua_toboolean(L,3);
int callback = 4;
#line 1328 "winapi.l.c"
#line 1425 "winapi.l.c"
FileChangeParms *fc = (FileChangeParms*)malloc(sizeof(FileChangeParms));
lcb_callback(fc,L,callback);
fc->how = how;
@@ -1729,7 +1828,7 @@ static int l_watch_for_file_changes(lua_State *L) {
/// Class representing Windows registry keys.
// @type regkey
#line 1352 "winapi.l.c"
#line 1449 "winapi.l.c"
typedef struct {
HKEY key;
@@ -1757,7 +1856,7 @@ static int push_new_regkey(lua_State *L,HKEY k) {
static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
#line 1353 "winapi.l.c"
#line 1450 "winapi.l.c"
this->key = k;
}
@@ -1769,7 +1868,7 @@ static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
regkey *this = regkey_arg(L,1);
const char *name = luaL_checklstring(L,2,NULL);
const char *val = luaL_checklstring(L,3,NULL);
#line 1361 "winapi.l.c"
#line 1458 "winapi.l.c"
return push_bool(L, RegSetValueEx(this->key,name,0,REG_SZ,val,lua_objlen(L,2)) == ERROR_SUCCESS);
}
@@ -1781,7 +1880,7 @@ static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
static int l_regkey_get_value(lua_State *L) {
regkey *this = regkey_arg(L,1);
const char *name = luaL_optlstring(L,2,"",NULL);
#line 1370 "winapi.l.c"
#line 1467 "winapi.l.c"
DWORD type,size = sizeof(wbuff);
if (RegQueryValueExW(this->key,wstring(name),0,&type,(void *)wbuff,&size) != ERROR_SUCCESS) {
return push_error(L);
@@ -1803,7 +1902,7 @@ static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
// @function get_keys
static int l_regkey_get_keys(lua_State *L) {
regkey *this = regkey_arg(L,1);
#line 1390 "winapi.l.c"
#line 1487 "winapi.l.c"
int i = 0;
LONG res;
DWORD size;
@@ -1827,7 +1926,7 @@ static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
// @function close
static int l_regkey_close(lua_State *L) {
regkey *this = regkey_arg(L,1);
#line 1412 "winapi.l.c"
#line 1509 "winapi.l.c"
RegCloseKey(this->key);
this->key = NULL;
return 0;
@@ -1835,13 +1934,13 @@ static void regkey_ctor(lua_State *L, regkey *this, HKEY k) {
static int l_regkey___gc(lua_State *L) {
regkey *this = regkey_arg(L,1);
#line 1418 "winapi.l.c"
#line 1515 "winapi.l.c"
if (this->key != NULL)
RegCloseKey(this->key);
return 0;
}
#line 1423 "winapi.l.c"
#line 1520 "winapi.l.c"
static const struct luaL_reg regkey_methods [] = {
{"set_value",l_regkey_set_value},
@@ -1861,7 +1960,7 @@ static void regkey_register (lua_State *L) {
}
#line 1425 "winapi.l.c"
#line 1522 "winapi.l.c"
/// Registry Functions.
// @section Registry
@@ -1875,7 +1974,7 @@ static void regkey_register (lua_State *L) {
static int l_open_key(lua_State *L) {
const char *path = luaL_checklstring(L,1,NULL);
int writeable = lua_toboolean(L,2);
#line 1435 "winapi.l.c"
#line 1532 "winapi.l.c"
HKEY hKey;
DWORD access;
char kbuff[1024];
@@ -1897,7 +1996,7 @@ static int l_open_key(lua_State *L) {
// @function create_key
static int l_create_key(lua_State *L) {
const char *path = luaL_checklstring(L,1,NULL);
#line 1455 "winapi.l.c"
#line 1552 "winapi.l.c"
char kbuff[1024];
HKEY hKey = split_registry_key(path,kbuff);
if (hKey == NULL) {
@@ -1910,7 +2009,7 @@ static int l_create_key(lua_State *L) {
}
}
#line 1537 "winapi.l.c"
#line 1634 "winapi.l.c"
static const char *lua_code_block = ""\
"function winapi.execute(cmd)\n"\
" cmd = os.getenv('COMSPEC')..' /c '..cmd\n"\
@@ -1970,7 +2069,7 @@ static const char *lua_code_block = ""\
" end\n"\
"end\n"\
"function winapi.mkdir(dir) return exec_cmd('mkdir',dir) end\n"\
"function winapi.rmdir(dir) return exec_cmd('rmdir',dir) end\n"\
"function winapi.rmdir(dir,tree) return exec_cmd('rmdir '.. (tree and '/S'),dir) end\n"\
"function winapi.delete(file) return exec_cmd('del',file) end\n"\
"function winapi.get_files(mask,subdirs,attrib)\n"\
" local flags = '/B '\n"\
@@ -1987,7 +2086,9 @@ static void load_lua_code (lua_State *L) {
}
#line 1539 "winapi.l.c"
#line 1637 "winapi.l.c"
/*** Constants.
The following constants are available:
@@ -2035,18 +2136,21 @@ The following constants are available:
* FILE\_ACTION\_RENAMED\_NEW\_NAME
@section constants
*/#line 1585 "winapi.l.c"
*/#line 1683 "winapi.l.c"
#line 1587 "winapi.l.c"
#line 1685 "winapi.l.c"
/// useful Windows API constants
// @table constants
#line 1632 "winapi.l.c"
#define CP_UTF16 -1
#line 1733 "winapi.l.c"
static void set_winapi_constants(lua_State *L) {
lua_pushinteger(L,CP_ACP); lua_setfield(L,-2,"CP_ACP");
lua_pushinteger(L,CP_UTF8); lua_setfield(L,-2,"CP_UTF8");
lua_pushinteger(L,CP_UTF16); lua_setfield(L,-2,"CP_UTF16");
lua_pushinteger(L,SW_HIDE); lua_setfield(L,-2,"SW_HIDE");
lua_pushinteger(L,SW_MAXIMIZE); lua_setfield(L,-2,"SW_MAXIMIZE");
lua_pushinteger(L,SW_MINIMIZE); lua_setfield(L,-2,"SW_MINIMIZE");
@@ -2088,11 +2192,12 @@ static void set_winapi_constants(lua_State *L) {
lua_pushinteger(L,FILE_ACTION_RENAMED_NEW_NAME); lua_setfield(L,-2,"FILE_ACTION_RENAMED_NEW_NAME");
}
#line 1634 "winapi.l.c"
#line 1735 "winapi.l.c"
static const luaL_reg winapi_funs[] = {
{"set_encoding",l_set_encoding},
{"get_encoding",l_get_encoding},
{"encode",l_encode},
{"uexpand",l_uexpand},
{"find_window",l_find_window},
{"foreground_window",l_foreground_window},
{"desktop_window",l_desktop_window},
@@ -2106,19 +2211,19 @@ static const luaL_reg winapi_funs[] = {
{"copy_file",l_copy_file},
{"move_file",l_move_file},
{"shell_exec",l_shell_exec},
{"setenv",l_setenv},
{"set_clipboard",l_set_clipboard},
{"get_clipboard",l_get_clipboard},
{"short_path_name",l_short_path_name},
{"process",l_process},
{"current_pid",l_current_pid},
{"current_process",l_current_process},
{"get_processes",l_get_processes},
{"wait_for_processes",l_wait_for_processes},
{"setenv",l_setenv},
{"spawn",l_spawn},
{"timer",l_timer},
{"open_pipe",l_open_pipe},
{"server",l_server},
{"short_path_name",l_short_path_name},
{"get_logical_drives",l_get_logical_drives},
{"get_drive_type",l_get_drive_type},
{"get_disk_free_space",l_get_disk_free_space},