Skip to content

Instantly share code, notes, and snippets.

@Oleksiy-Yakovenko
Created January 25, 2018 16:39
Show Gist options
  • Save Oleksiy-Yakovenko/4f65cb89903c0e08e44555a55501bce5 to your computer and use it in GitHub Desktop.
Save Oleksiy-Yakovenko/4f65cb89903c0e08e44555a55501bce5 to your computer and use it in GitHub Desktop.
cross platform pipe IPC implementation in C++
#include "stdafx.h"
#define USE_POPEN 1
#if PLATFORM_WINDOWS
#define popen _popen
#define pclose _pclose
#endif
class Pipe
{
private:
#if USE_POPEN
FILE *fp;
#else
HANDLE hChildStd_IN_Rd;
HANDLE hChildStd_IN_Wr;
HANDLE hChildStd_OUT_Rd;
HANDLE hChildStd_OUT_Wr;
HANDLE hChildStd_ERR_Rd;
HANDLE hChildStd_ERR_Wr;
PROCESS_INFORMATION piProcInfo;
#endif
public:
Pipe();
~Pipe()
{
Close();
}
int Open(const char *cmdline, bool write=false);
int Close();
size_t Read(char *buffer, size_t size);
size_t Write(char *buffer, size_t size);
};
#if USE_POPEN
Pipe::Pipe()
{
fp = NULL;
}
int Pipe::Open(const char *cmdLine, bool write)
{
fp = popen(cmdLine, write ? "wb" : "rb");
if (!fp)
return -1;
return 0;
}
int Pipe::Close()
{
if (fp)
{
pclose(fp);
fp = NULL;
}
return 0;
}
size_t Pipe::Read(char *buffer, size_t size)
{
return fread(buffer, 1, size, fp);
}
size_t Pipe::Write(char *buffer, size_t size)
{
return fwrite(buffer, 1, size, fp);
}
#else
Pipe::Pipe()
{
hChildStd_IN_Rd = NULL;
hChildStd_IN_Wr = NULL;
hChildStd_OUT_Rd = NULL;
hChildStd_OUT_Wr = NULL;
hChildStd_ERR_Rd = NULL;
hChildStd_ERR_Wr = NULL;
}
int Pipe::Open(const char *cmdLine, bool write)
{
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))
return -1;
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!write)
{
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
return -1;
}
else
{
if (!SetHandleInformation(hChildStd_OUT_Wr, HANDLE_FLAG_INHERIT, 0))
return -1;
}
// Create a pipe for the child process's STDERR.
if (!CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &saAttr, 0))
return -1;
// Ensure the read handle to the pipe for STDERR is not inherited.
if (!write)
{
if (!SetHandleInformation(hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0))
return -1;
}
else
{
if (!SetHandleInformation(hChildStd_ERR_Wr, HANDLE_FLAG_INHERIT, 0))
return -1;
}
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0))
return -1;
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
return -1;
STARTUPINFOA siStartInfo;
BOOL bSuccess = FALSE;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_ERR_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.hStdInput = hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
bSuccess = CreateProcessA(NULL,
(char *)cmdLine, // 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)
return -1;
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
if (write)
{
CloseHandle(hChildStd_OUT_Rd);
hChildStd_OUT_Rd = NULL;
CloseHandle(hChildStd_OUT_Wr);
hChildStd_OUT_Wr = NULL;
CloseHandle(hChildStd_ERR_Rd);
hChildStd_ERR_Rd = NULL;
CloseHandle(hChildStd_ERR_Wr);
hChildStd_ERR_Wr = NULL;
CloseHandle(hChildStd_IN_Rd);
hChildStd_IN_Rd = NULL;
}
else
{
CloseHandle(hChildStd_IN_Rd);
hChildStd_IN_Rd = NULL;
CloseHandle(hChildStd_IN_Wr);
hChildStd_IN_Wr = NULL;
// CloseHandle(hChildStd_OUT_Rd);
// hChildStd_OUT_Rd = NULL;
CloseHandle(hChildStd_OUT_Wr);
hChildStd_OUT_Wr = NULL;
CloseHandle(hChildStd_ERR_Rd);
hChildStd_ERR_Rd = NULL;
CloseHandle(hChildStd_ERR_Wr);
hChildStd_ERR_Wr = NULL;
}
return 0;
}
int Pipe::Close()
{
if (hChildStd_IN_Rd)
{
CloseHandle(hChildStd_IN_Rd);
hChildStd_IN_Rd = NULL;
}
if (hChildStd_IN_Wr)
{
CloseHandle(hChildStd_IN_Wr);
hChildStd_IN_Wr = NULL;
}
if (hChildStd_OUT_Rd)
{
CloseHandle(hChildStd_OUT_Rd);
hChildStd_OUT_Rd = NULL;
}
if (hChildStd_OUT_Wr)
{
CloseHandle(hChildStd_OUT_Wr);
hChildStd_OUT_Wr = NULL;
}
if (hChildStd_ERR_Rd)
{
CloseHandle(hChildStd_ERR_Rd);
hChildStd_ERR_Rd = NULL;
}
if (hChildStd_ERR_Wr)
{
CloseHandle(hChildStd_ERR_Wr);
hChildStd_ERR_Wr = NULL;
}
return 0;
}
size_t Pipe::Read(char *buffer, size_t size)
{
// check if the process terminated
DWORD dwRead;
BOOL bSuccess = ReadFile(hChildStd_OUT_Rd, buffer, size, &dwRead, NULL);
return bSuccess ? dwRead : 0;
}
size_t Pipe::Write(char *buffer, size_t size)
{
DWORD dwWritten;
BOOL bSuccess = WriteFile(hChildStd_IN_Wr, buffer, size, &dwWritten, NULL);
return bSuccess ? dwWritten : 0;
}
#endif
int main()
{
Pipe pipeIn;
pipeIn.Open("ffmpeg.exe -i input.mp3 -map_metadata -1 -f wav -c:a pcm_f32le -");
Pipe pipeOut;
pipeOut.Open("opusenc.exe --ignorelength --bitrate 128 - output.opus", true);
char buf[16384];
size_t res = 0;
do
{
res = pipeIn.Read(buf, sizeof(buf));
pipeOut.Write(buf, res);
} while (res != 0);
pipeIn.Close();
pipeOut.Close();
return 0;
}
@robkrakora
Copy link

Nice job!

@hidegi
Copy link

hidegi commented May 29, 2022

windows = cross platform??

@Oleksiy-Yakovenko
Copy link
Author

@skyphonics1666 as far as I remember, when I wrote this code, it was supposed to work on mac+windows. I don't remember whether I tested it on Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment