Взаимодействие io.popen() и cmd

hookernice

Новичок
Автор темы
4
0
Версия MoonLoader
Другое
Решил написать код, который выполняет запросы для командной строки Windows прямо в SA:MP, но столкнулся с большой проблемой, обнаружив, что при выполнении в игре функции io.popen() неизбежно открываеться окно консоли как при открытии bat-файла, что приводит к принудительному сворачиванию игры, и это, очевидно, крайне неудобно (даже при переводе окна консоли в "nul 2>&1" игра все равно сворачивается хоть уже и без самого окошка). Хотел бы спросить: возможно ли это избежать и по моей задумке выполнять команды и получать вывод с этих команд? Буду очень благодарен любому решению, мне хочется как минимум понять что можно попробовать.


Lua:
require("lib.moonloader")
require("lib.sampfuncs")

function main()
    while not isSampAvailable() do wait(123) end

    -- начать выполнение
    sampRegisterChatCommand('Test', function()
        exec = lua_thread.create(exectest)
    end)

    wait(-1)
end

function exectest()

    -- выполнение команды
    local handle = io.popen("help")

    -- вывод результата построчно
    while true do
        wait(0)
        local line = handle:read()
        if line == nil then
            break
        end
        sampAddChatMessage(line)
    end
    handle:close()
end
 

yung milonov

Известный
1,031
534
вот кому-то делать нехуй конечно
может и проще можно, может вовсе в пару строк можно, ну мне похуй так-то
учитывай что на 49 строке меняется кодировка в консоли, чтоб в сампе нормально это вывелось, по дефолту в консоли CP866 стоит
пиздец:
local ffi = require("ffi")

ffi.cdef[[
    typedef void* HANDLE;
    typedef struct {
        unsigned long nLength;
        void* lpSecurityDescriptor;
        int bInheritHandle;
    } SECURITY_ATTRIBUTES;

    typedef struct {
        unsigned long cb;
        char* lpReserved;
        char* lpDesktop;
        char* lpTitle;
        unsigned long dwX, dwY, dwXSize, dwYSize;
        unsigned long dwXCountChars, dwYCountChars;
        unsigned long dwFillAttribute, dwFlags;
        unsigned short wShowWindow, cbReserved2;
        unsigned char* lpReserved2;
        HANDLE hStdInput, hStdOutput, hStdError;
    } STARTUPINFO;

    typedef struct {
        HANDLE hProcess, hThread;
        unsigned long dwProcessId, dwThreadId;
    } PROCESS_INFORMATION;

    int CreatePipe(HANDLE* hReadPipe, HANDLE* hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, unsigned long nSize);
    int CreateProcessA(const char* lpApplicationName, char* lpCommandLine, void* lpProcessAttributes, void* lpThreadAttributes,
                    int bInheritHandles, unsigned long dwCreationFlags, void* lpEnvironment, const char* lpCurrentDirectory,
                    STARTUPINFO* lpStartupInfo, PROCESS_INFORMATION* lpProcessInformation);
    int ReadFile(HANDLE hFile, char* lpBuffer, unsigned long nNumberOfBytesToRead, unsigned long* lpNumberOfBytesRead, void* lpOverlapped);
    int CloseHandle(HANDLE hObject);
]]

function exec(command)
    local si = ffi.new("STARTUPINFO")
    si.cb = ffi.sizeof("STARTUPINFO")
    local pi = ffi.new("PROCESS_INFORMATION")
    local sa = ffi.new("SECURITY_ATTRIBUTES")
    sa.nLength, sa.bInheritHandle = ffi.sizeof("SECURITY_ATTRIBUTES"), 1
    local readPipe, writePipe = ffi.new("HANDLE[1]"), ffi.new("HANDLE[1]")
    if ffi.C.CreatePipe(readPipe, writePipe, sa, 0) == 0 then
        return nil
    end
    si.dwFlags, si.hStdOutput, si.hStdError = 0x100, writePipe[0], writePipe[0]

    local fullCmd = "cmd.exe /c chcp 1251 >nul && " .. command
    local cmdBuf = ffi.new("char[?]", #fullCmd + 1)
    ffi.copy(cmdBuf, fullCmd)
    if ffi.C.CreateProcessA(nil, cmdBuf, nil, nil, 1, 0x08000000, nil, nil, si, pi) == 0 then
        ffi.C.CloseHandle(readPipe[0])
        ffi.C.CloseHandle(writePipe[0])
        return nil
    end
    ffi.C.CloseHandle(writePipe[0])
    local buffer, bytesRead, output = ffi.new("char[4096]"), ffi.new("unsigned long[1]"), ""
    while true do
        if ffi.C.ReadFile(readPipe[0], buffer, 4096, bytesRead, nil) == 0 then break end
        if bytesRead[0] == 0 then break end
        output = output .. ffi.string(buffer, bytesRead[0])
    end

    ffi.C.CloseHandle(readPipe[0])
    ffi.C.CloseHandle(pi.hProcess)
    ffi.C.CloseHandle(pi.hThread)
    return output
end

function main()
    sampRegisterChatCommand("test123", function()
        local result, err = exec("help")
        for line in result:gmatch("[^\r\n]+") do
            sampAddChatMessage(line, -1)
        end
    end)
    wait(-1)
end
 

hookernice

Новичок
Автор темы
4
0
вот кому-то делать нехуй конечно
может и проще можно, может вовсе в пару строк можно, ну мне похуй так-то
учитывай что на 49 строке меняется кодировка в консоли, чтоб в сампе нормально это вывелось, по дефолту в консоли CP866 стоит
пиздец:
local ffi = require("ffi")

ffi.cdef[[
    typedef void* HANDLE;
    typedef struct {
        unsigned long nLength;
        void* lpSecurityDescriptor;
        int bInheritHandle;
    } SECURITY_ATTRIBUTES;

    typedef struct {
        unsigned long cb;
        char* lpReserved;
        char* lpDesktop;
        char* lpTitle;
        unsigned long dwX, dwY, dwXSize, dwYSize;
        unsigned long dwXCountChars, dwYCountChars;
        unsigned long dwFillAttribute, dwFlags;
        unsigned short wShowWindow, cbReserved2;
        unsigned char* lpReserved2;
        HANDLE hStdInput, hStdOutput, hStdError;
    } STARTUPINFO;

    typedef struct {
        HANDLE hProcess, hThread;
        unsigned long dwProcessId, dwThreadId;
    } PROCESS_INFORMATION;

    int CreatePipe(HANDLE* hReadPipe, HANDLE* hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, unsigned long nSize);
    int CreateProcessA(const char* lpApplicationName, char* lpCommandLine, void* lpProcessAttributes, void* lpThreadAttributes,
                    int bInheritHandles, unsigned long dwCreationFlags, void* lpEnvironment, const char* lpCurrentDirectory,
                    STARTUPINFO* lpStartupInfo, PROCESS_INFORMATION* lpProcessInformation);
    int ReadFile(HANDLE hFile, char* lpBuffer, unsigned long nNumberOfBytesToRead, unsigned long* lpNumberOfBytesRead, void* lpOverlapped);
    int CloseHandle(HANDLE hObject);
]]

function exec(command)
    local si = ffi.new("STARTUPINFO")
    si.cb = ffi.sizeof("STARTUPINFO")
    local pi = ffi.new("PROCESS_INFORMATION")
    local sa = ffi.new("SECURITY_ATTRIBUTES")
    sa.nLength, sa.bInheritHandle = ffi.sizeof("SECURITY_ATTRIBUTES"), 1
    local readPipe, writePipe = ffi.new("HANDLE[1]"), ffi.new("HANDLE[1]")
    if ffi.C.CreatePipe(readPipe, writePipe, sa, 0) == 0 then
        return nil
    end
    si.dwFlags, si.hStdOutput, si.hStdError = 0x100, writePipe[0], writePipe[0]

    local fullCmd = "cmd.exe /c chcp 1251 >nul && " .. command
    local cmdBuf = ffi.new("char[?]", #fullCmd + 1)
    ffi.copy(cmdBuf, fullCmd)
    if ffi.C.CreateProcessA(nil, cmdBuf, nil, nil, 1, 0x08000000, nil, nil, si, pi) == 0 then
        ffi.C.CloseHandle(readPipe[0])
        ffi.C.CloseHandle(writePipe[0])
        return nil
    end
    ffi.C.CloseHandle(writePipe[0])
    local buffer, bytesRead, output = ffi.new("char[4096]"), ffi.new("unsigned long[1]"), ""
    while true do
        if ffi.C.ReadFile(readPipe[0], buffer, 4096, bytesRead, nil) == 0 then break end
        if bytesRead[0] == 0 then break end
        output = output .. ffi.string(buffer, bytesRead[0])
    end

    ffi.C.CloseHandle(readPipe[0])
    ffi.C.CloseHandle(pi.hProcess)
    ffi.C.CloseHandle(pi.hThread)
    return output
end

function main()
    sampRegisterChatCommand("test123", function()
        local result, err = exec("help")
        for line in result:gmatch("[^\r\n]+") do
            sampAddChatMessage(line, -1)
        end
    end)
    wait(-1)
end
Спасибо, а реально сделать так чтобы вывод не буферизовался? Потому что даже при выводе результата в самой функции exec вместо ожидания переменной output игра все равно зависает пока не выполнится запрос. Например, если запускать так файл пайтона, который выполняется 5 секунд, то игра зависает на 5 секунд пока программа не завершится. В моём предыдущем коде достаточно было поставить флаг -u для последовательного вывода результата от программы, но даже без него игра не зависала. Возможно это осуществить и для предложенного кода?
Lua:
function exec(command)
    local si = ffi.new("STARTUPINFO")
    si.cb = ffi.sizeof("STARTUPINFO")
    local pi = ffi.new("PROCESS_INFORMATION")
    local sa = ffi.new("SECURITY_ATTRIBUTES")
    sa.nLength, sa.bInheritHandle = ffi.sizeof("SECURITY_ATTRIBUTES"), 1
    local readPipe, writePipe = ffi.new("HANDLE[1]"), ffi.new("HANDLE[1]")
    if ffi.C.CreatePipe(readPipe, writePipe, sa, 0) == 0 then
        return nil
    end
    si.dwFlags, si.hStdOutput, si.hStdError = 0x100, writePipe[0], writePipe[0]

    local fullCmd = "cmd.exe /c chcp 1251 >nul && " .. command
    local cmdBuf = ffi.new("char[?]", #fullCmd + 1)
    ffi.copy(cmdBuf, fullCmd)
    if ffi.C.CreateProcessA(nil, cmdBuf, nil, nil, 1, 0x08000000, nil, nil, si, pi) == 0 then
        ffi.C.CloseHandle(readPipe[0])
        ffi.C.CloseHandle(writePipe[0])
        return nil
    end
    ffi.C.CloseHandle(writePipe[0])
    local buffer, bytesRead = ffi.new("char[4096]"), ffi.new("unsigned long[1]")
    while true do
        wait(0)
        if ffi.C.ReadFile(readPipe[0], buffer, 4096, bytesRead, nil) == 0 then break end
        if bytesRead[0] == 0 then break end
        sampAddChatMessage(ffi.string(buffer, bytesRead[0]))
    end

    ffi.C.CloseHandle(readPipe[0])
    ffi.C.CloseHandle(pi.hProcess)
    ffi.C.CloseHandle(pi.hThread)
end

function main()
    sampRegisterChatCommand("test123", function()
        thExec = lua_thread.create(exec, "help")
    end)
    wait(-1)
end