local ffi = require "ffi"
local C = ffi.C
ffi.cdef [[
typedef struct { // _SECURITY_ATTRIBUTES
unsigned long nLength;
void* lpSecurityDescriptor;
int32_t bInheritHandle;
} SECURITY_ATTRIBUTES;
typedef struct { // _STARTUPINFOW
unsigned long cb;
wchar_t* lpReserved;
wchar_t* lpDesktop;
wchar_t* lpTitle;
unsigned long dwX;
unsigned long dwY;
unsigned long dwXSize;
unsigned long dwYSize;
unsigned long dwXCountChars;
unsigned long dwYCountChars;
unsigned long dwFillAttribute;
unsigned long dwFlags;
unsigned short wShowWindow;
unsigned short cbReserved2;
unsigned char* lpReserved2;
void* hStdInput;
void* hStdOutput;
void* hStdError;
} STARTUPINFOW;
typedef struct { // _PROCESS_INFORMATION
void* hProcess;
void* hThread;
unsigned long dwProcessId;
unsigned long dwThreadId;
} PROCESS_INFORMATION;
int32_t CreatePipe(void** hReadPipe,void** hWritePipe,SECURITY_ATTRIBUTES* lpPipeAttributes,unsigned long nSize);
int32_t CreateProcessW(const wchar_t* lpApplicationName,wchar_t* lpCommandLine,SECURITY_ATTRIBUTES* lpProcessAttributes,SECURITY_ATTRIBUTES* lpThreadAttributes,int32_t bInheritHandles,unsigned long dwCreationFlags,void* lpEnvironment,const wchar_t* lpCurrentDirectory,STARTUPINFOW* lpStartupInfo,PROCESS_INFORMATION* lpProcessInformation); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
int32_t ReadFile(void* hFile,void* lpBuffer,unsigned long nNumberOfBytesToRead,unsigned long* lpNumberOfBytesRead,void* lpOverlapped);
int32_t CloseHandle(void* hObject);
void* GetStdHandle(unsigned long nStdHandle);
int32_t GetLastError(void);
bool GetExitCodeProcess(HANDLE hProcess, int32_t* lpExitCode); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx
int32_t WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
int32_t IsTextUnicode(const void* lpv,int iSize,int* lpiResult);
]]
local advapi32 = ffi.load("advapi32")
local IS_TEXT_UNICODE_ASCII16 = 0x0001
local IS_TEXT_UNICODE_STATISTICS = 0x0002
local STILL_ACTIVE = 0x0103 -- = 259 = STATUS_PENDING in WinNT.h
local CREATE_NO_WINDOW = 0x8000000
local STD_INPUT_HANDLE = -10
local STARTF_USESTDHANDLES = 0x00000100
local INFINITE = 0xFFFFFFFF
local function split(data,callback)
while true do
local s,f=string.find(data,"[\n\r]")
if s then
local line=string.sub(data,1,s-1)
callback(line)
if string.sub(data,s,f+1)=="\r\n" then f=f+1 end
data=string.sub(data,f+1)
else
break
end
end
return data
end
local function Execute(command,folder,callback,...)
local exitcode = ffi.new("DWORD[1]")
local ret,err
local pipe_security=ffi.new("SECURITY_ATTRIBUTES")
callback = callback or function(...) panel.GetUserScreen() io.write(...) io.write("\n") panel.SetUserScreen() end
pipe_security.nLength=ffi.sizeof(pipe_security)
pipe_security.bInheritHandle=true
local pipe_in,pipe_out=ffi.new("void*[1]"),ffi.new("void*[1]")
if C.CreatePipe(pipe_in,pipe_out,pipe_security,0) then
local startup = ffi.new("STARTUPINFOW")
startup.cb = ffi.sizeof(startup)
startup.dwFlags = STARTF_USESTDHANDLES
startup.hStdInput = C.GetStdHandle(STD_INPUT_HANDLE)
startup.hStdOutput = pipe_out[0];
startup.hStdError = pipe_out[0];
local info = ffi.new("PROCESS_INFORMATION")
if C.CreateProcessW(
ffi.NULL,
command and ffi.cast("void*",win.Utf8ToUtf16(command.."\0")) or ffi.NULL,
ffi.NULL,ffi.NULL,true,CREATE_NO_WINDOW,ffi.NULL,
folder and ffi.cast("void*",win.Utf8ToUtf16(folder.."\0")) or ffi.NULL,
startup,info)~=0 then
C.CloseHandle(pipe_out[0])
local size = 1024
local buffer = ffi.new("char[?]",size)
local readed = ffi.new("unsigned long[1]")
local unicode = ffi.new("int[1]")
unicode[0] = bit64.bor(IS_TEXT_UNICODE_ASCII16,IS_TEXT_UNICODE_STATISTICS)
local output = ""
while true do
C.ReadFile(pipe_in[0],buffer,size,readed,ffi.NULL)
if readed[0]==0 then
break
else
output = output..(advapi32.IsTextUnicode(buffer,readed[0],unicode)==0 and win.OemToUtf8 or win.Utf16ToUtf8)(ffi.string(buffer,readed[0]))
output = split(output,callback)
end
end
if #output>0 then callback(output,...) end
--C.WaitForSingleObject(info.hProcess,INFINITE)
if C.GetExitCodeProcess(info.hProcess,exitcode) then
ret,err = exitcode[0],nil
else
ret,err = false,C.GetLastError()
end
C.CloseHandle(info.hThread)
C.CloseHandle(info.hProcess)
else
ret,err = nil,C.GetLastError()
end
C.CloseHandle(pipe_in[0])
end
return ret,err
end
return Execute