我使用MsiDatabaseOpenView
打开MSI数据库视图,然后调用MsiViewExecute
。然后,即使我打电话给MsiViewClose
,我还需要打电话给MsiCloseHandle
吗? MsiCloseHandle
会不会调用MsiViewClose
(或者做一些事情来内部关闭所有必需的句柄)?
我问这个的实际原因:建议使用类PMSIHANDLE
而不是手动关闭句柄(析构函数将调用MsiCloseHandle
- VS中可见的源代码)。因此,当我使用MsiDatabaseOpenView
打开视图并将句柄包裹在PMSIHANDLE
中时,我放松了调用MsiCloseHandle
,但我必须(?)调用MsiViewClose
!?
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 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*
输出参数的函数一起使用会更加麻烦,但这可以通过创建与UnqiueMsiHandle
一起使用的包装函数或类来轻松解决。
好处:
std::unique_ptr
的人都会立即理解UniqueMsiHandle
的语义。