gcc 将输入管道输入时出现错误文件描述符错误
gcc Bad File Descriptor Error When Piping Input Into It
我发现您可以使用 gcc -x c -
从 stdin 使用 gcc 编译 c 代码,命令 cat main.c | gcc -x c -
成功编译它。
所以,我有一个 vb6 应用程序,它在字符串中包含 C 代码,我想将该 C 代码通过管道传输到带有 win32 的 gcc api,顺便说一句,我安装了 mingw。
以下基本代码创建输入管道并调用 gcc,但是在 CreateProcess
之后立即将以下内容打印到控制台:
cc1.exe: fatal error: stdout: Bad file descriptor
compilation terminated.
也就是说,在我将任何内容放入输入管道之前打印该错误消息
Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Function SetHandleInformation Lib "kernel32" (ByVal hObject As Long, ByVal dwMask As Long, ByVal dwFlags As Long) As Long
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessW" (ByVal lpApplicationName As Long, ByVal lpCommandLine As Long, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDriectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, Optional ByVal lpOverlapped As Long = 0) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0) As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function AllocConsole Lib "kernel32" () As Long
Private Declare Function FreeConsole Lib "kernel32" () As Long
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Const HANDLE_FLAG_INHERIT As Long = 1
Const STARTF_USESTDHANDLES As Long = &H100
Public Function Execute(CmdLine As String) As Boolean
ExecuteWithStdIn "gcc -x c -", "#error if gcc receives this code it shoud complain about this error directive"
End Function
'Pipes String StdIn into The Stdin Of The Child Process
'Output Of Child Process Is Redirected Into My StdOut
Public Function ExecuteWithStdIn(CmdLine As String, StdIn As String) As Boolean
Dim sAttr As SECURITY_ATTRIBUTES
Dim E As Long
Dim stdin_read As Long
Dim stdin_write As Long
AllocConsole
sAttr.nLength = LenB(sAttr)
sAttr.bInheritHandle = 1
E = CreatePipe(stdin_read, stdin_write, sAttr, 0)
E = SetHandleInformation(stdin_read, HANDLE_FLAG_INHERIT, 0)
Dim CmdLine As String
Dim Proci As PROCESS_INFORMATION 'all 0s
Dim Sti As STARTUPINFO
Sti.cb = LenB(Sti)
Sti.hStdError = GetStdHandle(-11)
Sti.hStdOutput = GetStdHandle(-11) 'Piping gcc output to my output
Sti.hStdInput = stdin_read 'Capturing the input so we may pipe the c code
Sti.dwFlags = STARTF_USESTDHANDLES
E = CreateProcess(0, StrPtr(CmdLine), 0, 0, 1, 0, 0, 0, Sti, Proci)
'### When The Code Reaches Here, E = 1 (Success) And gcc has already
'### outputted to stdout that Bad File Descriptor error message
E = GetLastError
Dim Buffer() As Byte
Dim BufferPtr As Long
Dim BufferSize As Long
Dim BytesWritten As Long
Let Buffer = StrConv(StdIn, vbFromUnicode)
BufferPtr = VarPtr(Buffer(0))
BufferSize = UBound(Buffer)
Do
If BufferSize > 4096 Then
E = WriteFile(stdin_write, BufferPtr, 4096, BytesWritten)
Else
E = WriteFile(stdin_write, BufferPtr, BufferSize, BytesWritten)
End If
If E = 0 Then Exit Do
BufferPtr = BufferPtr + BytesWritten
BufferSize = BufferSize - BytesWritten
Loop While BufferSize > 0
E = CloseHandle(stdin_read)
FreeConsole
End Function
您的 stdout 可以被子进程继承吗?其实是需要的。与 stderr.
相同
Sti.hStdOutput = GetStdHandle(-11) 'Piping gcc output to my output
您可以参考以下适合我的C++代码:
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
SECURITY_ATTRIBUTES s;
s.nLength = sizeof(SECURITY_ATTRIBUTES);
s.bInheritHandle = TRUE;
s.lpSecurityDescriptor = NULL;
HANDLE hin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &s, OPEN_EXISTING, 0, 0);
SetStdHandle(STD_INPUT_HANDLE, hin);
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
//siStartInfo.hStdInput = hin;
//siStartInfo.hStdInput = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
非常感谢@YangXiaoPro 的回答,你对句柄继承的评论让我明白了这一行:
SetHandleInformation(stdin_read, HANDLE_FLAG_INHERIT, 0)
阻止 管道 reader 被继承的做法是错误的。相反,它应该阻止 pipe writer 被继承
SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0) 'fixed
我发现您可以使用 gcc -x c -
从 stdin 使用 gcc 编译 c 代码,命令 cat main.c | gcc -x c -
成功编译它。
所以,我有一个 vb6 应用程序,它在字符串中包含 C 代码,我想将该 C 代码通过管道传输到带有 win32 的 gcc api,顺便说一句,我安装了 mingw。
以下基本代码创建输入管道并调用 gcc,但是在 CreateProcess
之后立即将以下内容打印到控制台:
cc1.exe: fatal error: stdout: Bad file descriptor
compilation terminated.
也就是说,在我将任何内容放入输入管道之前打印该错误消息
Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Function SetHandleInformation Lib "kernel32" (ByVal hObject As Long, ByVal dwMask As Long, ByVal dwFlags As Long) As Long
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessW" (ByVal lpApplicationName As Long, ByVal lpCommandLine As Long, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDriectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, Optional ByVal lpOverlapped As Long = 0) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0) As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function AllocConsole Lib "kernel32" () As Long
Private Declare Function FreeConsole Lib "kernel32" () As Long
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Const HANDLE_FLAG_INHERIT As Long = 1
Const STARTF_USESTDHANDLES As Long = &H100
Public Function Execute(CmdLine As String) As Boolean
ExecuteWithStdIn "gcc -x c -", "#error if gcc receives this code it shoud complain about this error directive"
End Function
'Pipes String StdIn into The Stdin Of The Child Process
'Output Of Child Process Is Redirected Into My StdOut
Public Function ExecuteWithStdIn(CmdLine As String, StdIn As String) As Boolean
Dim sAttr As SECURITY_ATTRIBUTES
Dim E As Long
Dim stdin_read As Long
Dim stdin_write As Long
AllocConsole
sAttr.nLength = LenB(sAttr)
sAttr.bInheritHandle = 1
E = CreatePipe(stdin_read, stdin_write, sAttr, 0)
E = SetHandleInformation(stdin_read, HANDLE_FLAG_INHERIT, 0)
Dim CmdLine As String
Dim Proci As PROCESS_INFORMATION 'all 0s
Dim Sti As STARTUPINFO
Sti.cb = LenB(Sti)
Sti.hStdError = GetStdHandle(-11)
Sti.hStdOutput = GetStdHandle(-11) 'Piping gcc output to my output
Sti.hStdInput = stdin_read 'Capturing the input so we may pipe the c code
Sti.dwFlags = STARTF_USESTDHANDLES
E = CreateProcess(0, StrPtr(CmdLine), 0, 0, 1, 0, 0, 0, Sti, Proci)
'### When The Code Reaches Here, E = 1 (Success) And gcc has already
'### outputted to stdout that Bad File Descriptor error message
E = GetLastError
Dim Buffer() As Byte
Dim BufferPtr As Long
Dim BufferSize As Long
Dim BytesWritten As Long
Let Buffer = StrConv(StdIn, vbFromUnicode)
BufferPtr = VarPtr(Buffer(0))
BufferSize = UBound(Buffer)
Do
If BufferSize > 4096 Then
E = WriteFile(stdin_write, BufferPtr, 4096, BytesWritten)
Else
E = WriteFile(stdin_write, BufferPtr, BufferSize, BytesWritten)
End If
If E = 0 Then Exit Do
BufferPtr = BufferPtr + BytesWritten
BufferSize = BufferSize - BytesWritten
Loop While BufferSize > 0
E = CloseHandle(stdin_read)
FreeConsole
End Function
您的 stdout 可以被子进程继承吗?其实是需要的。与 stderr.
相同Sti.hStdOutput = GetStdHandle(-11) 'Piping gcc output to my output
您可以参考以下适合我的C++代码:
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
SECURITY_ATTRIBUTES s;
s.nLength = sizeof(SECURITY_ATTRIBUTES);
s.bInheritHandle = TRUE;
s.lpSecurityDescriptor = NULL;
HANDLE hin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &s, OPEN_EXISTING, 0, 0);
SetStdHandle(STD_INPUT_HANDLE, hin);
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
//siStartInfo.hStdInput = hin;
//siStartInfo.hStdInput = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
非常感谢@YangXiaoPro 的回答,你对句柄继承的评论让我明白了这一行:
SetHandleInformation(stdin_read, HANDLE_FLAG_INHERIT, 0)
阻止 管道 reader 被继承的做法是错误的。相反,它应该阻止 pipe writer 被继承
SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0) 'fixed