Windows HANDLE RAII 管理,如果返回布尔值而不是句柄怎么办?

Windows HANDLE RAII management, what if a bool is returned instead of a handle?

本文关键字:句柄 怎么办 布尔值 返回 RAII HANDLE 管理 如果 Windows      更新时间:2023-10-16

以前:

正确关闭WinAPI句柄(避免重复关闭)

我的问题是:如果 CreateFile 返回一个布尔值而不是一个句柄,并且输出有一个指针怎么办?发明的例子:

HANDLE handle;
if (CreateFile(&handle, "filename", ...) == true) {
//...
}

有没有很好的语法可以在C++提供的 RAII 类使用它?这样我就可以把我的对象作为第一个参数而不是指针。

在这种情况下,我只需向 RAII 类添加一个覆盖operator&,例如:

template< class traits >
class HandleWrapper {
...
public:
...
traits::HandleType* operator&() { return &FHandle; }
};

然后像这样使用它:

HandleWrapper<KernelHandleTraits> handle;
if (CreateFile(&handle, "filename", ...) == true) {
...
}

正如其他人所提到的,您当然可以通过重载operator&来实现目标,但这充满了风险,首先破坏了使用包装器的目的。

考虑如何使用假设的 HandleWrapper,其中包含简单的重载operator&,该重载仅返回指向内部句柄的指针:

HandleWrapper<KernelHandleTraits> handle;
if (CreateFile(&handle, "first", ...) == true) {
// process the first file...
}
if (CreateFile(&handle, "second", ...) == true) {
// process the second file...
}

这会将句柄泄漏到第一个文件,因为operator&重载允许您执行后门分配。 请注意,包装类没有专门赋值运算符,因此它不必在完成第一个资源后重用包装器来跟踪第二个资源的复杂性。

如果包装器已经包含资源,您可以扩展包装器以执行更智能的操作,但类变得更加复杂,并且您离 RAII 的想法越来越远。

如果我们拆分头发,引用的 HandleWrapper 代码应用 RRID(资源释放是销毁)而不是 RAII(资源分配是初始化)。

  • RRID 类是一个包装器,您可以将已分配资源的所有权转移到其中(以确保稍后释放它)。
  • RAII 类的生存期与其包装的资源完全相同。 它在资源存在的那一刻就拥有资源,并在资源释放时死亡。

如果您有一个将资源作为"输出参数"返回的函数,则可以编写适配器函数:

HANDLE CreateFileAdapter(const char * filename, ...) {
HANDLE handle;
return CreateFile(&handle, "filename", ...) ? handle : INVALID_HANDLE_VALUE;
}

然后,您可以通过调用适配器函数按原样使用 HandleWrapper:

HandleWrapper<KernelHandleTraits> handle(CreateFileAdapter("filename", ...));

严格的 RAII 解决方案会将资源的分配封装在构造函数中,就像将释放封装在其析构函数中一样。 您可以在 RRID 包装器和适配器函数的背面构建 RAII 包装器:

class FileHandleWrapper : public HandleWrapper<KernelHandleTraits> {
public:
FileHandleWrapper(const char * file_name, ...) :
HandleWrapper(CreateFileAdapter(file_name, ...) {}
private:
static HANDLE CreateFileAdapter(const char *file_name, ...) {
HANDLE handle;
return CreateFile(&handle, "filename", ...) ?
handle : INVALID_HANDLE_VALUE;
}
};

请注意,我已将适配器函数设置为私有静态函数,以确保没有人以不安全的方式使用它。

你想要的,是一个调用CreateFile的函数,如果它返回false,则抛出,并返回句柄。 像这样:

HANDLE createFileOrThrow(const std::string& filename, ... )
{
HANDLE handle;
if (!CreateFile(&handle, filename.c_str(), ...) {
throw suitable_exception_type();
}
return handle;
}
HandleWrapper<KernelHandleTraits> hFile(CreateFileOrThrow("filename"));

在这个特定示例中,您可以将函数设置为立即调用的 lambda,但您可能希望经常执行此操作。

或者:

HandleWrapper<KernelHandleTraits> createFileOrThrow(const std::string& filename, ... )
{
HANDLE handle;
if (!CreateFile(&handle, filename.c_str(), ...) {
throw suitable_exception_type();
}
return HandleWrapper<KernelHandleTraits>(handle);
}
HandleWrapper<KernelHandleTraits> hFile = CreateFileOrThrow("filename");

不过,最后一个选项确实需要HandleWrapper有一个移动构造函数。