处理句柄的实用指南






4.73/5 (23投票s)
2001 年 4 月 5 日
5分钟阅读

294862
文件句柄及其与 File *、CFile、CStdioFile 等的关系之奥秘。
免责声明
我正在撰写这篇技术文章。但是,其中已包含的信息非常有价值,以至于我决定将其发布出来,尽管我只涵盖了句柄的一小部分。在接下来的几个月里,我将对其进行改进。请耐心等待;更多内容即将到来。
文件句柄
我有一个 FILE *
。或者我有一个 CFile
或 CStdioFile
。它的句柄是什么?它是什么句柄?
在应用程序级别,文件句柄有几种表示方式。有 C 库提供的文件句柄,有 FILE *
对象,还有操作系统文件句柄。它们都是可以互换的,但前提是你知道你在做什么。
它们**并非**在瞬间就可以随意互换,因为存在诸如缓冲等问题。因此,如果您访问一个 FILE *
并获取底层操作系统句柄,然后执行 WriteFile
,您可能会得到一个严重损坏的文件,因为 FILE *
对象正在管理缓冲数据。因此,除非您了解如何刷新缓冲区并保持文件内容和位置的各种文件句柄图像的一致性,否则您将陷入严重困境。
更典型的情况是,您有一个新的、刚打开的句柄,想将其与更适合您任务的表示形式关联起来。例如,C 库函数 fopen
在处理文件共享方面做得非常糟糕,而文件共享的概念在最初指定它的 Unix 操作系统中是不存在的。您想使用完整的 Win32 文件共享,但又不想进行原始的 WriteFile
操作。也许您不能,因为您正在将某些内容回溯到现有的、可能跨操作系统可移植的源代码集中,并且您正在编写特定于操作系统的模块。因此,您可以从 CreateFile
获取一个真正的 Win32 HANDLE
,并希望将其与 FILE *
关联起来,以便现在可以方便地使用它。甚至可以将其与 C++ fstream
关联起来。请继续阅读!
C 库“句柄”是索引 C 运行时库中表的较小整数。传统上,C 库将同时打开的文件数量限制在非常有限的句柄数,例如 16 个。在 Win32 中,这不再是问题。Win32 中的 C 库现在支持多达 2048 个低级句柄。默认情况下,最多允许 512 个 FILE *
对象,尽管您可以通过调用 _setmaxstdio
轻松地将此限制更改为最多 2048 个。
如果您在下表中找不到所需的转换,您将需要组合使用这些转换。例如,要将 HANDLE
转换为 fstream
,您需要执行类似的操作:
HANDLE h = ::CreateFile(...); fstream f; f.attach(_open_osfhandle(h));
直接文件句柄转换摘要
从 |
来源 |
改为 |
不如使用: |
HANDLE |
Win32
|
C 库句柄 | _open_osfhandle |
CFile |
CFile::CFile | ||
C 库句柄 | <io.h>
|
HANDLE |
_get_osfhandle |
FILE * |
_fdopen | ||
fstream |
fstream::fstream 或
fstream::attach | ||
FILE * |
<stdio.h>
|
C 库句柄 | _fileno |
CStdioFile |
CStdioFile::CStdioFile | ||
CFile |
MFC | HANDLE |
m_hFile 数据成员 |
CStdioFile |
MFC | FILE * |
m_pStream 数据成员 |
stdxxx |
Win32 | HANDLE |
GetStdHandle |
fstream |
C++ 库 | C 库句柄 | fstream 的 .fd 方法 |
Win32 HANDLE 到 C 库句柄
<io.h> int _open_osfhandle(long oshandle, int flags)
此函数接受一个 HANDLE
值并返回一个可以与 C 库交互的小整数。flags
值包括 O_APPEND
、O_RDONLY
和 O_TEXT
。请注意,此原型假定 long
和 HANDLE
大小相同,您需要进行类型转换才能通过编译器,例如:
int h = _open_osfhandle((long) myhandle, 0);
目前尚不清楚微软在 Win64 中将如何处理此库调用,因为我认为 Win64 中的句柄将是 64 位宽。
C 库句柄到 FILE *
给定一个 C 库文件句柄,您可以通过调用函数 _fdopen
,传入 C 库文件句柄并返回 FILE *
来将其转换为 FILE*
。
<stdio.h><code> int _fdopen(int filehandle, const char * mode)
其中 mode
是您可以提供给 fopen
的任何模式值,例如 "r"
、"rw"
、"w"
等。
FILE * 到 C 库句柄
给定一个 FILE *
对象,您可以使用 _fileno
获取底层 C 库句柄。
<stdio.h>
<code>int _fileno(FILE * f)
C 库句柄到 Win32 句柄
<io.h> long _get_osfhandle(int filehandle)
此函数接受一个 C 库文件句柄并返回底层的 Win32 HANDLE
。
CFile 到 HANDLE
在 CFile
对象之下是系统文件句柄。有时。可能。在原始的 CFile
中,m_hfile
成员是系统文件句柄。但是,微软警告说这在派生类中可能会改变。
HANDLE 到 CFile
要将 CFile
对象与操作系统文件句柄关联起来,请使用以下形式的 CFile
构造函数:
CFile::CFile(HANDLE h)
您是在堆栈变量中执行此操作还是使用堆分配取决于您的应用程序的性质。
CFile file(myhandle);
或
CFile * file = new CFile(myhandle);
CStdioFile 到 FILE *
CStdioFile
的 m_pStream
成员是对其底层 FILE *
对象的引用。
FILE * 到 CStdioFile
要将 CStdioFile
对象与操作系统文件句柄关联起来,请使用以下形式的 CStdioFile
构造函数:
CStdioFile::CStdioFile(FILE * f)
您是在堆栈变量中执行此操作还是使用堆分配取决于您的应用程序的性质。
<code>CStdioFile file(myfile);
或
CStdioFile * file = new CStdioFile(myfile);
stdxxx 到 HANDLE
如果您需要 stdin
、stdout
或 stderr
的句柄,而又不使用 stdio.h 库,您可以使用 API 调用 GetStdHandle
,指定常量 STD_INPUT_HANDLE
、STD_OUTPUT_HANDLE
或 STD_ERROR_HANDLE
之一。这些将返回一个 HANDLE
值,可用于 ReadFile
、WriteFile
或与上述任何函数一起使用。
C 库句柄到 fstream
如果您有一个 C 库句柄(来自 _open
或 _sopen
的小整数)并需要一个 C++ fstream
类对象,请查看 fstream
构造函数。一种形式接受 C 文件描述符:
fstream(filedesc fd)
您还可以使用 fstream::attach
方法将文件描述符附加到现有的 fstream
。
fstream f; f.attach(fd);
要使此功能正常工作,不能有文件描述符已附加到 fstream
。
要从 fstream
获取底层 C 库句柄,请使用 .fd
方法。
fstream f; f.attach(fd) ASSERT(fd == f.fd())
可继承句柄
好的,很快就会有……
其他句柄
同样……