即使存在MsiCloseHandle,也需要MsiViewClose调用吗?

时间:2019-04-24 06:35:19

标签: c++ c winapi windows-installer custom-action

我使用MsiDatabaseOpenView打开MSI数据库视图,然后调用MsiViewExecute。那么,即使我确实致电MsiViewClose,也需要打电话给MsiCloseHandle吗? MsiCloseHandle不会调用MsiViewClose(或做一些事情来在内部关闭所有必需的句柄)吗?

我问这个的实际原因: 建议使用类PMSIHANDLE而不是手动关闭句柄(析构函数将调用MsiCloseHandle-在VS中可见的源代码)。因此,当我使用MsiDatabaseOpenView打开视图并将句柄包裹在PMSIHANDLE中时,我不必调用MsiCloseHandle,但是必须(?)调用MsiViewClose!?

1 个答案:

答案 0 :(得分:3)

答案

MsiViewClose()不需要关闭手柄。仅当您要在同一视图上再次运行MsiViewExecute()时才需要,这对于将不同的参数传递给参数化的SQL查询很有用。 documentation的备注中对此进行了说明:

  

必须在MsiViewExecute之前调用MsiViewClose函数   除非结果的所有行都在视图上再次调用该函数   MsiViewFetch函数已获得该设置。

在最常见的用例中,您只需要对给定视图进行一次MsiViewExecute()调用,则无需调用MsiViewClose()

PMSIHANDLE pView;
UINT res = MsiDatabaseOpenViewW( hDatabase, L"SELECT * FROM `File`", &pView );
if( res == ERROR_SUCCESS )
{
    res = MsiViewExecute( pView, nullptr );
}
// Destructor of PMSIHANDLE calls MsiCloseHandle()

注意事项

从现代C ++的角度来看,PMSIHANDLE的设计似乎很糟糕。首先,它无法防止意外复制句柄,否则会导致在同一句柄上两次调用MsiViewClose()。同样,虽然隐式转换为MSIHANDLE*很方便,但也很危险,因为它有可能不经先关闭就意外覆盖现有句柄。

以下是基于C ++ 11 std::unique_ptrPMSIHANDLE的替代方法:

// A deleter for MSIHANDLE.
struct MsiHandleDeleter
{
    // This alias enables us to actually store values of type MSIHANDLE in the unique_ptr
    // (by default it would be MSIHANDLE*).
    using pointer = MSIHANDLE;

    void operator()( MSIHANDLE h ) const { if( h ) ::MsiCloseHandle( h ); }
};

// A RAII wrapper for MSI handle. The destructor automatically closes the handle, if not 0.
using UniqueMsiHandle = std::unique_ptr< MSIHANDLE, MsiHandleDeleter >;

用法示例:

UniqueMsiHandle record{ ::MsiCreateRecord( 1 ) };
::MsiRecordSetInteger( record.get(), 1, 42 );
// Destructor takes care of calling MsiCloseHandle(), just like PMSIHANDLE.

PMSIHANDLE相比,将其与具有MSIHANDLE*输出参数的函数一起使用比较麻烦,但是可以通过创建与UnqiueMsiHandle一起使用的包装函数或类来轻松地对其进行补救。 / p>

优势:

  • 以更少的方式做错事。
  • 清除所有权和moveability
  • 每个习惯std::unique_ptr的人都会立即理解UniqueMsiHandle的语义。