Registration Free COM и OLE DB
Предыстория
В конце лета 2016-го, мой любимый рабочий ноутбук начал выносить мозг внезапными выключениями. К октябрю это окончательно задрало и я решил что пришло время осуществить свою мечту притворить в реальность мысли о новом рабочей лошадке. Которая сможет заменить и ноутбук и десктоп, собранный в 2008 году. В начале ноября новый компьютер был собран. Его дури хватило на то, чтобы за четыре дня зарелизить собственного клиента для FB3 🙂
После этого началась настройка новой системы для ежедневной рутинной работы.
По теме
Основная проблема, которая сопровождает разработку и тестирование IBProvider-a на одном компьютере — это конфликты регистрационных записей в реестре Windows. Нужно постоянно помнить, какая сборка сейчас зарегистрирована. Это напрягает и периодически приводит к ошибкам типа «тесты подхватили не ту DLL».
Решение у этой проблемы уже давно есть — в манифест исполняемого файла (EXE) тестов следует добавить записи, позволяющие создавать COM-объекты с использованием технологии «Registration Free COM». Добавил.
И тут внезапно оказалось, что стандартный пул подключений OLE DB не поддерживает провайдеры «зарегистрированные» таким образом. Создавать-то он их может. Но подключения не кэширует — ему нужны дополнительные данные о провайдере из реестра, которых, понятное дело, там нет.
Грязно выругавшись, попутно вспомнив про родителей баги этой стандартной компоненты, я начал пилить свою реализацию пула подключений.
Потратив на это занятие для умалишенных чуть более трех месяцев (захотелось сделать все по-уму), вернулся к теме, ради которой все затевалось — изоляция тестов.
Если интересно посмотреть своими глазами, то нужно установить все компоненты IBProvider-а (32бита или 64бита — это без разницы) и перейти в каталог «c:\Program Files\LCPI\IBProvider.3\TestCode\ActiveX\IBP\oledb_test».
Для 32битных и 64битных тестов были сформированы файлы с дополнительными данными для манифестов:
- manifests\ibprovider_w32.manifest
- manifests\ibprovider_w64.manifest
Содержимое файла ibprovider_w32.manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <file name = "private\lcpi.ibprovider_v3_vc15_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc15.release" clsid="{1ED1A41E-5D9F-4EAC-9A8A-E3BAA4BE4901}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider_v3_vc15_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc15.debug" clsid="{2F9837F6-EE9F-4841-A8D3-D0D6803A917C}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w32_prof_i.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc15.release" clsid="{1329FCE3-50CB-4036-AC2F-A98C9651940F}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w32_prof_d.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc15.debug" clsid="{5E8359F3-6D19-4EA9-80EC-7588C2D43698}" threadingModel = "Both" /> </file> </assembly>
Содержимое файла ibprovider_w64.manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <file name = "private\lcpi.ibprovider_v3_vc15_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc15.release" clsid="{C010F775-31CB-457A-9B2F-ED6FA1A686F5}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider_v3_vc15_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc15.debug" clsid="{213E7754-8C67-41C8-9CD4-B92B882D2399}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w64_prof_i.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc15.release" clsid="{C5E11739-40A5-4A00-B8F9-9EDF5BFD4207}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w64_prof_d.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc15.debug" clsid="{2FF3E7C9-5D84-458E-9030-1A7165278A1F}" threadingModel = "Both" /> </file> </assembly>
Как видим, здесь определены различные релизные и отладочные сборки провайдера и пула подключений.
ProgID-ы можно назначать какие угодно.
CLSID-ы должны быть теми, которые поддерживает DLL. В случае IBProvider-а CLSID-ы можно найти в файлах:
- sdk/ibprovider/v03/lcpi_sdk__ibprovider__v03__clsids.cpp
- sdk/ibprovider/v03/lcpi_sdk__ibprovider__v03__private_clsids.cpp
В первом файле определены публичные CLSID-ы, которые используются при регистрации провайдера в реестре Windows.
Во втором файле определены CLSID, уникальные для каждой сборки (компилятор+редакция+платформа+конфигурация).
Для автоматизации модификации манифеста исполняемого файла тестов в проектный файл тестовой системы были добавлены следующие записи:
<Target Name="BeforeBuild"> <Error Text="WindowsSDK80Path not defined!" Condition="'$(WindowsSDK80Path)' == ''" /> </Target> <Target Name="AfterBuild"> <Message Importance="High" Text="Try to modify the assembly manifest" /> <Exec Condition="'$(Platform)'=='x64'" Command=""$(WindowsSDK80Path)bin\x86\mt.exe" -updateresource:$(TargetPath);#1 -manifest ..\..\manifests\ibprovider_w64.manifest" /> <Exec Condition="'$(Platform)'=='Win32'" Command=""$(WindowsSDK80Path)bin\x86\mt.exe" -updateresource:$(TargetPath);#1 -manifest ..\..\manifests\ibprovider_w32.manifest" /> </Target>
Теперь в каталоге (target) со сгенерированными исполняемым файлами тестов создаем подкаталог «private» и копируем в него сборки провайдера и пула подключений:
- lcpi.ibprovider_v3_vc15_w32_prof_d.dll
- lcpi.ibprovider_v3_vc15_w32_prof_i.dll
- lcpi.ibprovider_v3_vc15_w64_prof_d.dll
- lcpi.ibprovider_v3_vc15_w64_prof_i.dll
- lcpi.oledb_services_v1_vc15_w32_prof_d.dll
- lcpi.oledb_services_v1_vc15_w32_prof_i.dll
- lcpi.oledb_services_v1_vc15_w64_prof_d.dll
- lcpi.oledb_services_v1_vc15_w64_prof_i.dll
Всё, можно запускать тесты, которые не будут зависеть от реестра:
Открываем диспечер задач, чтобы проверить результаты наших трудов, и видим: