Article

From:
To:
All
Subject:
How OLEInitialize + Threads work togetter
Newsgroup:
embarcadero.public.cppbuilder.nativeapi

How OLEInitialize + Threads work togetter

Hi,

We have the following problem.
Every once in a while our application get an EAccessViolation (typically once a day). We can___t reproduce it at our development site but at the site of a customer this happens. We have made some fixed and put the new executable at the site of our customer.

We haven___t seen any EAccesViolations at the customer site for last 12 hours.

But when we run our code in Builder the Debugger stops on exceptions parsing the Query-Text at the moment we have assigned the new query text. In more detail the exception occurs when ADO is parsing the parameters. The exception message is: ___Project MMIServer.exe raised exception class EOleException with message ___The arguments are of the wrong type, are outside the allowed range or are conflicting with each other______ (Please note that the part of the message between the single quotes is translated from Dutc h, so the exact English message could differ slightly). Those exceptions are blocked inside the VCL because outside the Builder we don___t run into these exceptions We know that for sure because we log all exceptions we get in a Query.

Query example:
{Code}
bool daoDiagnosticDatabaseManager::CriticalMessagePresent()
{
  static TLoggingMethodId __id(__FILE__,__FUNC__);
  LogEnter;

  bool lRetval = false;
  if (mConMgr->Connected)   {     clsMobaQuery* lQuery = new clsMobaQuery(NULL);     try     {       try       {         lQuery->Connection = mConMgr->Connection;
lQuery->SQL->Text = "SELECT COUNT(8) AS NrCritical FROM Tbl_Messages WHERE Critical = :lCritical AND Acknowledge = :lAcknowledge ";
        SetParamValue("lAcknowledge", lQuery, false);         SetParamValue("lCritical", lQuery, true);
        lQuery->Prepared = true;

        try         {           lQuery->Open();           lRetval = (lQuery->FieldByName("NrCritical")->AsInteger > 0);           lQuery->Close();         }         catch(Exception &E)         {           mConMgr->LogSQLException(__id,lQuery,E);           throw;         }       }       catch(Exception &E)       {         mConMgr->LogSQLException(__id,lQuery,E);         throw;       }     }     __finally     {       // Clean up       if(lQuery)       {         if (lQuery->Active)         {           lQuery->Close();         }         delete lQuery;         lQuery=NULL;       }     }   }   else   {     Log->ErrorParameter(__id, MSG_DB_NOT_CONNECTED.c_str());   }

  LogLeave;   return lRetval; } //---------------------------------------------------------------------------- {Code}
What we see is that after about 300 to 400 calls of this code we get the earlier described OleException.

We use OLEInitialize(); before entering this code. And OleUninitilize() after leaving this code.

This part of code is in a stand alone SoapWebserver. Based on the THTTPWebBrokerBridge It has no FWebBrokerBridge->Scheduler set to it explicitly. As a result a thread is created for each request that is received. Besides the threads that are used to execute the Soap requests there is one background-thread that runs every second. Each thread performs the OleInitialise and OleUninitialise. Also each thread performs a lock to prevent two threads from concurrently accessing all the code associated to ADO and files.

After using the OleInitialize We know that we run in a SingleThreadedAppartement.

I have read that when parsing objects in between threads we need to use Marshaling. Although we do not exactly know what that means we want to shortly describe how or code works: ___ The HTTPWebBrokerBridge receives the SOAP request and determines that webservice-function that must be called
___	For each request a separate thread is created.
___	The webservice-function is called as the Run method of the created thread.
___ Inside the webservice function the first thing that is performed is calling a method of a singleton object that performs the role of a fa__ade.
___	Inside each singleton method first the lock is acquired (lock->Enter).
___	After the lock is acquired OleInitialise is called.
___ Then a function performing an ADO request is called. This ADO request is implemented inside another singleton object.
___	When the ADO request has finished, OleUninitialise is called.
___	At the end the lock is release (lock->Leave).

Please note that all singleton objects are created at startup of the application and destroyed at termination of the application. This is done using an initialisation thread that performs a method to ___factory___ all relevant objects.

We found this information on MSDN but are not sure what the impact is on how we wrote our application:

{quote}
// from Microsoft MSDN Site http://msdn.microsoft.com/en-us/library/ms680112(VS.85).aspx

All calls to an object must be made on its thread (within its apartment). It is forbidden to call an object directly from another thread; using objects in this free-threaded manner could cause problems for applications. The implication of this rule is that all pointers to objects must be marshaled when passed between apartments. COM provides the following two functions for this purpose: ___ CoMarshalInterThreadInterfaceInStream marshals an interface into a stream object that is returned to the caller. ___ CoGetInterfaceAndReleaseStream unmarshals an interface pointer from a stream object and releases it. These functions wrap calls to CoMarshalInterface and CoUnmarshalInterface functions, which require the use of the MSHCTX_INPROC flag.
{quote}


We need feedback on whether or not we create our objects correctly. Any other feedback on errors/improvements are more than welcome.

Best regards,

jvdn
FYI: Phrase searches are enclosed in either single or double quotes
 
 
Originally created by
Tamarack Associates
Fri, 29 Mar 2024 05:02:58 UTC
Copyright © 2009-2024
HREF Tools Corp.