question

ngelJosRiesgo-9221 avatar image
0 Votes"
ngelJosRiesgo-9221 asked ngelJosRiesgo-9221 edited

C++ - problem when using Acedao.dll: different behaviour with installed Access vs. the 2016 redistributable engine

Hi,

I am working on a C++ Windows native application where we #import the Acedao.dll dynamic library in order to read and edit .accdb and .mdb files.

As the users of the application can have different Acedao configurations, we recently added some initialisation code that checks the version of the registered Acedao.dll library by calling the function DAO::_DBEngine::GetVersion. We tested this on a machine where the "Microsoft Access Database Engine 2016 Redistributable" had been installed and GetVersion returned "16.0". We found that with the older 2010 redistributable the return value was "14.0". So far, so good.

The problem arises when the user has a full installation of Office, rather than the redistributable engine. Even if the user has installed the redistributable (v. 16.0.4492.1000; 591 kB), any Office updates will change the registered version of the DLL to the latest Office one (as of January 2021, v. 16.0.13530.20062; 795 kB). This behaviour was mentioned in a thread in the old MSDN forums: AceDao - problem with requery. But when calling GetVersion with this latest version of Office a _com_error is thrown with a null description.

The following minimal C++ program displays the problem. The "C:\acedao_test\ACEDAO.DLL" file can be either the redistributable one or the latest Office one; it doesn't matter. But the behaviour will be different depending on which version is the one registered on the host system.

 #include <objbase.h>
    
 #include <iostream>
    
 #import <C:/acedao_test/ACEDAO.DLL> rename("EOF", "AdoNSEOF")
    
 int main()
 {
     CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    
     DAO::_DBEngine* daoEngine = nullptr;
    
     HRESULT hResult = CoCreateInstance(__uuidof(DAO::DBEngine), nullptr, CLSCTX_ALL, IID_IDispatch, reinterpret_cast<LPVOID*>(&daoEngine));
    
     if (daoEngine == nullptr)
     {
         std::wcout << L"Failed to find the Access Database engine" << std::endl;
     }
     else
     {
         try
         {
             _bstr_t version = daoEngine->GetVersion();
             std::wcout << L"Found Access Database engine with version " << version.GetBSTR() << L"." << std::endl;
         }
         catch (const _com_error& comError)
         {
             _bstr_t comErrorDescription = comError.Description();
             std::wcout << L"Error calling DAO::_DBEngine::GetVersion; description: " << (comErrorDescription.length() > 0 ? comErrorDescription.GetBSTR() : L"NULL") << std::endl;
         }
     }
    
     CoUninitialize();
 }


With v. 16.0.4492.1000 (redistributable), the output is:
"Found Access Database engine with version 16.0."

And with v. 16.0.13530.20062 (Office update), the output is:
"Error calling DAO::_DBEngine::GetVersion; description: NULL"

Is there any rationale for this change in behaviour? Or is our attempt to use GetVersion wrong for some reason?

Note: Since the MSDN forum has been migrated to this new Q&A site, there doesn't seem to be any appropriate "Access development" tag. The closest I've found is "office-addins-dev", although my question is not actually about add-ins.



c++office-addins-dev
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

ngelJosRiesgo-9221 avatar image
0 Votes"
ngelJosRiesgo-9221 answered ngelJosRiesgo-9221 edited

After a lot of trial and error, going through what the Office Update was changing in the Windows registry, attempting various changes in the configuration of the VS project and many other tests to no avail, I finally figured out what the problem was. I'm writing this answer to my own question in case anyone comes here with the same problem.

I was pointed in the right direction by a comment to an old similar question on the Stack Overflow site. Basically, the problem lies with the following line:

 HRESULT hResult = CoCreateInstance(__uuidof(DAO::DBEngine), nullptr, CLSCTX_ALL, IID_IDispatch, reinterpret_cast<LPVOID*>(&daoEngine));

This line can be found all over the Internet and the person who originally wrote that code probably copied it from some sample code they found online. The problem is... this line of code is wrong. While this used to work without problems in the past, it stopped working at some point, probably because of recent changes made by Microsoft in how COM objects are instantiated.

The problem lies in the third parameter IID_IDispatch. According to the CoCreateInstance documentation, the first parameter should be the unique id of the concrete implementation of the object (the "class") that we want to create, while the third parameter should be the unique id of the "interface" that we want the object to support. Because we are using IID_IDispatch, we're basically telling the COM system that we will be using the newly allocated pointer as an IDispatch* variable, and the CoCreateInstance actually gives us that. But we are then casting it to a DAO::_DBEngine* pointer, which is more than the IDispatch object we asked for!

So, the solution is simply to use the unique id for the DAO::_DBEngine interface:

 HRESULT hResult = CoCreateInstance(__uuidof(DAO::DBEngine), nullptr, CLSCTX_ALL, __uuidof(DAO::_DBEngine), reinterpret_cast<LPVOID*>(&daoEngine));

And now it works like a charm again.


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.