IBProvider and «LCPI COM API»
Introduction
A few years ago, we were approached with the question — «How to use IBProvider in Windows Azure?»
After studying the problem, we realized that «no way».
Because:
- There is not possible to register IBProvider in the target system.
- «Registration Free COM» involves modifying the launching EXE file, which is the part of Azure.
As usual, we did not raise our hands and now we offer you a solution to this task.
This document describes the general idea of the LCPI COM API and work with IBProvider through one of its implementations — «LCPI Easy COM».
All the files related to «IBProvider Professional Edition» / «Free IBProvider» / «LCPI OLE DB Services» / «LCPI ADO.NET Data provider for OLEDB» / «LCPI Easy COM» can be found in the user’s personal account on the site «ibprovider.com».
We suppose that the reader already has an experience with IBProvider and «LCPI ADO.NET Data provider for OLEDB».
Theory
Initially, IBProvider and later components «LCPI OLE DB Services» worked directly with the COM API.
Starting with IBProvider v5.27 («LCPI OLE DB Services» v1.22), interaction with the COM API goes through an interlayer in the form of a pair of DLLs (hereinafter referred to as LCPI COM API), whose names depend on the type of an executing process.
For 32-bit processes, these are:
- lcpi.infrastructure.os.windows.ole32-v01_w32.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w32.dll
For 64-bit processes, these are:
- lcpi.infrastructure.os.windows.ole32-v01_w64.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w64.dll
The LCPI COM API, in general terms, is just the subset of the COM API functions, to whose names were added a prefix “LCPI_OS__”:
- LCPI_OS__CoCreateInstance
- LCPI_OS__GetErrorInfo
- LCPI_OS__SetErrorInfo
- And so on …
The IBProvider and «LCPI OLE DB Services» installation kits contain the first implementation of LCPI COM API — «Simple Gate», which delegates calls to the Native Windows COM API.
As you may have already guessed, there is the second implementation of LCPI COM API called «LCPI Easy COM».
In it, the part of API is implemented by the third DLL (hereinafter referred to as ECM), the name of which also depends on the type of the executing process.
32-bit version:
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll
64-bit version:
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll
ECM implements the management for dynamic libraries with COM objects whose descriptions are stored in an XML file.
The 32-bit ECM works with the «lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll.settings» file.
The 64-bit ECM works with the «lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll.settings» file.
The example of a file with the description of IBProvider for a 64-bit application:
<ECM Version="1.0"> <Registry> <Key Name="HKCR"> <Key Name="CLSID"> <Key Name="{769A12A0-04BF-11D8-AE8B-00A0C907DB93}"> <Value Name="">LCPI.IBProvider.5</Value> <Value Name="OLE DB Services" Type="ui4">0xFFFFFFFF</Value> <Value Name="flush_log_period" Type="ui4">0</Value> <Key Name="ExtendedErrors"> <Value Name="">Extended Error Service</Value> <Key Name="{769A12A1-04BF-11D8-AE8B-00A0C907DB93}">LCPI.IBProvider Error Lookup [v5]</Key> </Key> <Key Name="InprocServer32"> <Value Name="">lcpi.ibprovider-v5_vc17_w64_prof_i.dll</Value> <Value Name="ThreadingModel">Free</Value> </Key> <Key Name="ProgID">LCPI.IBProvider.5</Key> <Key Name="VersionIndependentProgID">LCPI.IBProvider</Key> </Key> <Key Name="{769A12A1-04BF-11D8-AE8B-00A0C907DB93}"> <Value Name="">LCPI.IBProvider Error Lookup [v5]</Value> <Key Name="InprocServer32"> <Value Name="">lcpi.ibprovider-v5_vc17_w64_prof_i.dll</Value> <Value Name="ThreadingModel">Free</Value> </Key> </Key> </Key> <!-- /CLSID --> <Key Name="LCPI.IBProvider.5"> <Value Name="">LCPI OLE DB Provider for InterBase [v5]</Value> <Key Name="CLSID">{769A12A0-04BF-11D8-AE8B-00A0C907DB93}</Key> </Key> <Key Name="LCPI.IBProvider"> <Value Name="">LCPI OLE DB Provider for InterBase [v5]</Value> <Key Name="CurVer">LCPI.IBProvider.5</Key> </Key> </Key> </Registry> </ECM>
As you can see, the file contains data identical to the descriptions of COM objects in the Windows Registry.
In «InprocServer32» key you can specify both the absolute location of the binary file and the indirect one. In the latter case, the base directory will be the directory with ECM.
Settings-files are generated manually, but not everything is so scary — the «LCPI Easy COM» distribution kit already has ready-made files describing the IBProvider v5 and LCPI OLE DB Services components.
That is, in the end, «LCPI Easy COM» is currently four files.
For 32-bit processes, these are:
- lcpi.infrastructure.os.windows.ole32-v01_w32.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w32.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll.settings
For 64-bit processes, these are:
- lcpi.infrastructure.os.windows.ole32-v01_w64.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w64.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll.settings
«LCPI Easy COM» can only work with DLL servers and requires special support from the latter.
This means that «LCPI Easy COM» is not able to create, for example, ADODB COM-objects. Even if they are registered in the settings file.
And conversely, you cannot create components bound to «LCPI Easy COM» via Native Windows COM.
Let is summarize what we said earlier.
The LCPI COM API is the set of functions implemented by the pair of DLLs:
- lcpi.infrastructure.os.windows.ole32-v01_wXXXX.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_wXXXX.dll
Concrete implementations can be either projections to Native Windows COM or projections to «LCPI Easy COM».
Now let is move on to the practical part and see — how to use the «LCPI Easy COM» in client applications?
Practice
There are two options for using the LCPI COM API.
- In native applications.
- In .NET applications.
In the first case, you should replace the COM API calls with their counterparts with the «LCPI_OS__» prefix.
You can find the list of supported functions in the header files in the «src\cpp\public\source\lcpi\infrastructure\os» directory of the «LCPI Easy COM» package.
We will not waste time on this here and will immediately move on to the second, most interesting option — .NET applications.
To support different variants of the COM API in .NET applications, instead of direct calls to the Native Windows COM API, lcpi.lib.com.IComApiProvider interface is used.
There are two implementations of this interface:
- lcpi.lib.com.NativeComApiProvider
- lcpi.lib.com.LcpiComApiProvider
The first implementation works with the Native Windows COM API.
The second implementation works with the LCPI COM API, which is the previously described pair of DLLs:
- lcpi.infrastructure.os.windows.ole32-v01_wXXXX.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_wXXXX.dll
By default, the ADO.NET provider works with NativeComApiProvider.
In order to use the LCPI COM API, you need to:
- Create an LcpiComApiProvider object specifying the directory with LCPI COM API files.
- Pass the COM API provider to CreateWithComApiProvider static method of the required ADO.NET provider component.
Objects of the LcpiComApiProvider class are created in the standard way — through the call of a constructor.
// Constructor public LcpiComApiProvider(string rootDir);
Depending on the type of the executing process, the LcpiComApiProvider will load either 32-bit modules or 64-bit modules from the directory specified in rootDir.
It makes sense to store 32-bit and 64-bit files in different directories and form the value of the rootDir parameter taking into account the bitness of the current process.
Regarding the second point, the ADO.NET provider contains the following classes that create internal COM objects via COM API:
- OleDbServices
- OleDbConnectionStringBuilder
- OleDbConnection
- OleDbEnumerator
To support IComApiProvider, these classes define static methods with the common name CreateWithComApiProvider.
Let is note some things related to these classes.
OleDbEnumerator will currently only work with Native Windows COM due to the lack of «MSDAENUM» COM object with «LCPI Easy COM» support.
The global instance of OleDbServices, used by default by OleDbConnection objects, always uses NativeComApiProvider.
If the OleDbConnection and OleDbServices objects are bound to different COM API providers, then calling OleDbConnection.Open will throw an exception. Therefore, it makes sense to create OleDbConnection objects by calling OleDbServices.CreateInstance — in this case, the connection object will «inherit» the service object’s COM API provider.
Closed OleDbConnection object allows changing the COM API provider via the ComApiProvider RW-property.
Test project
We will use Visual Studio 2017, Firebird 3, and the employee.fdb database to create a test case. The application is expected to run in a 32-bit process. To do this, in the project settings, you need to check the “Prefer 32-bit” checkbox.
So…
1. It needs to install «LCPI ADO.NET Data provider for OLE DB» and specify the installation of assemblies for FW4.8.
2. Create in VS2017 a console project Example_001 in C# for .NET FW 4.8.
3. Add references to lcpi.lib and lcpi.data.oledb to our project.
4. Write the following code:
using System; using xdb=lcpi.data.oledb; using com_lib=lcpi.lib.com; namespace Example_001 { class Program { private const string c_cn_str ="provider=LCPI.IBProvider.5;" +"location=inet4://localhost/d:\\database\\fb_03_0_0\\employee.fdb;" +"dbclient_type=fb.direct;" +"auto_commit=true;" +"user id=SYSDBA;" +"password=masterkey;"; static void Main(string[] args) { com_lib.IComApiProvider comApiProvider=null; try { var asmPath =System.Reflection.Assembly.GetExecutingAssembly().Location; var curAsmDir =System.IO.Path.GetDirectoryName(asmPath); switch(IntPtr.Size) { case 4: curAsmDir=System.IO.Path.Combine(curAsmDir,"private-w32"); break; case 8: curAsmDir=System.IO.Path.Combine(curAsmDir,"private-w64"); break; default: throw new ApplicationException("ERROR!"); }//switch comApiProvider =new com_lib.LcpiComApiProvider(curAsmDir); var svc =xdb.OleDbServices.CreateWithComApiProvider(comApiProvider); var cn =svc.CreateConnection(c_cn_str); cn.Open(); var cmd =new xdb.OleDbCommand("select count(*) from employee",cn); object value =cmd.ExecuteScalar(); Console.WriteLine("VALUE: {0}",value); } catch(Exception e) { Console.WriteLine("ERROR: {0} - {1}",e.Source,e.Message); } } } }
5. Compile it and run it right away (Ctrl+F5). The following error should appear:
Failed to load DLL [D:\Users\…\Samples\Example_001\bin\Debug\private-w32\lcpi.infrastructure.os.windows.ole32-v01_w32.dll]. Win32 error code: 126.
The COM error code: 0x8007007E.
It means that LcpiComApiProvider could not load one DLL of the LCPI COM API.
6. Copy to the directory «bin\Debug\private-w32» 32-bit files «LCPI Easy COM»:
- lcpi.infrastructure.os.windows.ole32-v01_w32.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w32.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll.settings
7. Check the file name «lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll.settings». If it has the «—example» suffix, then remove this suffix.
8. Run our program again. We see a new error:
1. [lcpi.infrastructure.os.windows.ecm] [win32] Fail dynamic library loading [D:\Users\…\Samples\Example_001\bin\Debug\private-w32\lcpi.oledb_services-v1_vc17_w32_prof_i.dll]. WIN32 error code: 126.
The COM error code: 0x8007007E.
———————————
2. [lcpi.data.oledb.OleDbServices] Failed to create a system component for OLE DB provider initialization. Identifier of component: 2206CDB0-19C1-11D1-89E0-00C04FD7A829.
The COM error code: 0x8007007E.
It means that ECM does not find the lcpi.oledb_services-v1_vc17_w32_prof_i.dll file.
Download «LCPI OLE DB Services» (vc17, 32bit).
Install it.
From the directory «c:\Program Files (x86)\LCPI\OleDbServices.1\bin» copy the following files to «bin\Debug\private-w32»:
- lcpi.infrastructure.core-v01_vc17_w32_i.dll
- lcpi.infrastructure.multitasking.ibp-v03_vc17_w32_i.dll
- lcpi.oledb_services-v1_vc17_w32_prof_i.dll
Do not copy from «c:\Program Files (x86)\LCPI\OleDbServices.1\bin» these files:
- lcpi.infrastructure.os.windows.ole32-v01_w32.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w32.dll
This is the projection of the LCPI COM API onto the Native Windows COM API.
9. Run our program again (Ctrl+F5) and get another error:
1. [lcpi.infrastructure.os.windows.ecm] [win32] Fail dynamic library loading [D:\Users\…\Samples\Example_001\bin\Debug\private-w32\lcpi.ibprovider-v5_vc17_w32_prof_i.dll]. Код ошибки WIN32: 126.
The COM error code: E_FAIL.
———————————
2. [LCPI.OleDbServices.DataInitManager.Global.1] Failed to create OLE DB provider [clsid: {769A12A0-04BF-11D8-AE8B-00A0C907DB93}][clsctx: 1].
The COM error code: 0x8007007E.
———————————
3. [lcpi.data.oledb.OleDbConnection] Failed to create OLE DB provider with ProgID «LCPI.IBProvider.5».
The COM error code: 0x8007007E.
Here a service object cannot create the OLE DB provider – «lcpi.ibprovider-v5_vc17_w32_prof_i.dll» file is missing.
10. Copy «lcpi.ibprovider-v5_vc17_w32_prof_i.dll» from «IBProvider Professional Edition» (vc17, 32bit) installation directory to «bin\Debug\private-w32» directory.
11. Run our program again. If everything was configured correctly, then the program should work without errors. Perhaps the program will display the following text:
In the final, «bin\Debug\private-w32» will has the following files:
- lcpi.ibprovider-v5_vc17_w32_prof_i.dll
- lcpi.infrastructure.core-v01_vc17_w32_i.dll
- lcpi.infrastructure.multitasking.ibp-v03_vc17_w32_i.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w32.dll.settings
- lcpi.infrastructure.os.windows.ole32-v01_w32.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w32.dll
- lcpi.oledb_services-v1_vc17_w32_prof_i.dll
If you do not have «IBProvider Professional Edition» (lcpi.ibprovider-v5_vc17_w32_prof_i.dll), then you can use «Free IBProvider».
To do this, change the programmatic identifier of the provider in the c_cn_str variable.
private const string c_cn_str ="provider=LCPI.IBProvider.5.Free;" // <----------- +"location=inet4://localhost/d:\\database\\fb_03_0_0\\employee.fdb;" +"dbclient_type=fb.direct;" +"auto_commit=true;" +"user id=SYSDBA;" +"password=masterkey;";
Then copy the file «lcpi.ibprovider-v5_vc17_w32_free_i.dll» to «bin\Debug\private-w32».
If this is not done, the program will generate the following error:
1. [lcpi.infrastructure.os.windows.ecm] [win32] Fail dynamic library loading [D:\Users\…\Samples\Example_001\bin\Debug\private-w32\lcpi.ibprovider-v5_vc17_w32_free_i.dll]. Код ошибки WIN32: 126.
The COM error code: E_FAIL.
———————————
2. [LCPI.OleDbServices.DataInitManager.Global.1] Failed to create OLE DB provider [clsid: {769A12AC-04BF-11D8-AE8B-00A0C907DB93}][clsctx: 1].
The COM error code: 0x8007007E.
———————————
3. [lcpi.data.oledb.OleDbConnection] Failed to create OLE DB provider with ProgID «LCPI.IBProvider.5.Free».
The COM error code: 0x8007007E.
Run the program and see the result equal to the previous one:
If the program is launched in a 64-bit process, then you need to create the «bin\Debug\private-w64» directory and add the following files to it:
- lcpi.ibprovider-v5_vc17_w64_prof_i.dll
- lcpi.infrastructure.core-v01_vc17_w64_i.dll
- lcpi.infrastructure.multitasking.ibp-v03_vc17_w64_i.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll
- lcpi.infrastructure.os.windows.lcpi_easy_com-v01_w64.dll.settings
- lcpi.infrastructure.os.windows.ole32-v01_w64.dll
- lcpi.infrastructure.os.windows.oleaut32-v01_w64.dll
- lcpi.oledb_services-v1_vc17_w64_prof_i.dll
To work through the 64-bit «Free IBProvider», you need to copy the file «lcpi.ibprovider-v5_vc17_w64_free_i.dll» in «private-w64» folder.