pipe - 建立管道, 但是使用fopen来读写管道数据

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);
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值