Publish date: 2018-08-22
Updated: 2018-11-15

IBProvider and «Registration Free COM»


Introduction

Since from Windows XP, the operating system has the support of the technologies «Free COM Registration», 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 an XML document stored in the application module resources.

The description of the components includes ProgID, CLSID and 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_v3_vc15_w32_prof_i.dll">
   <comClass progid="LCPI.IBProvider.3" clsid="{769A1280-04BF-11D8-AE8B-00A0C907DB93}" threadingModel = "Both" />
  </file>
</assembly>

This allows:

  • Creation of application, which distributed by simply copying files.
  • Linking of 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.3» is bound to the public CLSID «769A1280-04BF-11D8-AE8B-00A0C907DB93».

Specific CLSIDs for «LCPI.IBProvider.3», which compiled by VS2017 (vc15), are:

Module Provider CLSID
lcpi.ibprovider_v3_vc15_w32_prof_i.dll 1ED1A41E-5D9F-4EAC-9A8A-E3BAA4BE4901
lcpi.ibprovider_v3_vc15_w64_prof_i.dll C010F775-31CB-457A-9B2F-ED6FA1A686F5

All specific identifiers enumerates in the file «sdk\ibprovider\v03\lcpi_sdk__ibprovider__v03__private_clsids.cpp».

If we want to use «lcpi.ibprovider_v3_vc15_w32_prof_i.dll» in a 32-bit process, and «lcpi.ibprovider_v3_vc15_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_v3_vc15_w32_prof_i.dll">
   <comClass progid="LCPI.IBProvider.3.32bit" clsid="{1ED1A41E-5D9F-4EAC-9A8A-E3BAA4BE4901}" threadingModel = "Both" />
  </file>
  <file name = "private\lcpi.ibprovider_v3_vc15_w64_prof_i.dll">
   <comClass progid="LCPI.IBProvider.3.64bit" clsid="{C010F775-31CB-457A-9B2F-ED6FA1A686F5}" 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.3.32bit» or «LCPI.IBProvider.3.64bit».

Solution of the second problem with OLE DB service components

Recall that the second problem consists of two parts:

  1. OLE DB error handling.
  2. OLE DB Connection Pool.

Both tasks 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_vc15_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_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>

If the application is 64-bit, then instead of «lcpi.oledb_services_v1_vc15_w32_prof_i.dll» need use «lcpi.oledb_services_v1_vc15_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_v3_vc15_w32_prof_i.dll
  • lcpi.oledb_services_v1_vc15_w32_prof_i.dll

In case of 64-bit process, we will use modules:

  • lcpi.ibprovider_v3_vc15_w64_prof_i.dll
  • lcpi.oledb_services_v1_vc15_w64_prof_i.dll

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_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>

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.

  1. Run «Developer Command Prompt for VS 2017» (or for another version of Visual Studio):
    Selelection at Windows Menu
  2. Go to the directory with the files «RowsetViewer.exe» and «ibprovider_w32.manifest».
  3. Run from the command line:
    mt.exe -outputresource:RowsetViewer.exe;#1 -manifest ibprovider_w32.manifest

    Console Window
  4. Run «RowsetViewer.exe» and force it to connect to the database via «LCPI.IBProvider.3».
  5. Check using the «Process Explorer» program, that «RowsetViewer.exe» really used our DLLs from the «private» directory:
    Rowset Viewer and used DLLs
  6. 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:

mt.exe -updateresource:SomeApplication1.exe;#1 -manifest ibprovider_w32.manifest

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="&quot;$(WindowsSDK80Path)bin\x86\mt.exe&quot; -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>

  <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>

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>

  <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>

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="&quot;$(WindowsSDK80Path)bin\x86\mt.exe&quot; -updateresource:$(TargetPath);#1 -manifest ..\..\manifests\ibprovider_w64.manifest" />
 <Exec Condition="'$(Platform)'=='Win32'" Command="&quot;$(WindowsSDK80Path)bin\x86\mt.exe&quot; -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="&quot;$(WindowsSDK80Path)bin\x86\mt.exe&quot; -updateresource:$(TargetPath);#1 -manifest .\manifests\ibprovider_w64.manifest" />
 <Exec Condition="'$(PlatformTarget)'=='x86'" Command="&quot;$(WindowsSDK80Path)bin\x86\mt.exe&quot; -updateresource:$(TargetPath);#1 -manifest .\manifests\ibprovider_w32.manifest" />
</Target>

Publish date: 2018-08-22. Copyright: IBProvider. This material may be reproduced on other web sites, without written permission but link https://www.ibprovider.com/eng required.