IBProvider and «Registration Free COM»
Solving the first problem with the platform
Solution of the second problem with OLE DB service components
Modifying an existing application manifest
Modifying a manifest during the compilation of an application
Another examples
See also
Introduction
Since from Windows XP, the operating system has the support of the technologies «Registration Free COM», which allows using COM objects without their registration in the Windows registry.
To do this, simply specify the required components in the application manifest.
The manifest is a XML document stored in the application module resources.
The description of the components includes ProgID, CLSID and a module name (DLL). ProgID can be arbitrary. CLSID must supported by module.
Example:
<?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-v5_vc16_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5" clsid="{769A12A0-04BF-11D8-AE8B-00A0C907DB93}" threadingModel = "Both" /> </file> </assembly>
This allows:
- Creation an application, which distributed by simply copying files.
- Linking an application with explicitly defined modules with COM objects.
All looks great, but this technology has a number of problems, which are worth paying attention to.
At first. There is no way to specify the platform of component: 32bit or 64bit. This is not a problem for native applications, which compiled for a specific platform. However, it creates certain difficulties for managed applications, which compiled for «AnyCPU» platform.
At second. In general, this technology is not suitable for OLE DB providers, because they rely on system components, and system components get the necessary information about the provider from the Windows registry. That is, the OLE DB provider must be registered in the system. Without this, in particular, objects that provide error descriptions and the OLE DB connection pool will not work.
IBProvider provides a solution for both problems.
Solving the first problem with the platform
The provider components linked with two sets of identifiers (CLSIDs):
- Public identifiers
- Specific identifiers
Public identifiers do not depend on the platform (32/64 bits) and the compiler used. These CLSIDs used to register the provider components in the Windows registry.
The specific identifiers are unique for each module.
For example, «LCPI.IBProvider.5» is bound to the public CLSID «769A12A0-04BF-11D8-AE8B-00A0C907DB93».
Specific CLSIDs for «LCPI.IBProvider.5», which compiled by VS2019 (vc16), are:
Module | Provider CLSID |
lcpi.ibprovider-v5_vc16_w32_prof_i.dll | 16CBE51A-E8DA-4D75-81A5-58E671171A9D |
lcpi.ibprovider-v5_vc16_w64_prof_i.dll | F77C84F7-B095-45D7-9554-600A259E9DE1 |
All specific identifiers enumerates in the file «cpp\public\source\lcpi\sdk\ibprovider\v05\lcpi_sdk__ibprovider__v05__private_clsids.cpp».
If we want to use «lcpi.ibprovider-v5_vc16_w32_prof_i.dll» in a 32-bit process, and «lcpi.ibprovider-v5_vc16_w64_prof_i.dll» in a 64-bit process, then we can create such a 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-v5_vc16_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.32bit" clsid="{16CBE51A-E8DA-4D75-81A5-58E671171A9D}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc16_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.64bit" clsid="{F77C84F7-B095-45D7-9554-600A259E9DE1}" threadingModel = "Both" /> </file> </assembly>
In a managed application compiled for «AnyCPU» platform, we dynamically determine the target platform of the process (for example, by analyzing the IntPtr.Size value) and use either «LCPI.IBProvider.5.32bit» or «LCPI.IBProvider.5.64bit».
Solution of the second problem with OLE DB service components
Recall that the second problem consists of two parts:
- OLE DB error handling.
- OLE DB Connection Pool.
Both tasks are addressed by replacing the standard service components with compatible implementations.
IBProvider does not use standard components for error handling and returns its own objects that do not need data from the registry.
Instead of the standard OLE DB connection pool, you should use the connection pool from «LCPI OLE DB Services». Like IBProvider, the components of «LCPI OLE DB Services» have public and specific identifiers. In addition, can be used the identifiers of standard OLE DB connection pool components:
Standard component | CLSID |
MSDAINITIALIZE | 2206CDB0-19C1-11D1-89E0-00C04FD7A829 |
PDPO | CCB4EC60-B9DC-11D1-AC80-00A0C9034873 |
For replacing of standard OLE DB connection pool to «LCPI OLE DB Services», implemented by the 32-bit module «lcpi.oledb_services-v1_vc16_w32_prof_i.dll», need add the following entries to the application 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.oledb_services-v1_vc16_w32_prof_i.dll"> <comClass progid="MSDAINITIALIZE" clsid="{2206CDB0-19C1-11D1-89E0-00C04FD7A829}" threadingModel = "Both" /> <comClass progid="PDPO" clsid="{CCB4EC60-B9DC-11D1-AC80-00A0C9034873}" threadingModel = "Both" /> </file> </assembly>
If the application is 64-bit, then instead of «lcpi.oledb_services-v1_vc16_w32_prof_i.dll» need use «lcpi.oledb_services-v1_vc16_w64_prof_i.dll». ProgID and CLSIDs remain the same.
Modifying an existing application manifest
To update (add) a manifest, need use the command line utility «mt.exe», which included in the Visual Studio.
At first. Need determine the target platform (32 or 64 bits) of the application. If the application compiled for «AnyCPU» platform, it’s necessary to determine process type (32 or 64 bits).
For the 32-bit case we will use modules:
- lcpi.ibprovider-v5_vc16_w32_prof_i.dll
- lcpi.oledb_services-v1_vc16_w32_prof_i.dll
- lcpi.infrastructure.core-v01_vc16_w32_i.dll [it is auxiliary module]
- lcpi.infrastructure.multitasking.ibp-v02_vc16_w32_i.dll [it is auxiliary module]
In case of 64-bit process, we will use modules:
- lcpi.ibprovider-v5_vc16_w64_prof_i.dll
- lcpi.oledb_services-v1_vc16_w64_prof_i.dll
- lcpi.infrastructure.core-v01_vc16_w64_i.dll [it is auxiliary module]
- lcpi.infrastructure.multitasking.ibp-v02_vc16_w64_i.dll [it is auxiliary module]
Let’s conduct our experiment with the 32-bit native application «RowsetViewer.exe» from the «OLE DB SDK», which initially does not have any manifest.
Copy the IBProvider module and «LCPI OLE DB Services» module into a «private» subdirectory located on the same directory as «RowsetViewer.exe».
At second. Create a file «ibprovider_w32.manifest» with the descriptions of the components that you will need to add to the application 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-v5_vc16_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5" clsid="{769A12A0-04BF-11D8-AE8B-00A0C907DB93}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services-v1_vc16_w32_prof_i.dll"> <comClass progid="MSDAINITIALIZE" clsid="{2206CDB0-19C1-11D1-89E0-00C04FD7A829}" threadingModel = "Both" /> <comClass progid="PDPO" clsid="{CCB4EC60-B9DC-11D1-AC80-00A0C9034873}" threadingModel = "Both" /> </file> </assembly>
Put «ibprovider_w32.manifest» file to folder with the file «RowsetViewer.exe».
At third. Need to add component descriptions from «ibprovider_w32.manifest» to the application manifest.
-
Run «Developer Command Prompt for VS 2017» (or for another version of Visual Studio):
- Go to the directory with the files «RowsetViewer.exe» and «ibprovider_w32.manifest».
-
Run from the command line:
mt.exe -outputresource:RowsetViewer.exe;#1 -manifest ibprovider_w32.manifest
- Run «RowsetViewer.exe» and force it to connect to the database via «LCPI.IBProvider.5».
-
Check using the «Process Explorer» program, that «RowsetViewer.exe» really used our DLLs from the «private» directory:
- See that everything is OK.
Since «RowsetViewer.exe» initially did not have a manifest, we called the utility «mt.exe» with the command «outputresource».
In case when application already has a manifest, need to call «mt.exe» with the «updateresource» command:
Modifying a manifest during the compilation of an application
If you need to modify the application manifest while compiling it (in Visual Studio), you can add to project file the «AfterBuild» task with the call of «mt.exe».
Updating a managed (.NET) application, which compiled for a 32-bit platform:
<Target Name="AfterBuild"> <Message Importance="High" Text="Try to modify the assembly manifest" /> <Exec Condition="'$(PlatformTarget)'=='x86'" Command=""$(WindowsSDK80Path)bin\x86\mt.exe" -updateresource:$(TargetPath);#1 -manifest .\manifests\ibprovider_w32.manifest" /> </Target>
Another examples
In the IBProvider distribution, you can find ready-made examples, which used the «Registration Free COM» technology.
Primary IBProvider Test System
Catalog: «TestCode\ActiveX\IBP\oledb_test».
This test system is a native application written in C++ and compiled for 32-bit and 64-bit Windows.
Data for the manifest of a 32-bit binary file (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_vc14xp_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc14xp.release" clsid="{5C58747A-2F61-45BA-AC80-5157A4F7AC61}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider_v3_vc14xp_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc14xp.debug" clsid="{3146533B-0C38-4399-A54F-C2A44897F33F}" threadingModel = "Both" /> </file> <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> <!-- IBProvider v5 --> <file name = "private\lcpi.ibprovider-v5_vc12xp_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc12xp.release" clsid="{4000E7C5-B23B-43FA-9EE8-446170E78F5E}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc12xp_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc12xp.debug" clsid="{06C627D7-64B0-4871-A003-4B85B961F8D9}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc14xp_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc14xp.release" clsid="{AB74369E-0453-4B44-AB71-4370A5F32766}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc14xp_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc14xp.debug" clsid="{B44D56D2-8C4F-4A26-B667-5753CD0E5537}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc15_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc15.release" clsid="{B81942DB-3D3F-43AD-ABC1-54E86004A867}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc15_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc15.debug" clsid="{E5E5D036-0DC4-410C-B091-52AF6E6E9F10}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc16_w32_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc16.release" clsid="{16CBE51A-E8DA-4D75-81A5-58E671171A9D}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc16_w32_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc16.debug" clsid="{3FF6ADB2-22E6-4BDA-8168-EEB5CFB650E7}" threadingModel = "Both" /> </file> <!-- LCPI OLEDB Services --> <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> <file name = "private\lcpi.oledb_services-v1_vc16_w32_prof_i.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc16.release" clsid="{F1CE2600-2673-4DDB-96A8-4EE229B0E97E}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services-v1_vc16_w32_prof_d.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc16.debug" clsid="{2D3F2A3D-A129-47B1-8127-7280C9C51402}" threadingModel = "Both" /> </file> </assembly>
Data for the manifest of a 64-bit binary file (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_vc14xp_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc14xp.release" clsid="{1B93BA36-09CF-4C04-9BF2-D15EC740B125}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider_v3_vc14xp_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.3.Private.vc14xp.debug" clsid="{18111C74-F5DE-4FBE-8234-DC90227C15AF}" threadingModel = "Both" /> </file> <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> <!-- IBProvider v5 --> <file name = "private\lcpi.ibprovider-v5_vc12xp_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc12xp.release" clsid="{2A9077CE-0CCE-4BD3-8D35-D7DD9454609D}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc12xp_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc12xp.debug" clsid="{FF98B403-6A5F-40C3-B23B-50EF2427591D}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc14xp_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc14xp.release" clsid="{3BEA0EA0-3C99-4D91-971F-9BC61CA8EE8C}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc14xp_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc14xp.debug" clsid="{5A5602AF-1BC8-4347-A9F5-C61E876E4FEA}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc15_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc15.release" clsid="{CB8F7D22-F43F-441D-A520-B854CE3B8E57}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc15_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc15.debug" clsid="{7A9A2F39-2688-4DC2-9FA1-0F9F5032E85D}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc16_w64_prof_i.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc16.release" clsid="{F77C84F7-B095-45D7-9554-600A259E9DE1}" threadingModel = "Both" /> </file> <file name = "private\lcpi.ibprovider-v5_vc16_w64_prof_d.dll"> <comClass progid="LCPI.IBProvider.5.Private.vc16.debug" clsid="{AF304342-FD44-454E-A9FC-8CEEB7902A5A}" threadingModel = "Both" /> </file> <!-- LCPI OLEDB Services --> <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> <file name = "private\lcpi.oledb_services-v1_vc16_w64_prof_i.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc16.release" clsid="{F5B3B347-0F32-4D21-B28D-8A9E6619C040}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services-v1_vc16_w64_prof_d.dll"> <comClass progid="LCPI.OleDbServices.DataInitManager.Local.1.Private.vc16.debug" clsid="{4F8AD48C-2ED4-485E-B129-63D1B6192365}" threadingModel = "Both" /> </file> </assembly>
As you may see, the ProgIDs in the files are identical, but they tied to different CLSIDs.
You should also pay attention to the various ProgIDs for the release and debug builds.
To automate the update of the binary file manifest, the following commands added to the project file:
<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>
Example for «LCPI ADO.NET Data Provider for OLE DB»
Catalog: «Samples\oledb_net\Collection_001\Sample_0026__PrivateIBProvider»
This example written on C# and compiled for 32-bit and 64-bit platforms.
For each platform define its own «IBProvider» and «LCPI OLE DB Services» modules.Data for the manifest of the 32-bit module:
<?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" clsid="{769A1280-04BF-11D8-AE8B-00A0C907DB93}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w32_prof_i.dll"> <comClass progid="MSDAINITIALIZE" clsid="{2206CDB0-19C1-11D1-89E0-00C04FD7A829}" threadingModel = "Both" /> <comClass progid="PDPO" clsid="{CCB4EC60-B9DC-11D1-AC80-00A0C9034873}" threadingModel = "Both" /> </file> </assembly>
Data for the manifest of the 64-bit module:
<?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" clsid="{769A1280-04BF-11D8-AE8B-00A0C907DB93}" threadingModel = "Both" /> </file> <file name = "private\lcpi.oledb_services_v1_vc15_w64_prof_i.dll"> <comClass progid="MSDAINITIALIZE" clsid="{2206CDB0-19C1-11D1-89E0-00C04FD7A829}" threadingModel = "Both" /> <comClass progid="PDPO" clsid="{CCB4EC60-B9DC-11D1-AC80-00A0C9034873}" threadingModel = "Both" /> </file> </assembly>
To automate the update of the binary file manifest, the following commands added to the project file:
<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="'$(PlatformTarget)'=='x64'" Command=""$(WindowsSDK80Path)bin\x86\mt.exe" -updateresource:$(TargetPath);#1 -manifest .\manifests\ibprovider_w64.manifest" /> <Exec Condition="'$(PlatformTarget)'=='x86'" Command=""$(WindowsSDK80Path)bin\x86\mt.exe" -updateresource:$(TargetPath);#1 -manifest .\manifests\ibprovider_w32.manifest" /> </Target>