CDatabase::Close() causing exception
Tim Robinson -- timtroyr@ionet.net
Tuesday, March 11, 1997
Environment: VC++ 4.2b, NT 4.0
OK, I really hate to say this because it always sounds like a copout, but I
think I've got my hands on a nasty MFC bug. If I'm right, I'm desparate
for a workaround. If I'm wrong, I could stand being given a very big clue
since I've scoured my MSDN CD's and everything else I could find to no
avail. Mike B. will probably point out that I'm an idiot and I missed some
"obvious" documentation 8-), but idiot or genius, I need to figure this out.
The Primary Symptom:
First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access Violation.
On the one hand, an exception like this should be easy to trace, but
unfortunately it occurs *randomly* even though I've got a deterministic
program and there are (to the best of my knowledge) no un-initialized
values relevant to the problem. Further, my stack trace isn't inside my
application, but leads me to some pure assembler with a stack only 2
entries deep. I've basically traced this to misbehaviour in
CDatabase::Close().
The particulars:
I've got a global variable:
extern CDatabase _Database;
... and I use it for all my CRecordSet variables. However, I get my crash
without involving any of them. In my CMainFrame::OnCreate, just after
showing my splash screen, I do the following:
// open the database while the splash screen shows
CString temp;
temp.Format( "DSN=%s;UID=%s;PWD=%s",
AfxGetApp()->GetProfileString( SETTINGS, "Database", "MyDB" ),
AfxGetApp()->GetProfileString( SETTINGS, "DBLogin", "Admin" ),
AfxGetApp()->GetProfileString( SETTINGS, "DBPassword", "" ) );
TRY {
_Database.OpenEx( temp );
}
CATCH_ALL(e)
AfxMessageBox( IDS_DATABASEMISSING, MB_ICONSTOP|MB_OK );
END_CATCH_ALL
Then later on there is a perfectly boring close-down of the CDatabase
object as follows:
void CMainFrame::OnClose()
{
TRACE( "Closing DB\n" );
_Database.Close();
TRACE( "DB Closed\n" );
SaveBarState( "DockState" );
CMDIFrameWnd::OnClose();
}
As I said before, the problem is random. The TRACE statements got me to
the problem and it has to do with threading. (Richard Morris -- in another
post to the list -- seems to have had the same problem with threading and
DAO, so there is something to this.) The threads, however, aren't from my
program which has only a main thread. When the program runs properly, I
get the following in my debug window (my annotations in brackets):
The thread 0xD2 has exited with code 0 (0x0).
Closing DB
The thread 0xE8 has exited with code 0 (0x0). [1]
DB Closed
The thread 0x55 has exited with code 0 (0x0). [2]
The thread 0x61 has exited with code 0 (0x0). [3]
The thread 0xD3 has exited with code 0 (0x0).
Sometimes the threads marked with [2] and [3] will precede the line "DB
Closed", but no matter... there is no crash. However, when I get a crash,
the output in the debug window looks like:
The thread 0xD7 has exited with code 0 (0x0).
Closing DB
DB Closed
First-chance exception in MyApp.exe: 0xC0000005: Access Violation.
When that thread closing that should follow "Closing DB" isn't there before
I return from the call to CDatabase::Close(), I'm dead. The line of code
in MFC responsible for causing messages [1], [2] and [3] above is in
DBCORE.CPP in CDatabase::Close() and reads:
AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
My only guess is that a thread in the ODBC drivers is expecting something
still to be available in the MFC code (why?) that isn't there anymore. Due
to the vagaries of thread priorities, a delay finds the ODBC thread without
a resource and so it dies. That this bug mostly occurs IMMEDIATELY after a
re-build (when the hard-drive is still humming) must say something about this.
Quick repeat on a previous detail: no, I don't open any CRecordsets. Just
a CDatabase::OpenEx, then a subsequent CDatabase::Close with damn near no
other activity than an idle application in between. Naturally, it's
possible to assume I've got something trashing memory, however, I've got
two otherwise unrelated programs exhibiting identical symptoms and I doubt
they could both be trashing memory the same way.
Any advice or insight would be appreciated.
| Tim Robinson | Once you let go of your liberty, you |
| http://www.ionet.net/~timtroyr | won't easily get it back. -- Plautus |
Venkat NJAOST -- VVaitheeswa@NJAOST.ML.com
Friday, March 14, 1997
[Mini-digest: 7 responses]
Hai,
I am replying this directly as I am not sure whether this is a right
solution for you. But some 'nightmare' experiences of mine last year may
throw some light into your problem. So read on ..
a) We found out that we had 'pure' timing problems whenever we
encountered these type of problems. All I would have is ??? in my debug
window assembler code.
b) If you find these symptoms, try adjusting the PageTimeOut settings
for your ODBC driver. I don't know what type of database you are using,
but looks like you are encountering this in Access (.mdb) files. If not
do NOT read ahead.
c) A wait before you call DB->Close() would really help the ODBC driver
to wind their threads.
This may not be a neat soultion but waiting for a few milliseconds
before winding the app down did solve the problem for us.
Good Luck (whether you hear it from Mike or not)
>----------
>From: Tim Robinson[SMTP:timtroyr@ionet.net]
>Sent: Tuesday, March 11, 1997 2:58 AM
>To: mfc-l@netcom.com
>Subject: CDatabase::Close() causing exception
>
>Environment: VC++ 4.2b, NT 4.0
>
>OK, I really hate to say this because it always sounds like a copout, but I
>think I've got my hands on a nasty MFC bug. If I'm right, I'm desparate
>for a workaround. If I'm wrong, I could stand being given a very big clue
>since I've scoured my MSDN CD's and everything else I could find to no
>avail. Mike B. will probably point out that I'm an idiot and I missed some
>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>
>The Primary Symptom:
>
>First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access
>Violation.
>
>On the one hand, an exception like this should be easy to trace, but
>unfortunately it occurs *randomly* even though I've got a deterministic
>program and there are (to the best of my knowledge) no un-initialized
>values relevant to the problem. Further, my stack trace isn't inside my
>application, but leads me to some pure assembler with a stack only 2
>entries deep. I've basically traced this to misbehaviour in
>CDatabase::Close().
>
>The particulars:
>
>I've got a global variable:
>
> extern CDatabase _Database;
>
>... and I use it for all my CRecordSet variables. However, I get my crash
>without involving any of them. In my CMainFrame::OnCreate, just after
>showing my splash screen, I do the following:
>
> // open the database while the splash screen shows
> CString temp;
> temp.Format( "DSN=%s;UID=%s;PWD=%s",
> AfxGetApp()->GetProfileString( SETTINGS, "Database", "MyDB" ),
> AfxGetApp()->GetProfileString( SETTINGS, "DBLogin", "Admin" ),
> AfxGetApp()->GetProfileString( SETTINGS, "DBPassword", "" ) );
>
> TRY {
> _Database.OpenEx( temp );
> }
> CATCH_ALL(e)
> AfxMessageBox( IDS_DATABASEMISSING, MB_ICONSTOP|MB_OK );
> END_CATCH_ALL
>
>Then later on there is a perfectly boring close-down of the CDatabase
>object as follows:
>
> void CMainFrame::OnClose()
> {
> TRACE( "Closing DB\n" );
> _Database.Close();
> TRACE( "DB Closed\n" );
> SaveBarState( "DockState" );
>
> CMDIFrameWnd::OnClose();
> }
>
>As I said before, the problem is random. The TRACE statements got me to
>the problem and it has to do with threading. (Richard Morris -- in another
>post to the list -- seems to have had the same problem with threading and
>DAO, so there is something to this.) The threads, however, aren't from my
>program which has only a main thread. When the program runs properly, I
>get the following in my debug window (my annotations in brackets):
>
> The thread 0xD2 has exited with code 0 (0x0).
> Closing DB
> The thread 0xE8 has exited with code 0 (0x0). [1]
> DB Closed
> The thread 0x55 has exited with code 0 (0x0). [2]
> The thread 0x61 has exited with code 0 (0x0). [3]
> The thread 0xD3 has exited with code 0 (0x0).
>
>Sometimes the threads marked with [2] and [3] will precede the line "DB
>Closed", but no matter... there is no crash. However, when I get a crash,
>the output in the debug window looks like:
>
> The thread 0xD7 has exited with code 0 (0x0).
> Closing DB
> DB Closed
> First-chance exception in MyApp.exe: 0xC0000005: Access Violation.
>
>When that thread closing that should follow "Closing DB" isn't there before
>I return from the call to CDatabase::Close(), I'm dead. The line of code
>in MFC responsible for causing messages [1], [2] and [3] above is in
>DBCORE.CPP in CDatabase::Close() and reads:
>
> AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
>
>My only guess is that a thread in the ODBC drivers is expecting something
>still to be available in the MFC code (why?) that isn't there anymore. Due
>to the vagaries of thread priorities, a delay finds the ODBC thread without
>a resource and so it dies. That this bug mostly occurs IMMEDIATELY after a
>re-build (when the hard-drive is still humming) must say something about
>this.
>
>Quick repeat on a previous detail: no, I don't open any CRecordsets. Just
>a CDatabase::OpenEx, then a subsequent CDatabase::Close with damn near no
>other activity than an idle application in between. Naturally, it's
>possible to assume I've got something trashing memory, however, I've got
>two otherwise unrelated programs exhibiting identical symptoms and I doubt
>they could both be trashing memory the same way.
>
>Any advice or insight would be appreciated.
>
>
>| Tim Robinson | Once you let go of your liberty, you |
>| http://www.ionet.net/~timtroyr | won't easily get it back. -- Plautus |
>
>
-----From: "GoroKhM1"
I had similar problem with DAO: _sometimes_ crash on exit.
KB Q153897 solved it and may give you an idea.
In my case framework unloads MSJT3032.DLL (for DAO) _before_
database close.
MarkG@usa.net
-----From: riddell@kudos.net (codeware)
At 01:58 AM 3/11/97 -0600, you wrote:
>Environment: VC++ 4.2b, NT 4.0
>
>OK, I really hate to say this because it always sounds like a copout, but I
>think I've got my hands on a nasty MFC bug. If I'm right, I'm desparate
>for a workaround. If I'm wrong, I could stand being given a very big clue
>since I've scoured my MSDN CD's and everything else I could find to no
>avail. Mike B. will probably point out that I'm an idiot and I missed some
>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>
>The Primary Symptom:
>
>First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access Violation.
>
Get the latest version of the compiler. I had the same problem as you are
reporting. This week I received VC version 5 in the mail and after
recompiling the error went away.
Cory
-----From: jeremy@omsys.com (Jeremy H. Griffith)
On Tue, 11 Mar 1997 01:58:12 -0600, Tim Robinson wrote:
>Environment: VC++ 4.2b, NT 4.0
>
>OK, I really hate to say this because it always sounds like a copout, but I
>think I've got my hands on a nasty MFC bug. If I'm right, I'm desparate
>for a workaround. If I'm wrong, I could stand being given a very big clue
>since I've scoured my MSDN CD's and everything else I could find to no
>avail. Mike B. will probably point out that I'm an idiot and I missed some
>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>I've got a global variable:
>
> extern CDatabase _Database;
At the risk of being charged with another "red herring" ;-), I'd suggest
you try:
extern CDatabase *pDatabase;
and wait until your App InitInstance (or at least ctor) to instantiate.
I think you are asking for trouble by having any globals that require
dynamic memory allocation, because the allocator isn't initialized yet
at that time. This could lead to problems when the memory is freed,
which appears to be the sort of problem you are having...
--Jeremy
-----From: "Doug Brubacher"
In a project I was working on last year I had a similar external
definition for a database object. The database object was actually
defined in a static library which I had created.
I ran into similar problems to what you are seeing and resolved it by
making my database object a pointer and explicitly new-ing and
delete-ing it. I think I had to play with the location of the new and
delete but since I do not have the code in front of me I can not give
you a definite answer.
I hope this helps, and does not fall into that red-herring category of
solutions.
Regards
Doug Brubacher
Doug_Brubacher@compuware.com
-----From: Mike Blaszczak
At 01:58 3/11/97 -0600, Tim Robinson wrote:
>Environment: VC++ 4.2b, NT 4.0
>Mike B. will probably point out that I'm an idiot and I missed some
>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
I grepped the archives that I have here at home, and I can't find any
point where, in the history of the list, I called someone an idiot. As a
result, your comment seems like unjustified defamation. I guess I'm obliged
to give you the benefit of the doubt, though, and try and answer your
question anyway.
>The Primary Symptom:
>First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access
Violation.
>Further, my stack trace isn't inside my
>application, but leads me to some pure assembler with a stack only 2
>entries deep. I've basically traced this to misbehaviour in
>CDatabase::Close().
It would really help to see the exact stack trace and hear something about
the state of the pertinent variables when the crash happens. Just because
the trace is shallow doesn't mean it's meaningless.
>The particulars:
>I've got a global variable:
> extern CDatabase _Database;
>
>... and I use it for all my CRecordSet variables. However, I get my crash
>without involving any of them. In my CMainFrame::OnCreate, just after
>showing my splash screen, I do the following:
Odds are that you're using the CDatabase object before it's been initialized,
or after it's been shut down. Global objects construct and destruct with
special rules that are described by the C++ language unstandard. The compiler
follows this part of the unstandard very closely. You need to make sure
what you're doing falls well within those rules.
One rule that could be affecting you is that a global object isn't guaranteed
to be constructed until some code in the module where it's declared actually
runs. Because of this rule, it's possible that you're using your object before
it's been constructed. Since the C++ unstandard has similar rules for
destruction, it's possilbe that you're destroying the object
>(Richard Morris -- in another post to the list -- seems to have
>had the same problem with threading and DAO, so there is
>something to this.)
You're making quite an invalid jump. DAO is well-documented to not be
safe to use across threads, period. If someone is trying to do that,
they can expect nothing but trouble. The documentation says:
"DAO itself is not multithreaded, so you can't use the
MFC DAO classes in multiple threads. Confine your
DAO code to a single thread of execution."
in at least a couple of places. Since you're not using multiple threads,
there's absolutely no connection between the problem Richard Morris saw
and the problem you're seeing.
>The line of code
>in MFC responsible for causing messages [1], [2] and [3] above is in
>DBCORE.CPP in CDatabase::Close() and reads:
>
> AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
The underlying driver might be using threads, itself, to get the job done.
I don't think ODBC itself ever spawns extra threads, but it might do so,
too. But I don't think your conclusion that MFC is taking away something
a closing thread in ODBC needs has any merit.
I'm not convinced you've found a bug in MFC simply because I really don't
understand what you're doing with the class. If you've not carefully
worked out the lifecycle of your global object, you can get into very bad
trouble. That's not a bug: it's a fact of life for any C++ class.
If you want your object to be global, you can skip learning about the
scope and lifetime of global objects by adding the CDatabase object to
your CWinApp's instance data and accessing it via AfxGetApp(). This is
really the safest way to go, since you know that the CDatabase object is
going to construct only when MFC's initialized and destroy when it knows
that everything is safe.
.B ekiM
http://www.nwlink.com/~mikeblas/
These words are my own. I do not speak on behalf of Microsoft.
One is too many and a million is not enough.
-----From: Richard Morris
I came to the same conclusion after encountering this error and much debugging.
I found a workaround in my code. The bug seems to have to do with
the initialization and cleanup sequence of OLE, DAO, and thread management.
I am including a recent e-mail I posted which discussed my workaround for
this problem.
Should you find any further information, please let me know, as I am interested
in summarizing and posting for the benefit of all.
Good luck.
My work around:
*****************************************************************************
Environment: VC++ 4.1, VC++ 4.2, 95, NT 3.51, NT 4.0
Here is some additional information which a number of us may find useful.
This info came about through some exploring I did after a few conversations
with others who were experiencing the same problem.
----------
In summary:
The problem seems connected to threads being managed by MFC,
Ole, and DAO. It seems that by arranging the order in which Ole, DAO,
and any threads which are started and stopped in InitInstance and ExitInstance
to be last in-first out cures the problem.
Try explicitly calling AfxOleInit(), then AfxDaoInit() as the first lines
in InitInstance, then in reverse order AfxDaoTerm(), AfxOleTerm(), as the
last lines in ExitInstance.
If anyone else has any further insights, I would appreciate it if you would send
me e-mail. I'll compile a digest of the information and then post it.
---------
For more information on the problem, see a copy of a previous message below:
I have seen this problem too in VC++ 4.1 and 4.2.
I suspect there are some rough edges in the MFC code that are related to
CWinThread, COle and CDAO, and thread management.
I reduced my code to just a few lines which would cause the access violation.
I found the it could be caused by starting a thread having nothing to do
with COle
or CDAO in CMyApp::InitInstance (which in the minimum case to cause the access
violation, did nothing but start and then go to sleep waiting for an event
telling it to terminate itself, no DAO, no OLE, nothing
else) and then opening and closing a CDaoDatabase. Then stopping and cleaning
up the thread in CMyApp::ExitInstance. Then when the COle code does the
deinitialization in CWinApp::ExitInstance the 0xc0000005 access violation
occurs.
I used the debugger and explored the MFC code trying to find the connection
to the access violation without much success. Also searched the Knowledge base
without results. But with a little deduction and experimentation, I found that
I could move starting the thread to CMainFrame and the problem was solved.
This lead me to the my initial suspicion.
I find it quite curious as in my app where I ran into this problem, I have
several other threads doing different tasks without any problems, but with
this one case of starting the thread in CMyApp::Initinstance, it seems there
is a gotcha waiting to bite, with this very specific set of events.
*****************************************************************************
At 01:58 AM 3/11/97 -0600, you wrote:
>Environment: VC++ 4.2b, NT 4.0
>
>OK, I really hate to say this because it always sounds like a copout, but I
>think I've got my hands on a nasty MFC bug. If I'm right, I'm desparate
>for a workaround. If I'm wrong, I could stand being given a very big clue
>since I've scoured my MSDN CD's and everything else I could find to no
>avail. Mike B. will probably point out that I'm an idiot and I missed some
>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>
>The Primary Symptom:
>
>First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access Violation.
****************************************************************
Live now.
Make now always the most precious time.
Now will never come again.
- Captain Jean-Luc Picard, U.S.S. Enterprise
Tim Robinson -- timtroyr@ionet.net
Tuesday, March 18, 1997
[Mini-digest: 2 responses]
I have had some semblance of success with the problem. But before I go
into my solution, a couple comments on the responses I got.
"GoroKhM1" wrote:
> a) We found out that we had 'pure' timing problems whenever we
>encountered these type of problems. All I would have is ??? in my debug
>window assembler code.
> b) If you find these symptoms, try adjusting the PageTimeOut settings
>for your ODBC driver. I don't know what type of database you are using,
>but looks like you are encountering this in Access (.mdb) files. If not
>do NOT read ahead.
> c) A wait before you call DB->Close() would really help the ODBC driver
>to wind their threads.
>
> This may not be a neat soultion but waiting for a few milliseconds
>before winding the app down did solve the problem for us.
Timing is close to solving the problem. You suggest a pause in the program
before shutdown. I'm normally loathe to do something of that nature unless
I'm controlling hardware that has such considerations built in. For
something strictly software that depends on thread interaction, I don't
think you can ever get a pause to work 100% of the time. However, I was
about ready to do this with the problem. As it was, I didn't.
-----------
MarkG@usa.net wrote:
>I had similar problem with DAO: _sometimes_ crash on exit.
>KB Q153897 solved it and may give you an idea.
>
>In my case framework unloads MSJT3032.DLL (for DAO) _before_
>database close.
Thank you for that one. I had rummaged through my MSDN thoroughly to find
info on the problem, but to no avail. After I plugged the Q153897 into the
search, the January 97 CD came up telling me there was nothing there by
that number. It was on the Web, but I didn't scan that as thoroughly. Now
the mystery is why that's not on my CD. I will, however, take the advice
of Q135897 in the future unless something in VC5.0 cures it. (See next note)
---------
riddell@kudos.net (codeware) wrote:
>Get the latest version of the compiler. I had the same problem as you are
>reporting. This week I received VC version 5 in the mail and after
>recompiling the error went away.
It's on its way to me. When it arrives, I may test this some more.
-------------
"Doug Brubacher" wrote:
> I ran into similar problems to what you are seeing and resolved it by
> making my database object a pointer and explicitly new-ing and
> delete-ing it. I think I had to play with the location of the new and
> delete but since I do not have the code in front of me I can not give
> you a definite answer.
>
> I hope this helps, and does not fall into that red-herring category of
> solutions.
Regrettably, I didn't mention that the code I posted to the list was not
how it started out. It started the way you suggested. I switched to a
global instance to a CDatabase object as a shot in the dark to cure the
problem. I'm guessing that it was the positioning of the new and delete
that actually cured your problem.
------------
Mike Blaszczak wrote:
>
>>Mike B. will probably point out that I'm an idiot and I missed some
>>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>
>I grepped the archives that I have here at home, and I can't find any
>point where, in the history of the list, I called someone an idiot. As a
>result, your comment seems like unjustified defamation. I guess I'm obliged
>to give you the benefit of the doubt, though, and try and answer your
>question anyway.
I never said you DID call someone that and I apologize if I conveyed that.
You can certainly call me an idiot if I missed some clearly posted solution
to the problem.
>It would really help to see the exact stack trace and hear something about
>the state of the pertinent variables when the crash happens. Just because
>the trace is shallow doesn't mean it's meaningless.
This is true. I regret that I assumed it was. I didn't note the stack
position and now have to go back and retrofit my program with the bug to
get that trace.
>Odds are that you're using the CDatabase object before it's been initialized,
>or after it's been shut down. Global objects construct and destruct with
>special rules that are described by the C++ language unstandard. The compiler
>follows this part of the unstandard very closely. You need to make sure
>what you're doing falls well within those rules.
>
>One rule that could be affecting you is that a global object isn't guaranteed
>to be constructed until some code in the module where it's declared actually
>runs. Because of this rule, it's possible that you're using your object
before
>it's been constructed. Since the C++ unstandard has similar rules for
>destruction, it's possilbe that you're destroying the object
I really regret not having mentioned that I tried this both as a global
instance and as a pointer where I explicitly controlled the ctor/dtor pair
besides CDatabase::OpenEx and CDatabase::Close(). I guess I had already
cleared it in my own head that the use of a global instance wasn't the
problem, so I didn't mention it. My ommission led to confusing both you
and Doug (above) about the nature of my code.
>>(Richard Morris -- in another post to the list -- seems to have
>>had the same problem with threading and DAO, so there is
>>something to this.)
>
>You're making quite an invalid jump. DAO is well-documented to not be
>safe to use across threads, period. If someone is trying to do that,
>they can expect nothing but trouble. The documentation says:
>
> "DAO itself is not multithreaded, so you can't use the
> MFC DAO classes in multiple threads. Confine your
> DAO code to a single thread of execution."
>
>in at least a couple of places. Since you're not using multiple threads,
>there's absolutely no connection between the problem Richard Morris saw
>and the problem you're seeing.
On first blush, I assumed so. I have had two apps with this problem. The
first that had the problem was heavily threaded and ODBC has matching
warnings about threading. (Yes, I've found a way to share the database
objects between threads.) In retrospect, I'm still a little confused and
continue to suspect a relationship, but I'll take your word for it.
>>The line of code
>>in MFC responsible for causing messages [1], [2] and [3] above is in
>>DBCORE.CPP in CDatabase::Close() and reads:
>>
>> AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
>
>The underlying driver might be using threads, itself, to get the job done.
>I don't think ODBC itself ever spawns extra threads, but it might do so,
>too. But I don't think your conclusion that MFC is taking away something
>a closing thread in ODBC needs has any merit.
Hmm... just watch the debug window for the creation and exit of the threads
when you open and close the database objects. I thought it was kinda hard
to miss.
>I'm not convinced you've found a bug in MFC simply because I really don't
>understand what you're doing with the class. If you've not carefully
>worked out the lifecycle of your global object, you can get into very bad
>trouble. That's not a bug: it's a fact of life for any C++ class.
It's a bug somewhere. If MFC is following the rules, and if the rules are
wrong, then arguably there is nothing wrong with MFC. The carpet is being
yanked out from under the code somewhere, though. I'm grateful to MarkG
for pointing out the KB article. It's the final solution, but read on.
>If you want your object to be global, you can skip learning about the
>scope and lifetime of global objects by adding the CDatabase object to
>your CWinApp's instance data and accessing it via AfxGetApp(). This is
>really the safest way to go, since you know that the CDatabase object is
>going to construct only when MFC's initialized and destroy when it knows
>that everything is safe.
Point taken about using it as an object in the application's instance.
However, I need to point out that I was getting the crash before arriving
at the object's destructor. That was the case both when the object was
new'ed and when it constructed as a global object.
-----
Richard Morris wrote:
>I found a workaround in my code. The bug seems to have to do with
>the initialization and cleanup sequence of OLE, DAO, and thread management.
> [details removed]
I ultimately got the bug to go away by taking a similar approach of
re-ordering creation of objects. Effectively, we did what the LoadLibrary
solution of the KB article did: kept a portion of code in memory that would
otherwise have gone away.
In my case, I moved the OpenEx call from my main windows OnCreate to my
application's InitInstance. The matching CDatabase::Close call got moved
to my application's destructor. The problem went away.
Geez... was that problem beat on thoroughly enough? 8-) Many thanks to
all for all the good feedback.
| Tim Robinson | Once you let go of your liberty, you |
| http://www.ionet.net/~timtroyr | won't easily get it back. -- Plautus |
-----From: "Andreas Epple"
I have had the same problem. If you use the MS - ODBC drivers, try the
following:
//
// Bugfix for Jet - ODBC
//
VERIFY(LoadLibrary("MSJT3032.DLL"));
I found this hint in the MS knowledge database. You must not unload the
dll, WIN95 or WIN NT is doing this for you.
Bye
Andreas Epple
> >----------
> >From: Tim Robinson[SMTP:timtroyr@ionet.net]
> >Sent: Tuesday, March 11, 1997 2:58 AM
> >To: mfc-l@netcom.com
> >Subject: CDatabase::Close() causing exception
> >
> >Environment: VC++ 4.2b, NT 4.0
> >The Primary Symptom:
> >
> >First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access
> >Violation.
> >
Mike Blaszczak -- mikeblas@nwlink.com
Thursday, March 20, 1997
[Mini-digest: 4 responses]
At 01:22 3/18/97 -0600, Tim Robinson wrote:
>>The underlying driver might be using threads, itself, to get the job done.
>>I don't think ODBC itself ever spawns extra threads, but it might do so,
>>too. But I don't think your conclusion that MFC is taking away something
>>a closing thread in ODBC needs has any merit.
>
>Hmm... just watch the debug window for the creation and exit of the threads
>when you open and close the database objects. I thought it was kinda hard
>to miss.
How does the information quoted from your debug output window make it hard
to miss that MFC is taking away something that a dying ODBC thread needs?
>It's a bug somewhere. If MFC is following the rules, and if the rules are
>wrong, then arguably there is nothing wrong with MFC. The carpet is being
>yanked out from under the code somewhere, though. I'm grateful to MarkG
>for pointing out the KB article. It's the final solution, but read on.
That KB article is a little creepy. It suggests to add a LoadLibrary() on
the JET implemnetation DLL, but never suggests calling FreeLibrary(). That
seems to indicate that ODBC and/or the Access driver and/or JET itself
aren't able to manage their own reference counts correctly, which
sounds like a bug in ODBC and/or JET and/or the Access ODBC driver.
After aplying this fix, does your program exit normally, without getting
stuck in memory? When you close it, it's gone, and PVIEW says it's gone,
too, right? If so, then the bug is squarely in the database API court.
>However, I need to point out that I was getting the crash before arriving
>at the object's destructor. That was the case both when the object was
>new'ed and when it constructed as a global object.
Unfortunately, I didn't know that when I wrote the text you quoted.
> arguably in MFC
MFC won't implement the fix, by the way, because we don't know that you're
planning on using the Access Driver through ODBC, and it would be a waste
to load the DLL if you weren't. We further don't know what version of
DAO you have installed, and if it's still implemented in a file
with that name.
.B ekiM
http://www.nwlink.com/~mikeblas/
These words are my own. I do not speak on behalf of Microsoft.
One is too many and a million is not enough.
-----From: "Dan Kirby"
This is a known bug and has been fixed in a patch which is in the
Knowledgebase (search on MSJT3032.DLL) or better yet, just use the newer
ODBC drivers which come with Office 97 or search for WX1350.EXE in the
Knowledgebase which also includes the newer drivers. Visual C++ 5.0 which
is out has the newest drivers and they don't have the problem either.
--dan
----------
> From: Tim Robinson
> To: mfc-l@netcom.com
> Cc: Richard Morris Mike Blaszczak
"Doug Brubacher"
riddell@kudos.net MarkG@usa.net "GoroKhM1"
> Subject: RE: CDatabase::Close() causing exception
> Date: Monday, March 17, 1997 11:22 PM
>
> [Mini-digest: 2 responses]
>
> I have had some semblance of success with the problem. But before I go
> into my solution, a couple comments on the responses I got.
>
> "GoroKhM1" wrote:
> > a) We found out that we had 'pure' timing problems whenever we
> >encountered these type of problems. All I would have is ??? in my debug
> >window assembler code.
> > b) If you find these symptoms, try adjusting the PageTimeOut settings
> >for your ODBC driver. I don't know what type of database you are using,
> >but looks like you are encountering this in Access (.mdb) files. If not
> >do NOT read ahead.
> > c) A wait before you call DB->Close() would really help the ODBC driver
> >to wind their threads.
> >
> > This may not be a neat soultion but waiting for a few milliseconds
> >before winding the app down did solve the problem for us.
>
> Timing is close to solving the problem. You suggest a pause in the
program
> before shutdown. I'm normally loathe to do something of that nature
unless
> I'm controlling hardware that has such considerations built in. For
> something strictly software that depends on thread interaction, I don't
> think you can ever get a pause to work 100% of the time. However, I was
> about ready to do this with the problem. As it was, I didn't.
>
> -----------
>
> MarkG@usa.net wrote:
>
> >I had similar problem with DAO: _sometimes_ crash on exit.
> >KB Q153897 solved it and may give you an idea.
> >
> >In my case framework unloads MSJT3032.DLL (for DAO) _before_
> >database close.
>
> Thank you for that one. I had rummaged through my MSDN thoroughly to
find
> info on the problem, but to no avail. After I plugged the Q153897 into
the
> search, the January 97 CD came up telling me there was nothing there by
> that number. It was on the Web, but I didn't scan that as thoroughly.
Now
> the mystery is why that's not on my CD. I will, however, take the advice
> of Q135897 in the future unless something in VC5.0 cures it. (See next
note)
>
> ---------
>
> riddell@kudos.net (codeware) wrote:
> >Get the latest version of the compiler. I had the same problem as you
are
> >reporting. This week I received VC version 5 in the mail and after
> >recompiling the error went away.
>
> It's on its way to me. When it arrives, I may test this some more.
>
> -------------
>
> "Doug Brubacher" wrote:
> > I ran into similar problems to what you are seeing and resolved it
by
> > making my database object a pointer and explicitly new-ing and
> > delete-ing it. I think I had to play with the location of the new
and
> > delete but since I do not have the code in front of me I can not
give
> > you a definite answer.
> >
> > I hope this helps, and does not fall into that red-herring category
of
> > solutions.
>
> Regrettably, I didn't mention that the code I posted to the list was not
> how it started out. It started the way you suggested. I switched to a
> global instance to a CDatabase object as a shot in the dark to cure the
> problem. I'm guessing that it was the positioning of the new and delete
> that actually cured your problem.
>
> ------------
>
> Mike Blaszczak wrote:
> >
> >>Mike B. will probably point out that I'm an idiot and I missed some
> >>"obvious" documentation 8-), but idiot or genius, I need to figure this
out.
> >
> >I grepped the archives that I have here at home, and I can't find any
> >point where, in the history of the list, I called someone an idiot. As a
> >result, your comment seems like unjustified defamation. I guess I'm
obliged
> >to give you the benefit of the doubt, though, and try and answer your
> >question anyway.
>
> I never said you DID call someone that and I apologize if I conveyed
that.
> You can certainly call me an idiot if I missed some clearly posted
solution
> to the problem.
>
> >It would really help to see the exact stack trace and hear something
about
> >the state of the pertinent variables when the crash happens. Just
because
> >the trace is shallow doesn't mean it's meaningless.
>
> This is true. I regret that I assumed it was. I didn't note the stack
> position and now have to go back and retrofit my program with the bug to
> get that trace.
>
> >Odds are that you're using the CDatabase object before it's been
initialized,
> >or after it's been shut down. Global objects construct and destruct
with
> >special rules that are described by the C++ language unstandard. The
compiler
> >follows this part of the unstandard very closely. You need to make sure
> >what you're doing falls well within those rules.
> >
> >One rule that could be affecting you is that a global object isn't
guaranteed
> >to be constructed until some code in the module where it's declared
actually
> >runs. Because of this rule, it's possible that you're using your object
> before
> >it's been constructed. Since the C++ unstandard has similar rules for
> >destruction, it's possilbe that you're destroying the object
>
> I really regret not having mentioned that I tried this both as a global
> instance and as a pointer where I explicitly controlled the ctor/dtor
pair
> besides CDatabase::OpenEx and CDatabase::Close(). I guess I had already
> cleared it in my own head that the use of a global instance wasn't the
> problem, so I didn't mention it. My ommission led to confusing both you
> and Doug (above) about the nature of my code.
>
> >>(Richard Morris -- in another post to the list -- seems to have
> >>had the same problem with threading and DAO, so there is
> >>something to this.)
> >
> >You're making quite an invalid jump. DAO is well-documented to not be
> >safe to use across threads, period. If someone is trying to do that,
> >they can expect nothing but trouble. The documentation says:
> >
> > "DAO itself is not multithreaded, so you can't use the
> > MFC DAO classes in multiple threads. Confine your
> > DAO code to a single thread of execution."
> >
> >in at least a couple of places. Since you're not using multiple threads,
> >there's absolutely no connection between the problem Richard Morris saw
> >and the problem you're seeing.
>
> On first blush, I assumed so. I have had two apps with this problem.
The
> first that had the problem was heavily threaded and ODBC has matching
> warnings about threading. (Yes, I've found a way to share the database
> objects between threads.) In retrospect, I'm still a little confused and
> continue to suspect a relationship, but I'll take your word for it.
>
> >>The line of code
> >>in MFC responsible for causing messages [1], [2] and [3] above is in
> >>DBCORE.CPP in CDatabase::Close() and reads:
> >>
> >> AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
> >
> >The underlying driver might be using threads, itself, to get the job
done.
> >I don't think ODBC itself ever spawns extra threads, but it might do so,
> >too. But I don't think your conclusion that MFC is taking away
something
> >a closing thread in ODBC needs has any merit.
>
> Hmm... just watch the debug window for the creation and exit of the
threads
> when you open and close the database objects. I thought it was kinda
hard
> to miss.
>
> >I'm not convinced you've found a bug in MFC simply because I really
don't
> >understand what you're doing with the class. If you've not carefully
> >worked out the lifecycle of your global object, you can get into very
bad
> >trouble. That's not a bug: it's a fact of life for any C++ class.
>
> It's a bug somewhere. If MFC is following the rules, and if the rules
are
> wrong, then arguably there is nothing wrong with MFC. The carpet is
being
> yanked out from under the code somewhere, though. I'm grateful to MarkG
> for pointing out the KB article. It's the final solution, but read on.
>
> >If you want your object to be global, you can skip learning about the
> >scope and lifetime of global objects by adding the CDatabase object to
> >your CWinApp's instance data and accessing it via AfxGetApp(). This is
> >really the safest way to go, since you know that the CDatabase object is
> >going to construct only when MFC's initialized and destroy when it knows
> >that everything is safe.
>
> Point taken about using it as an object in the application's instance.
> However, I need to point out that I was getting the crash before arriving
> at the object's destructor. That was the case both when the object was
> new'ed and when it constructed as a global object.
>
> -----
>
> Richard Morris wrote:
>
> >I found a workaround in my code. The bug seems to have to do with
> >the initialization and cleanup sequence of OLE, DAO, and thread
management.
> > [details removed]
>
> I ultimately got the bug to go away by taking a similar approach of
> re-ordering creation of objects. Effectively, we did what the
LoadLibrary
> solution of the KB article did: kept a portion of code in memory that
would
> otherwise have gone away.
>
> In my case, I moved the OpenEx call from my main windows OnCreate to my
> application's InitInstance. The matching CDatabase::Close call got moved
> to my application's destructor. The problem went away.
>
> Geez... was that problem beat on thoroughly enough? 8-) Many thanks to
> all for all the good feedback.
>
>
> | Tim Robinson | Once you let go of your liberty, you |
> | http://www.ionet.net/~timtroyr | won't easily get it back. -- Plautus |
>
> -----From: "Andreas Epple"
>
> I have had the same problem. If you use the MS - ODBC drivers, try the
> following:
>
> //
> // Bugfix for Jet - ODBC
> //
>
> VERIFY(LoadLibrary("MSJT3032.DLL"));
>
> I found this hint in the MS knowledge database. You must not unload the
> dll, WIN95 or WIN NT is doing this for you.
>
> Bye
> Andreas Epple
>
> > >----------
> > >From: Tim Robinson[SMTP:timtroyr@ionet.net]
> > >Sent: Tuesday, March 11, 1997 2:58 AM
> > >To: mfc-l@netcom.com
> > >Subject: CDatabase::Close() causing exception
> > >
> > >Environment: VC++ 4.2b, NT 4.0
> > >The Primary Symptom:
> > >
> > >First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access
> > >Violation.
> > >
>
-----From: Phil Daley
At 01:22 AM 3/18/97 -0600, Tim Robinson wrote:
>[Mini-digest: 2 responses]
We talked to Microsoft about this and they agree it's a bug. They are investigating a solution.
Phil Daley Relay Technology
http://www.conknet.com/~p_daley
-----From: mfc-l@netcom.com
>From mfc-l Tue Mar 18 01:22:44 0600 1997 remote from netcom.com
Received: from netcom.com by hyd.ap.nic.in; Sat, 22 Mar 1997 09:49 GMT
Received: by majordomo.netcom.com (8.7.5/8.7.3/(NETCOM MLS v1.01)) id RAA22344; Thu, 20 Mar 1997 17:54:56 -0800 (PST)
Message-Id: <3.0.32.19970318012240.00856a60@ionet.net>
X-Sender: timtroyr@ionet.net
X-Mailer: Windows Eudora Pro Version 3.0 (32)
Date: Tue, 18 Mar 1997 01:22:44 -0600
To: netcom.com!mfc-l
From: ionet.net!timtroyr (Tim Robinson)
Subject: RE: CDatabase::Close() causing exception
Cc:
SMTP.ATG-NET.COM!gorokhm1 ("Richard Morris Mike Blaszczak \"Doug Brubacher\" riddell@kudos.net MarkG@usa.net \"GoroKhM1\"")
Mime-Version: 1.0
Sender: owner-mfc-l@majordomo.netcom.com
Errors-To: owner-mfc-l@majordomo.netcom.com
Precedence: bulk
Reply-To: mfc-l@netcom.com
Content-Type: text/plain; charset="us-ascii"
Content-Length: 9832
[Mini-digest: 2 responses]
I have had some semblance of success with the problem. But before I go
into my solution, a couple comments on the responses I got.
"GoroKhM1" wrote:
> a) We found out that we had 'pure' timing problems whenever we
>encountered these type of problems. All I would have is ??? in my debug
>window assembler code.
> b) If you find these symptoms, try adjusting the PageTimeOut settings
>for your ODBC driver. I don't know what type of database you are using,
>but looks like you are encountering this in Access (.mdb) files. If not
>do NOT read ahead.
> c) A wait before you call DB->Close() would really help the ODBC driver
>to wind their threads.
>
> This may not be a neat soultion but waiting for a few milliseconds
>before winding the app down did solve the problem for us.
Timing is close to solving the problem. You suggest a pause in the program
before shutdown. I'm normally loathe to do something of that nature unless
I'm controlling hardware that has such considerations built in. For
something strictly software that depends on thread interaction, I don't
think you can ever get a pause to work 100% of the time. However, I was
about ready to do this with the problem. As it was, I didn't.
-----------
MarkG@usa.net wrote:
>I had similar problem with DAO: _sometimes_ crash on exit.
>KB Q153897 solved it and may give you an idea.
>
>In my case framework unloads MSJT3032.DLL (for DAO) _before_
>database close.
Thank you for that one. I had rummaged through my MSDN thoroughly to find
info on the problem, but to no avail. After I plugged the Q153897 into the
search, the January 97 CD came up telling me there was nothing there by
that number. It was on the Web, but I didn't scan that as thoroughly. Now
the mystery is why that's not on my CD. I will, however, take the advice
of Q135897 in the future unless something in VC5.0 cures it. (See next note)
---------
riddell@kudos.net (codeware) wrote:
>Get the latest version of the compiler. I had the same problem as you are
>reporting. This week I received VC version 5 in the mail and after
>recompiling the error went away.
It's on its way to me. When it arrives, I may test this some more.
-------------
"Doug Brubacher" wrote:
> I ran into similar problems to what you are seeing and resolved it by
> making my database object a pointer and explicitly new-ing and
> delete-ing it. I think I had to play with the location of the new and
> delete but since I do not have the code in front of me I can not give
> you a definite answer.
>
> I hope this helps, and does not fall into that red-herring category of
> solutions.
Regrettably, I didn't mention that the code I posted to the list was not
how it started out. It started the way you suggested. I switched to a
global instance to a CDatabase object as a shot in the dark to cure the
problem. I'm guessing that it was the positioning of the new and delete
that actually cured your problem.
------------
Mike Blaszczak wrote:
>
>>Mike B. will probably point out that I'm an idiot and I missed some
>>"obvious" documentation 8-), but idiot or genius, I need to figure this out.
>
>I grepped the archives that I have here at home, and I can't find any
>point where, in the history of the list, I called someone an idiot. As a
>result, your comment seems like unjustified defamation. I guess I'm obliged
>to give you the benefit of the doubt, though, and try and answer your
>question anyway.
I never said you DID call someone that and I apologize if I conveyed that.
You can certainly call me an idiot if I missed some clearly posted solution
to the problem.
>It would really help to see the exact stack trace and hear something about
>the state of the pertinent variables when the crash happens. Just because
>the trace is shallow doesn't mean it's meaningless.
This is true. I regret that I assumed it was. I didn't note the stack
position and now have to go back and retrofit my program with the bug to
get that trace.
>Odds are that you're using the CDatabase object before it's been initialized,
>or after it's been shut down. Global objects construct and destruct with
>special rules that are described by the C++ language unstandard. The compiler
>follows this part of the unstandard very closely. You need to make sure
>what you're doing falls well within those rules.
>
>One rule that could be affecting you is that a global object isn't guaranteed
>to be constructed until some code in the module where it's declared actually
>runs. Because of this rule, it's possible that you're using your object
before
>it's been constructed. Since the C++ unstandard has similar rules for
>destruction, it's possilbe that you're destroying the object
I really regret not having mentioned that I tried this both as a global
instance and as a pointer where I explicitly controlled the ctor/dtor pair
besides CDatabase::OpenEx and CDatabase::Close(). I guess I had already
cleared it in my own head that the use of a global instance wasn't the
problem, so I didn't mention it. My ommission led to confusing both you
and Doug (above) about the nature of my code.
>>(Richard Morris -- in another post to the list -- seems to have
>>had the same problem with threading and DAO, so there is
>>something to this.)
>
>You're making quite an invalid jump. DAO is well-documented to not be
>safe to use across threads, period. If someone is trying to do that,
>they can expect nothing but trouble. The documentation says:
>
> "DAO itself is not multithreaded, so you can't use the
> MFC DAO classes in multiple threads. Confine your
> DAO code to a single thread of execution."
>
>in at least a couple of places. Since you're not using multiple threads,
>there's absolutely no connection between the problem Richard Morris saw
>and the problem you're seeing.
On first blush, I assumed so. I have had two apps with this problem. The
first that had the problem was heavily threaded and ODBC has matching
warnings about threading. (Yes, I've found a way to share the database
objects between threads.) In retrospect, I'm still a little confused and
continue to suspect a relationship, but I'll take your word for it.
>>The line of code
>>in MFC responsible for causing messages [1], [2] and [3] above is in
>>DBCORE.CPP in CDatabase::Close() and reads:
>>
>> AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
>
>The underlying driver might be using threads, itself, to get the job done.
>I don't think ODBC itself ever spawns extra threads, but it might do so,
>too. But I don't think your conclusion that MFC is taking away something
>a closing thread in ODBC needs has any merit.
Hmm... just watch the debug window for the creation and exit of the threads
when you open and close the database objects. I thought it was kinda hard
to miss.
>I'm not convinced you've found a bug in MFC simply because I really don't
>understand what you're doing with the class. If you've not carefully
>worked out the lifecycle of your global object, you can get into very bad
>trouble. That's not a bug: it's a fact of life for any C++ class.
It's a bug somewhere. If MFC is following the rules, and if the rules are
wrong, then arguably there is nothing wrong with MFC. The carpet is being
yanked out from under the code somewhere, though. I'm grateful to MarkG
for pointing out the KB article. It's the final solution, but read on.
>If you want your object to be global, you can skip learning about the
>scope and lifetime of global objects by adding the CDatabase object to
>your CWinApp's instance data and accessing it via AfxGetApp(). This is
>really the safest way to go, since you know that the CDatabase object is
>going to construct only when MFC's initialized and destroy when it knows
>that everything is safe.
Point taken about using it as an object in the application's instance.
However, I need to point out that I was getting the crash before arriving
at the object's destructor. That was the case both when the object was
new'ed and when it constructed as a global object.
-----
Richard Morris wrote:
>I found a workaround in my code. The bug seems to have to do with
>the initialization and cleanup sequence of OLE, DAO, and thread management.
> [details removed]
I ultimately got the bug to go away by taking a similar approach of
re-ordering creation of objects. Effectively, we did what the LoadLibrary
solution of the KB article did: kept a portion of code in memory that would
otherwise have gone away.
In my case, I moved the OpenEx call from my main windows OnCreate to my
application's InitInstance. The matching CDatabase::Close call got moved
to my application's destructor. The problem went away.
Geez... was that problem beat on thoroughly enough? 8-) Many thanks to
all for all the good feedback.
| Tim Robinson | Once you let go of your liberty, you |
| http://www.ionet.net/~timtroyr | won't easily get it back. -- Plautus |
-----From: "Andreas Epple"
I have had the same problem. If you use the MS - ODBC drivers, try the
following:
//
// Bugfix for Jet - ODBC
//
VERIFY(LoadLibrary("MSJT3032.DLL"));
I found this hint in the MS knowledge database. You must not unload the
dll, WIN95 or WIN NT is doing this for you.
Bye
Andreas Epple
> >----------
> >From: Tim Robinson[SMTP:timtroyr@ionet.net]
> >Sent: Tuesday, March 11, 1997 2:58 AM
> >To: mfc-l@netcom.com
> >Subject: CDatabase::Close() causing exception
> >
> >Environment: VC++ 4.2b, NT 4.0
> >The Primary Symptom:
> >
> >First-chance exception in MyApp.exe (NTDLL.DLL): 0xC0000005: Access
> >Violation.
> >
Become an MFC-L member
| Вернуться в корень Архива
|