是否需要MsiViewClose调用,即使MsiCloseHandle在那里?

问题描述 投票:1回答:1

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

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

c++ c winapi windows-installer custom-action
1个回答
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 ++ 11s PMSIHANDLEstd::unique_ptr的替代方案:

// 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一起使用的包装函数或类来轻松解决。

好处:

  • 做错事的方法少了。
  • 明确所有权和moveability
  • 每个习惯于std::unique_ptr的人都会立即理解UniqueMsiHandle的语义。
© www.soinside.com 2019 - 2024. All rights reserved.