1

I have encountered an issue and can't work out why it is happening.

Context:

  • Windows 11
  • Visual Studio 2022
  • MFC Dialog 64 bit
  • DEBUG mode

When I run the software in DEBUG mode, and perform a specific action (reading a ACCDB database) and then terminate the software, I get an exception.

I use the basic methods to open and close the database:

if (m_dbDatabase.OpenEx(strDBConnectString, CDatabase::noOdbcDialog))
{
    m_pRecords = std::make_unique<CRecordset>(&m_dbDatabase);
}

And:

void CPTSDatabase::CloseDatabase()
{
    if (m_dbDatabase.IsOpen())
        m_dbDatabase.Close();
}

The database activity is operating correctly and not causing exceptions. Yet, when I terminate the software:

enter image description here

static void __cdecl try_cor_exit_process(UINT const return_code) throw()
{
    __crt_unique_hmodule mscoree;
    if (!GetModuleHandleExW(0, L"mscoree.dll", mscoree.get_address_of()))
        return;

    auto const cor_exit_process = __crt_get_proc_address<exit_process_pft>(mscoree.get(), "CorExitProcess");
    if (!cor_exit_process)
        return;

    cor_exit_process(return_code);
}

I don't understand why did message is being displayed. My application InitInstance and ExitInstance both make reference to (respectively):

  • ::CoInitialize(nullptr);
  • ::CoUninitialize();

What I can confirm is that if I do not run the code that opens the database to read in and then close my application, I will not get this exception.


This is the Time Delay Debug entry: enter image description here

Not sure how that helps me at this stage.

BTW. If I comment out CoUninitialize it makes no difference. I sill get the error.


I don't know if it is related, when the OpenEx call is performed on the database I debugged into it and noticed:

d:\a01_work\43\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\dbcore.cpp(616) : AppMsg - Warning: ODBC Success With Info, d:\a01_work\43\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\dbcore.cpp(174) : AppMsg - Driver's SQLSetConnectAttr failed

The actual function returned true though and gave me a valid database. I do not know if this message is part of the problem.


Update

I thought I would try a simple test project (dialog) and simply get it in the dialogs OnInitDialog to open my DB and then close it. Then I shut the test app down.

If I run this code in DEBUG x86:

if (db.OpenEx(_T("Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\My Documents\\Public Talks\\Public Talks.MDB;Pwd=~~~~"), CDatabase::noOdbcDialog))
{
    AfxMessageBox(_T("DB Opened"));
    db.Close();
}

And close that app down - no issues triggered.

But when I try this with DEBUG x64 and close it:

if (db.OpenEx(_T("Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=D:\\My Documents\\Public Talks\\Public Talks.ACCDB;Pwd=~~~~~"), CDatabase::noOdbcDialog))
{
    AfxMessageBox(_T("DB Opened"));
    db.Close();
}

Now that triggers:

enter image description here

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • [Possibly related](https://devblogs.microsoft.com/oldnewthing/20200129-00/?p=103380)? I'm assuming your database uses the COM interface and that you have smart pointers around ... maybe try moving the `CoUninitialize()` call to the (very end of) your application's destructor? – Adrian Mole Mar 15 '22 at 11:29
  • ... more specifically, when does the object owned by `m_pRecords` get deleted/destroyed? – Adrian Mole Mar 15 '22 at 11:55
  • There's almost no way to call `CoUninitialize` properly. It's best not called at all. The issue itself bubbles up from Mso20win32client.dll, where the code apparently attempts to dereference a null pointer. Could be the result of getting some COM reference count management wrong. A full dump is probably insightful, and if you don't mind the effort, you **will** pinpoint the issue when pulling a [Time Travel Debugging](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace. – IInspectable Mar 15 '22 at 11:56
  • @IInspectable Please see updated question. – Andrew Truckle Mar 15 '22 at 12:21
  • @AdrianMole The records object would have been deleted automatically when the `CPTSDatabase` object went out of scope, which would have been nested about 4 dialogs deep in my process. I have also tried calling `release` on that pointer **before** closing the database. Made no difference. – Andrew Truckle Mar 15 '22 at 12:22
  • @AdrianMole It seems to have nothing to do with the CPTSDatabase records pointer. I turned it into a new / delete pointer and I still got the exception. – Andrew Truckle Mar 15 '22 at 12:27
  • It's called *"Time **Travel** Debugging"* because it allows you to literally travel in time. Now that you know where the null pointer gets dereferenced, move back in time to see when (and why) it was set to null. – IInspectable Mar 15 '22 at 12:35
  • @IInspectable I will have to put this on hold. I got so many other things to upgrade. I'll have to come back to it. A lot to learn and take in. – Andrew Truckle Mar 15 '22 at 12:47
  • 1
    Sure, none of that is trivial or straight forward. The good news: If you keep the TTD trace, it will indefinitely preserve the defect, and you can come back to it at any time in the future and *know* that all the information you need is in there. – IInspectable Mar 15 '22 at 13:05
  • 1
    FWIW, I'm experiencing the exact same problem. It's in an application that I've recently ported to 64-bit and I'm also using MS-Access via ODBC. – criddell May 10 '23 at 14:56

0 Answers0