pipe - 建立匿名管道, 但是使用fopen来读写管道数据
概述
建立匿名管道后, 正常读写是用ReadFile, WriteFile.
看了资料, 可以用fead, fwrite来读写匿名管道数据.
中间有小坑, 需要注意中间转换用的文件句柄的复制, 关闭, 管道句柄的打开方式. 否则会出现读管道时卡住, 退出程序后报错.
一个没用的实验, 看以后会不会有用场.
笔记
/*!
* \file D:\my_dev\my_local_git_prj\study\openSSL\openssl_cmd_exp\case_enc_pwd_by_pipe\case_enc_pwd_by_pipe.cpp
* \note pipe - 建立匿名管道, 但是使用fopen来读写管道数据
* \ref https://learn.microsoft.com/zh-cn/windows/win32/ProcThread/creating-a-child-process-with-redirected-input-and-output
* \ref https://blog.csdn.net/winux/article/details/871770
* \ref https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/open-osfhandle?view=msvc-170
* \ref https://zhuanlan.zhihu.com/p/112549454 !!!
*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <cassert>
#include <io.h> // for _open_osfhandle
#include <fcntl.h> // for _O_TEXT
#define BUFSIZE 4096
bool WriteToPipeBy_WriteFile(IN HANDLE hPipe, IN const char* pszInfo, IN int lenInfo);
bool ReadFromPipeBy_ReadFile(IN HANDLE hPipe, OUT char* pszInfo, OUT int& lenInfo);
bool WriteToPipeBy_fwrite(IN HANDLE hPipe, IN const char* pszInfo, IN int lenInfo);
bool ReadFromPipeBy_fread(IN HANDLE hPipe, OUT char* pszInfo, OUT int& lenInfo);
void ErrorExit(LPCTSTR);
int _tmain(int argc, TCHAR* argv[])
{
char szBufIn[BUFSIZE];
char szBufOut[BUFSIZE];
HANDLE hPipeRead = NULL;
HANDLE hPipeWrite = NULL;
int len = 0;
bool b_rc = false;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
int fd_read = -1;
// 建立匿名管道
if (!CreatePipe(&hPipeRead, &hPipeWrite, NULL, BUFSIZE))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// ----------
// 1. 管道直接读写测试 - WriteFile/ReadFile
// ----------
// 向管道中写东西
strcpy(szBufIn, "1. hello");
b_rc = WriteToPipeBy_WriteFile(hPipeWrite, szBufIn, (int)strlen(szBufIn));
assert(b_rc);
// 从管道中是否能读到东西
len = (int)sizeof(szBufOut);
memset(szBufOut, 0, sizeof(szBufOut));
b_rc = ReadFromPipeBy_ReadFile(hPipeRead, szBufOut, len);
assert(b_rc && (strlen(szBufIn) == len));
// ----------
// 2. 管道直接读写测试 - fWrite/ReadFile
// ----------
// 向管道中写东西
strcpy(szBufIn, "2. hello");
b_rc = WriteToPipeBy_fwrite(hPipeWrite, szBufIn, (int)strlen(szBufIn));
assert(b_rc);
// 从管道中是否能读到东西
len = (int)sizeof(szBufOut);
memset(szBufOut, 0, sizeof(szBufOut));
b_rc = ReadFromPipeBy_ReadFile(hPipeRead, szBufOut, len);
assert(b_rc && (strlen(szBufIn) == len));
// ----------
// 3. 管道直接读写测试 - WriteFile/fread
// ----------
// 向管道中写东西
strcpy(szBufIn, "3. hello");
b_rc = WriteToPipeBy_WriteFile(hPipeWrite, szBufIn, (int)strlen(szBufIn));
assert(b_rc);
// 从管道中是否能读到东西
len = (int)sizeof(szBufOut);
b_rc = ReadFromPipeBy_fread(hPipeRead, szBufOut, len);
assert(b_rc && (strlen(szBufIn) == len));
// ----------
// 4. 管道直接读写测试 - fwirte/fread
// ----------
// 向管道中写东西
strcpy(szBufIn, "4. hello");
b_rc = WriteToPipeBy_fwrite(hPipeWrite, szBufIn, (int)strlen(szBufIn));
assert(b_rc);
// 从管道中是否能读到东西
len = (int)sizeof(szBufOut);
b_rc = ReadFromPipeBy_fread(hPipeRead, szBufOut, len);
assert(b_rc && (strlen(szBufIn) == len));
// ----------
// 5. 管道中写入数据, 将管道句柄转成fd, 给openssl干活用
// ----------
// 向管道中写东西, e.g. 口令
strcpy(szBufIn, "111111");
b_rc = WriteToPipeBy_fwrite(hPipeWrite, szBufIn, (int)strlen(szBufIn));
assert(b_rc);
// openssl参数只接受文件名或文本的直接数据, 无法通过管道(匿名管道,命名管道)传递命令行参数
// 将管道句柄转成fd后, 给openssl干活用
// fd_read = _open_osfhandle((intptr_t)hPipeRead, _O_RDWR | _O_BINARY);
// assert(fd_read >= 0);
// memset(szBufIn, 0, sizeof(szBufIn));
// openssl enc -e -aes-256-cbc -pbkdf2 -in hello.txt -out hello.txt.enc -pass pass:111111
// sprintf(szBufIn, "C:\\openssl_3d2\\bin\\openssl.exe enc -e -aes-256-cbc -pbkdf2 -in hello.txt -out hello.txt.enc -pass file:%d", fd_read);
// system(szBufIn);
// ----------
// 关闭管道
// ----------
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);
return 0;
}
bool WriteToPipeBy_WriteFile(IN HANDLE hPipe, IN const char* pszInfo, IN int lenInfo)
{
DWORD dwWritten = 0;
BOOL bSuccess = FALSE;
assert(NULL != hPipe);
assert(NULL != pszInfo);
bSuccess = WriteFile(hPipe, pszInfo, lenInfo, &dwWritten, NULL);
assert(bSuccess && (lenInfo == dwWritten));
printf("WriteToPipeBy_WriteFile : %s\n", pszInfo);
return (bSuccess ? true : false);
}
bool ReadFromPipeBy_ReadFile(IN HANDLE hPipe, OUT char* pszInfo, OUT int& lenInfo)
{
DWORD dwRead = 0;
BOOL bOk = FALSE;
BOOL bSuccess = FALSE;
assert(NULL != hPipe);
do {
if (NULL == pszInfo)
{
break;
}
memset(pszInfo, 0, lenInfo);
bSuccess = ReadFile(hPipe, pszInfo, lenInfo, &dwRead, NULL);
lenInfo = (int)dwRead;
bOk = (bSuccess || (0 != dwRead));
assert(bOk);
printf("ReadFromPipeBy_ReadFile : %s\n", pszInfo);
} while (false);
return (bOk);
}
bool WriteToPipeBy_fwrite(IN HANDLE hPipe, IN const char* pszInfo, IN int lenInfo)
{
DWORD dwWritten = 0;
bool bSuccess = FALSE;
int fd = 0;
FILE* fp = NULL;
size_t szCnt = 0;
int dup_fd = 0;
assert(NULL != hPipe);
assert(NULL != pszInfo);
fd = _open_osfhandle((intptr_t)hPipe, _O_RDWR | _O_BINARY);
assert(fd >= 0);
dup_fd = _dup(fd); // !!! 不要直接使用fd, 会有问题(如果关闭fp, 管道也被关掉了; 如果不关闭fp, 程序离开main()后报错).
fp = _fdopen(dup_fd, "w");
szCnt = fwrite(pszInfo, sizeof(char), lenInfo, fp);
bSuccess = (szCnt == lenInfo);
assert(bSuccess);
fflush(fp); // ! fwrite操作是带缓存的, 必须fwrite后, flush到管道中. 否则后续读管道读不到这里写入的东西
printf("WriteToPipeBy_fwrite : %s\n", pszInfo);
// 如果要从FILE* 转到 fd, 使用以下代码
//fd = _fileno(fp);
//assert(fd >= 0);
// 如果要从fd转到 OS_HANDLE, 使用以下代码
//hPipe = (HANDLE)_get_osfhandle(fd);
//assert(NULL != hPipe);
fclose(fp);
// 不用关闭fd
return bSuccess;
}
bool ReadFromPipeBy_fread(IN HANDLE hPipe, OUT char* pszInfo, OUT int& lenInfo)
{
DWORD dwWritten = 0;
bool bSuccess = FALSE;
int fd = 0;
FILE* fp = NULL;
int dup_fd = 0;
size_t szCnt = 0;
assert(NULL != hPipe);
assert(NULL != pszInfo);
// 管道的读不只是读, 因为读一个少一个, 应该要有写的权限
fd = _open_osfhandle((intptr_t)hPipe, _O_RDWR | _O_BINARY);
assert(fd >= 0);
dup_fd = _dup(fd); // !!! 不要直接使用fd, 会有问题(如果关闭fp, 管道也被关掉了; 如果不关闭fp, 程序离开main()后报错).
fp = _fdopen(dup_fd, "w"); // 这里必须是w, 否则后面fread时卡住
assert(fp);
memset(pszInfo, 0, lenInfo);
szCnt = fread(pszInfo, sizeof(char), lenInfo, fp);
bSuccess = (szCnt > 0);
assert(bSuccess);
lenInfo = (int)szCnt;
printf("ReadFromPipeBy_fread : %s\n", pszInfo);
fd = _fileno(fp);
assert(fd >= 0);
hPipe = (HANDLE)_get_osfhandle(fd);
assert(NULL != hPipe);
fclose(fp);
// 不用关闭fd
return bSuccess;
}
void ErrorExit(LPCTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}