Lynxer

Documentation

Windows API access

On Windows, Lynxer calls the Win32 API through the built-in winAPI.* surface. It is not a module — there is no import — and it is gated by the same syscalls(...) target selector the named Linux syscalls use:

global setup(){ syscalls("winAPI", "amd64"); }

global main(){
    println(winAPI.getProcessId());
    println(winAPI.getComputerName());
    println(winAPI.getCurrentDirectory());
}

Every call is kernel32, so nothing extra has to be installed or linked.

Selecting the target

syscalls("<os>", "<arch>") must run before any winAPI.* call.

KeywordKindSelects
winAPI, windows, win32operating systemthe Win32 surface
amd64, x86-64architecturex86-64
arm64, aarch64architectureAArch64

Conventions

System information

FunctionReturns
winAPI.getProcessId()the current process id
winAPI.getCurrentDirectory()the current directory
winAPI.getComputerName()the computer name
winAPI.getTempPath()the temporary directory (with a trailing separator)
winAPI.getSystemDirectory()the system directory
winAPI.getWindowsDirectory()the Windows directory
winAPI.getModuleFileName()the running executable's full path
winAPI.getTickCount()milliseconds since the system started
winAPI.getLastError()the last Win32 error code
winAPI.getDiskFreeBytes(path)free bytes on the volume, or -1
winAPI.sleep(milliseconds)—
winAPI.beep(frequency, duration)whether it played
winAPI.outputDebugString(text)— (goes to the debugger)

Environment

FunctionReturns
winAPI.getEnvironmentVariable(name)the variable's value, or "" when unset
winAPI.setEnvironmentVariable(name, value)whether it was set
winAPI.expandEnvironmentStrings(text)text with %VAR% expanded

Files and handles

FunctionReturns
winAPI.createFile(path, mode)a handle, or -1
winAPI.readFile(handle, length)up to length bytes, or ""
winAPI.writeFile(handle, text)bytes written, or -1
winAPI.closeHandle(handle)whether it closed
winAPI.fileSize(handle)size in bytes, or -1
winAPI.seekFile(handle, offset)the new absolute position, or -1
winAPI.deleteFile(path)whether it was removed
winAPI.copyFile(source, destination, overwrite)whether it was copied
winAPI.moveFile(source, destination)whether it was moved
winAPI.createDirectory(path)whether it was created
winAPI.removeDirectory(path)whether it was removed
winAPI.getFileAttributes(path)the attribute bits, or -1
winAPI.setFileAttributes(path, attributes)whether they were set
winAPI.getFullPathName(path)the absolute path, or ""
winAPI.flushFileBuffers(handle)whether the buffers were flushed

mode is "read" (OPEN_EXISTING), "write" (CREATE_ALWAYS, truncated), "append" (OPEN_ALWAYS, seek to end) or "readwrite" (OPEN_ALWAYS). seekFile positions absolutely from the start of the file.

global setup(){ syscalls("winAPI", "amd64"); }

global main(){
    str path = winAPI.getTempPath() + "demo.txt";

    int file = winAPI.createFile(path, "write");
    if (file < 0) {
        println("cannot create: " + winAPI.getLastError());
        return;
    }
    winAPI.writeFile(file, "hello");
    winAPI.closeHandle(file);

    int reader = winAPI.createFile(path, "read");
    println(winAPI.fileSize(reader));
    println(winAPI.readFile(reader, 64));
    winAPI.closeHandle(reader);
    winAPI.deleteFile(path);
}

Processes and threads

FunctionReturns
winAPI.getCurrentThreadId()the calling thread's id
winAPI.openProcess(processId)a handle with query/terminate/synchronize access, or -1
winAPI.terminateProcess(handle, exitCode)whether it was terminated
winAPI.getExitCodeProcess(handle)the exit code, or -1 (259 while still alive)
winAPI.createProcess(commandLine)the new process's handle, or -1
winAPI.waitForSingleObject(handle, milliseconds)0 when signalled, 258 on timeout, -1 on failure

createProcess starts commandLine through CreateProcessW with the inherited environment and no redirection; the thread handle is closed for you, so only the returned process handle needs closeHandle.

Console

These talk to the console the process is attached to; under a redirected stream or a non-interactive session they report -1/false rather than failing the call.

FunctionReturns
winAPI.getStdHandle(stream)the handle for "input", "output" or "error", or -1
winAPI.getConsoleMode(handle)the console mode, or -1
winAPI.setConsoleMode(handle, mode)whether it was set
winAPI.setConsoleTitle(text)whether the title was set
winAPI.getConsoleOutputCP()the console output code page
winAPI.setConsoleOutputCP(codePage)whether it was set

Timing

FunctionReturns
winAPI.queryPerformanceCounter()the high-resolution counter, or -1
winAPI.queryPerformanceFrequency()ticks per second, or -1

The long tail

Anything not in the tables above — other DLLs, user32, advapi32, the *A/*W pairs, structs, HANDLE-typed parameters — is reachable through the FFI built-ins (native-modules). On 64-bit Windows the Win32 calling convention is the one cdecl: describes, so a Win32 call looks like any other:

global setup(){ syscalls("winAPI", "amd64"); }

global main(){
    int user32 = ffiLoadLibrary("user32.dll");
    functionAddress box = ffiLookup(user32, "MessageBoxA");
    ffiCall(box, "cdecl:int32(int64,cstring,cstring,int32)",
            [int 0, str "hello", str "lynxer", int 0]);
    ffiCloseLibrary(user32);
}

A curated wrapper over that — more DLLs and first-class HANDLE values — is tracked in ../todo.

See also