即使 MsiCloseHandle 存在,是否也需要 MsiViewClose 调用?
Is MsiViewClose call required even when MsiCloseHandle is there?
我使用 MsiDatabaseOpenView
打开 MSI 数据库视图,然后调用 MsiViewExecute
。那么我是否需要调用 MsiViewClose
,即使我确实调用了 MsiCloseHandle
? MsiCloseHandle
不会调用 MsiViewClose
(或做一些事情来在内部关闭所有必需的句柄)吗?
我问这个问题的真正原因:
建议使用 class PMSIHANDLE
而不是手动关闭句柄(析构函数将调用 MsiCloseHandle
- 源代码在 VS 中可见)。因此,当我使用 MsiDatabaseOpenView
打开视图并将句柄包装在 PMSIHANDLE
中时,我不必再调用 MsiCloseHandle
,但我必须(?)调用 MsiViewClose
!?
回答
MsiViewClose()
不需要关闭句柄。只有当你想再次在同一个视图上 运行 MsiViewExecute()
时才需要,这对于将不同的参数传递给参数化的 SQL 查询很有用。这在documentation:
的评论中有说明
The MsiViewClose function must be called before the MsiViewExecute
function is called again on the view, unless all rows of the result
set have been obtained with the MsiViewFetch function.
在最常见的用例中,您只需为给定视图执行一次 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 的 PMSIHANDLE
的替代方案 std::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*
out 参数的函数一起使用更加麻烦,但这可以通过创建包装函数或 类 与 UnqiueMsiHandle
.
优点:
- 减少做错事的方法。
- 明确所有权和moveability。
- 用惯了
std::unique_ptr
的大家马上就会明白UniqueMsiHandle
的语义。
我使用 MsiDatabaseOpenView
打开 MSI 数据库视图,然后调用 MsiViewExecute
。那么我是否需要调用 MsiViewClose
,即使我确实调用了 MsiCloseHandle
? MsiCloseHandle
不会调用 MsiViewClose
(或做一些事情来在内部关闭所有必需的句柄)吗?
我问这个问题的真正原因:
建议使用 class PMSIHANDLE
而不是手动关闭句柄(析构函数将调用 MsiCloseHandle
- 源代码在 VS 中可见)。因此,当我使用 MsiDatabaseOpenView
打开视图并将句柄包装在 PMSIHANDLE
中时,我不必再调用 MsiCloseHandle
,但我必须(?)调用 MsiViewClose
!?
回答
MsiViewClose()
不需要关闭句柄。只有当你想再次在同一个视图上 运行 MsiViewExecute()
时才需要,这对于将不同的参数传递给参数化的 SQL 查询很有用。这在documentation:
The MsiViewClose function must be called before the MsiViewExecute function is called again on the view, unless all rows of the result set have been obtained with the MsiViewFetch function.
在最常见的用例中,您只需为给定视图执行一次 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 的 PMSIHANDLE
的替代方案 std::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*
out 参数的函数一起使用更加麻烦,但这可以通过创建包装函数或 类 与 UnqiueMsiHandle
.
优点:
- 减少做错事的方法。
- 明确所有权和moveability。
- 用惯了
std::unique_ptr
的大家马上就会明白UniqueMsiHandle
的语义。