{"id":13579141,"url":"https://github.com/stevedonovan/winapi","last_synced_at":"2025-06-12T20:32:52.115Z","repository":{"id":1542858,"uuid":"1866290","full_name":"stevedonovan/winapi","owner":"stevedonovan","description":"Minimal but useful Lua bindings to the Windows API","archived":false,"fork":false,"pushed_at":"2021-09-29T15:46:47.000Z","size":482,"stargazers_count":200,"open_issues_count":28,"forks_count":42,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-06-12T20:32:00.299Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://stevedonovan.github.com/winapi/api.html","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevedonovan.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-06-08T16:22:43.000Z","updated_at":"2025-06-09T14:15:16.000Z","dependencies_parsed_at":"2022-07-15T10:30:32.238Z","dependency_job_id":null,"html_url":"https://github.com/stevedonovan/winapi","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/stevedonovan/winapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Fwinapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Fwinapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Fwinapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Fwinapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevedonovan","download_url":"https://codeload.github.com/stevedonovan/winapi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Fwinapi/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259522453,"owners_count":22870469,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T15:01:36.887Z","updated_at":"2025-06-12T20:32:52.092Z","avatar_url":"https://github.com/stevedonovan.png","language":"C","funding_links":[],"categories":["C","Resources"],"sub_categories":["Native OS APIs"],"readme":"# winapi A useful Windows API subset for Lua\n\nThis 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.\n\n## Creating and working with Processes\n\nAn  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.\n\n@{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.\n\nThis 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_process}.\n\nApart 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):\n\n    winapi.shell_exec('open','myold.doc')\n\nOr an explorer window for a directory:\n\n    winapi.shell_exec('open','\\\\users\\\\steve\\\\lua')\n\nNote 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:\n\n    winapi.shell_exec(nil,'scite','wina.lua')\n\nThe 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:\n\n    winapi.shell_exec(nil,'notepad','wina.lua',nil,winapi.SW_MINIMIZE)\n\nFor fine control over console programs, use @{spawn_process} - 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.\n\n    \u003e proc,file = winapi.spawn_process 'cmd /c dir /b'\n    \u003e = file:read()\n    bonzo.lc\n    cexport.lua\n    class1.c\n    ...\n    \u003e = proc:wait()\n    userdata: 0000000000539608      OK\n    \u003e = proc:exit_code()\n    0\n\nIf the command is invalid, then you will get an error message instead:\n\n    \u003e = winapi.spawn_process 'frodo'\n    nil     The system cannot find the file specified.\n\nThis is what @{execute} does under the hood, but doing it explicitly gives you more control.  For instance, the @{Process: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'.\n\n    local _,status = proc:wait(500)\n    if status == 'TIMEOUT' then\n      proc:kill()\n    end\n\nThe 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 @{File: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.\n\nHaving a @{File: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:\n\n    \u003e proc,file = winapi.spawn_process [[lua -e \"io.stdout:setvbuf('no')\" -i]]\n    \u003e = file:read()  -- always read the program banner first!\n    Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio\n    \u003e\n    \u003e = file:write 'print \"hello\"\\n'\n    14\n    \u003e = file:read()\n    hello\n    \u003e\n    \u003e proc:kill()\n\n(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)\n\nNote that reading the result also returns the prompt '\u003e', which isn't so obvious if we're running Lua from within Lua itself. It's clearer when using Python:\n\n    \u003e proc,file = winapi.spawn_process [[python -i]]\n    \u003e = file:read()\n    Python 2.6.2c1 (r262c1:71369, Apr  7 2009, 18:44:00) [MSC v.1500 32 bit (Intel)]\n     on win32\n    Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n    \u003e\u003e\u003e\n    \u003e file:write '40+2\\n'\n    \u003e = file:read()\n    42\n    \u003e\u003e\u003e\n\nThis kind of interactive process capture is fine for a console application, but @{File:read} is blocking and will freeze any GUI program. For this, you use @{File:read_async} which returns the result through a callback. Continuing the Python example:\n\n    \u003e file:write '40+2\\n'\n    \u003e file:read_async(function(s) print('++',s) end)\n    \u003e ++    42\n    \u003e\u003e\u003e\n\nThis can work nicely with Lua coroutines, allowing us to write pseudo-blocking code for interacting with processes.\n\nThe process object can provide more useful information:\n\n    \u003e = proc:working_size()\n    200     1380\n    \u003e = proc:run_times()\n    0       31\n\n@{Process:get_working_size} gives you a lower and an upper bound on the process memory in kB; @{Process:get_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.\n\nIt is possible to wait on more than one process at a time. Consider this simple time-wasting script:\n\n    for i = 1,1e8 do end\n\nIt takes me 0.743 seconds to do this, with stock Lua 5.1. But running two such scripts in parallel is about the same speed (0.776):\n\n    require 'winapi'\n    local t = os.clock()\n    local P = {}\n    P[1] = winapi.spawn_process 'lua slow.lua'\n    P[2] = winapi.spawn_process 'lua slow.lua'\n    winapi.wait_for_processes(P,true)\n    print(os.clock() - t)\n\nSo 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 @{Process:wait} method, it has an optional timeout parameter.\n\nThe `true` parameter forces it to wait until _all_ the proceses are finished. Jf successful, `wait_for_processes` will return the index of the exiting process in the array of processes, so by using `false` we can wait for any process to finish, deal with the results, and continue waiting for the others. This is how Lake does [multithreading](https://github.com/stevedonovan/Lake/blob/master/lake#L376) on Windows.\n\n## Working with Windows\n\nThe `Window` 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:\n\n    \u003e = winapi.get_desktop_window():get_bounds()\n    1600    900\n\nFinding 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.)\n\n@{find_all_windows} returns all windows matching some function. For convenience, two useful matchers are provided, @{make_name_matcher} and @{make_class_matcher}. Once you have a group of related windows, you can do fun things like tile them:\n\n    \u003e t = winapi.find_all_windows(winapi.make_name_matcher '- SciTE')\n    \u003e = #t\n    2\n    \u003e winapi.tile_windows(winapi.get_desktop_window(),false,t)\n\nThis 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}`.\n\nWith tiling and the ability to hide windows with `w:show(winapi.SW_HIDE)` it is entirely possible to write a little 'virtual desktop' application.\n\n@{find_window_ex} also uses a matcher function; @{find_window_match} is a shortcut for the operation of finding a window by its caption.\n\nEvery window has an associated text value. For top-level windows, this is the window caption:\n\n    \u003e = winapi.get_foreground_window()\n    Command Prompt - lua -lwinapi\n\nSo the equivalent of the old DOS command `title` would here be:\n\n    winapi.get_foreground_window():set_text 'My new title'\n\nAny top-level window will contain child windows. For example, Notepad has a simple structure revealed by @{Window:enum_children}:\n\n    \u003e w = winapi.find_window_match 'Notepad'\n    \u003e = w\n    Untitled - Notepad\n    \u003e t = {}\n    \u003e w:enum_children(function(w) table.insert(t,w) end)\n    \u003e = #t\n    2\n    \u003e = t[1]:get_class_name()\n    Edit\n    \u003e = t[2]:get_class_name()\n    msctls_statusbar32\n\nWindows controls like the 'Edit' control interact with the unverse via messages.\n`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:\n\n    \u003e = t[1]:send_message(186,0,0)\n    6\n\nAn entertaining way to automate some programs is to send virtual keystrokes to them. The function @{send_to_window} sends characters to the current foreground window:\n\n    \u003e winapi.send_to_window '= 20 + 10\\n'\n    \u003e = 20 + 10\n    30\n\nAfter launching a window, you can make it the foreground window and send it text:\n\n    P = winapi.spawn_process 'notepad'\n    P:wait_for_input_idle()\n    w = winapi.find_window_match 'Untitled'\n    w:show()\n    w:set_foreground()\n    winapi.send_to_window 'hello dammit'\n\nWaiting on the process is important: it gives the other process a chance to get going, and to create a new window which we can promote.\n\n## Working with Text Encoding\n\nLua has internally no concept of text encoding; strings are sequences of bytes. This means that the string functions cannot generally give you the correct length of a UTF-8 encoded string, for instance. Internally, Windows uses UTF-16 and winapi gives you several options for passing and getting lua strings from Windows.\n\nAn important point is that you can choose to use UTF-8 encoding with winapi. This little program shows how:\n\n    -- @{caption.lua}\n    local W = require 'winapi'\n    W.set_encoding(W.CP_UTF8)\n    win = W.foreground_window()\n    win:set_text 'ελληνική'\n\nWhen run in SciTE, it successfully puts a little bit of Greek in the title bar.\n\n@{encode} can translate text explicitly between encodings; `winapi.enode(ein,eout,text)` where the encodings can be one of the `winapi.CP_ACP`, `winapi_UTF8` and `winapi_UTF16` constants.\n\n@{utf8_expand} will expand '#' two-byte Unicode hex constants:\n\n    local U = winapi.utf8_expand\n    txt = U '#03BB + #03BC + C'\n    print(txt)\n    print(U '#03BD')\n    ---\u003e OUTPUT\n    λ + μ + C\n    ν\n\nYou may work internally in UTF-8 and get a suitable _short file name_ for working with files in Lua.\n\n    -- @{testshort.lua}\n    name = winapi.short_path 'ελληνική.txt'\n    print(name)\n    local f,err = io.open(name,'w')\n    f:write 'a new file\\n'\n    f:close()\n\nA filename with the correct Greek name appears in Explorer, and can be edited with any Unicode-aware application like Notepad.\n\n## Working with Processes\n\n@{get_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:\n\n    \u003e w = winapi.get_foreground_window()\n    \u003e = w\n    Command Prompt - lua -lwinapi\n    \u003e p = w:get_process()\n    \u003e = p:get_process_name()\n    cmd.exe\n    \u003e = p:get_process_name(true)\n    C:\\WINDOWS\\system32\\cmd.exe\n\n(Note that the @{Process:get_process_name} method can optionally give you the full path to the process.)\n\nTo get all the current processes:\n\n    pids = winapi.get_processes()\n\n    for _,pid in ipairs(pids) do\n       local P = winapi.process_from_id(pid)\n       local name = P:get_process_name(true)\n       if name then print(pid,name) end\n       P:close()\n    end\n\n\n## Drive and Directory Operations\n\nThere 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.\n\n    -- @{drives.lua}\n    require 'winapi'\n\n    drives = winapi.get_logical_drives()\n    for _,drive in ipairs(drives) do\n        local free,avail = winapi.get_disk_free_space(drive)\n        if not free then -- call failed, avail is error\n            free = '('..avail..')'\n        else\n            free = math.ceil(free/1024) -- get Mb\n        end\n        local rname = ''\n        local dtype = winapi.get_drive_type(drive)\n        if dtype == 'remote' then  -- note it wants the drive letter!\n            rname = winapi.get_disk_network_name(drive:gsub('\\\\$',''))\n        end\n        print(drive,dtype,free,rname)\n    end\n\nThis script gives the following output on my home machine:\n\n    C:\\\tfixed\t218967\n    F:\\\tfixed\t1517\n    G:\\\tcdrom\t(The device is not ready.)\n    Q:\\\tfixed\t(Access is denied.)\n\nOr at work:\n\n    C:\\\tfixed\t1455\n    D:\\\tfixed\t49996\n    E:\\\tcdrom\t(The device is not ready.)\n    G:\\\tremote\t33844\t\\\\CARL-VFILE\\SYS\n    I:\\\tremote\t452789\t\\\\CARL-VFILE\\GROUPS\n    X:\\\tremote\t12160\t\\\\CARL-VFILE\\APPS\n    Y:\\\tremote\t33844\t\\\\CARL-VFILE\\SYS\\PUBLIC\n    Z:\\\tremote\t33844\t\\\\CARL-VFILE\\SYS\\PUBLIC\n\nA 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.\n\n    winapi.watch_for_file_changes(mydir,winapi.FILE_NOTIFY_CHANGE_LAST_WRITE,FALSE,\n        function(what,who)\n            -- 'what' will be winapi.FILE_ACTION_MODIFIED\n            -- 'who' will be the name of the file that changed\n            print(what,who)\n        end\n    )\n\nUsing a callback means that you can watch multiple directories and still respond to timers, etc.\n\n 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:\n\n    local status,output = winapi.execute('dir /B')\n    local files = {}\n    for f in output:gmatch '[^\\r\\n]+' do\n        table.insert(files,f)\n    end\n\n## Output\n\nGUI applications do not have a console so @{print} does not work. @{show_message} will put up a message box to bother users. Here is the old favourite, system message boxes:\n\n    print(winapi.show_message(\"Message\",\"stuff\\nand nonsense\",\"yes-no\",\"warning\"))\n\nThe 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.\n\nOr you may prefer to irritate the user with a sound:\n\n    winapi.beep 'warning'\n\n@{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.\n\n## Timers and Callbacks\n\nIt 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:\n\n    winapi.make_timer(500,function()\n        text:append 'gotcha'\n    end)\n\nSuch callbacks can be made GUI-safe by first calling @{use_gui} which ensures that any callback is called in the main GUI thread. You _must_ do this if integrating winapi with GUI toolkits such as [wxLua](?) or [IUP](?).\n\nThe basic rule for callbacks enforced by `winapi` is that only one may be active at a time; otherwise we would risk re-entering Lua on another thread, using the same Lua state. So be quick when responding to callbacks, since they effectively block Lua. If possible, use asynchronous code - for instance `Process:wait_async` if you are launchhing a new process, or `File:read_async` for reading from a file.\n\nFor a console application, callbacks only happen when the thread is sleeping. the best bet (after setting some timers and so forth) is just to sleep indefinitely:\n\n    winapi.sleep(-1)\n\nTo show what happens in an interactive prompt if you don't follow this rule:\n\n    \u003e winapi.timer(500,function() end)\n    \u003e = 23\n    nil     nil     return  23\n\nIn short: completely messed!\n\nIt's possible to read from the console asynchronously, which allows you to write servers which are responsive to interactive commands.\n\n    f = winapi.get_console()\n    f:read_async(function(line)\n      f:write(line)\n      if line:match '^quit' then\n        os.exit()\n      end\n    end)\n\n    winapi.sleep(-1)\n\nPlease note that you will get the end-of-line characters as well.\n\nAs of version 1.4, this console file object can also be waited on using `wait_for_processes`, which gives another way of handling commands. That function also supports a timeout, hence this entertaining little program which reads from the console and runs another operation every 500 ms.\n\n    local W = require 'winapi'\n    local f = W.get_console()\n    local title = W.get_foreground_window()\n    local count = 1\n    f:write '? '\n    while true do\n      local res = W.wait_for_processes({f},false,500)\n      if res == 1 then\n        local line = f:read()\n        if not line then break end\n        -- strip line feed\n        line = line:gsub('\\r\\n$','')\n        if line == 'quit' then break end\n        print(line:upper())\n        f:write '? '\n      else\n        title:set_text('counting '..count)\n        count = count + 1\n      end\n    end\n\nThe console handle is signalled as soon as you type any character, but the read will block until a whole line is entered. This explains why the manic caption updating stops while you're entering a line.  Please note that another alertable handles (like events or threads) can be waited on _as well_ in this way.\n\n\n## Reading from the Registry\n\nThe registry is an unavoidable part of living with Windows, since much useful information can be found in it, if you know the key.\n\nFor instance, the environment for the _current user_ can be queried:\n\n    local key = winapi.open_reg_key [[HKEY_CURRENT_USER\\Environment]]\n    print(key:get_value(\"PATH\"))\n    k:close()\n\nAnd `Regkey:set_value` will then allow you to update this path, which is useful for install programs. In that case, set the optional second argument to `true` to get write-access.\n\nThis has an optional third parameter, which is the data type of the key: `winapi` has the constants `REG_BINARY`,`REG_DWORD`, `REG_SZ`, `REG_MULTI_SZ` and `REG_EXPAND_SZ`. `REG_DWORD` can be passed a _number_ value, and `REG_BINARY` is passed a plain un-encoded binary Lua string; all the other types use the current encoding. The `REG_MULTI_SZ` type is special, and requires strings that look like \"alice\\0bob\\0\".\n\n`Regkey:get_keys` will return a list of all subkeys of the current key.\n\nWhen finished, it's best to explicitly use the `close` method.\n\n## Pipe Server\n\nInterprocess 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.\n\nLife 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  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.\n\n    require 'winapi'\n\n    winapi.server(function(file)\n      file:read_async(function(s)\n        print('['..s..']')\n      end)\n    end)\n\n    winapi.sleep(-1)\n\nLike 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 @{File: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.\n\nThe client can connect in a very straightforward way, but note that as with Unix pipes you have to flush the output to actually physically write to the pipe:\n\n    \u003e f = io.open('\\\\\\\\.\\\\pipe\\\\luawinapi','w')\n    \u003e f:write 'hello server!\\n'\n    \u003e f:flush()\n    \u003e f:close()\n\nand our server will say:\n\n    [hello server!\n    ]\n    []\n\n(Note that @{File:read} receives an _empty string_ when the handle is closed.)\n\nHowever, 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 @{File:read} and @{File:write} are regarded as receive/send events.\n\nThe server can do something to the received string and pass it back:\n\n    winapi.server(function(file)\n      file:read_async(function(s)\n       if s == '' then\n         print 'client disconnected'\n       else\n        file:write (s:upper())\n       end\n     end)\n    end)\n\nOn the client side:\n\n    f = winapi.open_pipe()\n    f:write 'hello\\n'\n    print(f:read()) -- HELLO\n    f:write 'dog\\n'\n    print(f:read()) -- DOG\n    f:close()\n\nAnother 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))\n\n## Events\n\nEvents are kernel-level synchronization objects in Windows. Initially they are 'unsignaled' and `Event:wait` will pause until they become signaled by calling `Event:signal`.\n\n    local W = require 'winapi'\n    local e = W.event()\n    local count = 1\n\n    W.make_timer(500,function()\n        print 'finis'\n        if count == 5 then\n            os.exit()\n        end\n        e:signal()\n        count = count + 1\n    end)\n\n    while true do\n        e:wait()\n        print 'gotcha'\n    end\n\nThere is also `Event:wait_async` to avoid blocking in a callback.\n\nThey can optionally be given a name, and can then work across _different processes_.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevedonovan%2Fwinapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevedonovan%2Fwinapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevedonovan%2Fwinapi/lists"}