Требуется ли вызов MsiViewClose, даже если MsiCloseHandle существует? - PullRequest
1 голос
/ 24 апреля 2019

Я открываю представление базы данных MSI, используя MsiDatabaseOpenView с последующим вызовом MsiViewExecute. Тогда нужно ли мне звонить на MsiViewClose, даже если я звоню на MsiCloseHandle? Не будет ли MsiCloseHandle не вызывать MsiViewClose (или сделать что-то для внутреннего закрытия всех необходимых дескрипторов)?

Фактическая причина, почему я спрашиваю это: Существует класс PMSIHANDLE, который рекомендуется вместо того, чтобы вручную закрывать дескрипторы (деструктор вызовет MsiCloseHandle - исходный код виден в VS). Поэтому, когда я открываю представление с помощью MsiDatabaseOpenView и оборачиваю ручку в PMSIHANDLE, я освобождаюсь от вызова MsiCloseHandle, но я должен (?) Вызывать MsiViewClose!?

1 Ответ

3 голосов
/ 24 апреля 2019

Ответ

MsiViewClose() не требуется для закрытия ручки.Это необходимо, только если вы хотите снова запустить MsiViewExecute() в том же представлении, что может быть полезно для передачи различных параметров в параметризованный запрос SQL.Это указано в комментариях к документации :

Функция MsiViewClose должна быть вызвана до повторного вызова функции MsiViewExecute в представлении, если только все строки набора результатов не имеютбыло получено с помощью функции 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* может быть удобным, оно также опасно, поскольку позволяет случайно перезаписать существующий дескриптор, не закрывая его вначале.

Вот альтернатива PMSIHANDLE на основеC ++ 11s 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.
...