Esecuzione asincrona (metodo di notifica)
ODBC consente l'esecuzione asincrona di operazioni di connessione e istruzioni. Un thread dell'applicazione può chiamare una funzione ODBC in modalità asincrona e la funzione può restituire un risultato prima che l'operazione sia completata, consentendo al thread dell'applicazione di eseguire altre attività. Nel SDK di Windows 7, per le operazioni asincrone di dichiarazione o connessione, un'applicazione verificava il completamento dell'operazione asincrona usando il metodo di polling. Per altre informazioni, vedere Esecuzione Asincrona (Metodo di Polling). A partire da Windows 8 SDK, è possibile determinare che un'operazione asincrona è stata completata usando il metodo di notifica.
Nel metodo di polling, le applicazioni devono chiamare la funzione asincrona ogni volta che vogliono lo stato dell'operazione. Il metodo di notifica è simile al callback e all'attesa in ADO.NET. ODBC usa tuttavia eventi Win32 come oggetto di notifica.
Impossibile utilizzare contemporaneamente la libreria di cursori ODBC e la notifica asincrona ODBC. L'impostazione di entrambi gli attributi restituirà un errore con SQLSTATE S1119 (la libreria di cursori e la notifica asincrona non possono essere abilitati contemporaneamente).
Per informazioni destinate agli sviluppatori di driver, vedere Notifica del completamento di funzioni asincrone.
Nota
Il metodo di notifica non è supportato con la libreria di cursori. Un'applicazione riceverà un messaggio di errore se tenta di abilitare la libreria di cursori tramite SQLSetConnectAttr, quando il metodo di notifica è abilitato.
Panoramica
Quando una funzione ODBC viene chiamata in modalità asincrona, il controllo viene restituito all'applicazione chiamante immediatamente con il codice restituito SQL_STILL_EXECUTING. L'applicazione deve eseguire ripetutamente il polling della funzione fino a quando non restituisce un valore diverso da SQL_STILL_EXECUTING. Il ciclo di polling aumenta l'utilizzo della CPU, causando prestazioni scarse in molti scenari asincroni.
Ogni volta che viene usato il modello di notifica, il modello di polling è disabilitato. Le applicazioni non devono chiamare di nuovo la funzione originale. Chiamare funzione SQLCompleteAsync per completare l'operazione asincrona. Se un'applicazione chiama nuovamente la funzione originale prima del completamento dell'operazione asincrona, la chiamata restituirà SQL_ERROR con SQLSTATE IM017 (il polling è disabilitato in modalità di notifica asincrona).
Quando si usa il modello di notifica, l'applicazione può chiamare SQLCancel o SQLCancelHandle per annullare un'istruzione o un'operazione di connessione. Se la richiesta di annullamento ha esito positivo, ODBC restituirà SQL_SUCCESS. Questo messaggio non indica che la funzione è stata effettivamente annullata; indica che la richiesta di annullamento è stata elaborata. Se la funzione viene effettivamente annullata è dipendente dal driver e dipendente dall'origine dati. Quando un'operazione viene annullata, il Gestore dei driver segnalerà comunque l'evento. Il gestore dei driver restituisce SQL_ERROR nella variabile di codice restituito e lo stato è SQLSTATE HY008 (Operazione annullata) per indicare che l'annullamento ha esito positivo. Se la funzione ha completato la normale elaborazione, il Gestione driver restituisce SQL_SUCCESS o SQL_SUCCESS_WITH_INFO.
Comportamento di livello inferiore
La versione del Gestore driver ODBC che supporta questa notifica al termine è ODBC 3.81.
Versione ODBC dell'applicazione | Versione del Gestore driver | Versione driver | Comportamento |
---|---|---|---|
Nuova applicazione di qualsiasi versione ODBC | ODBC 3.81 | ODBC 3.80 Driver | L'applicazione può usare questa funzionalità se il driver supporta questa funzionalità. In caso contrario, il Gestore dei driver darà un errore. |
Nuova applicazione di qualsiasi versione ODBC | ODBC 3.81 | Pre-ODBC 3.80 Driver | Il gestore dei driver genererà un errore se il driver non supporta questa funzionalità. |
Nuova applicazione di qualsiasi versione ODBC | Pre-ODBC 3.81 | Qualunque | Quando l'applicazione utilizza questa funzionalità, un vecchio Driver Manager considererà i nuovi attributi come specifici del driver, e il driver dovrebbe generare un errore. Un nuovo Driver Manager non passerà questi attributi al driver. |
Un'applicazione deve controllare la versione di Driver Manager prima di usare questa funzionalità. In caso contrario, se un driver scritto in modo errato non produce errori e la versione del Gestore driver è precedente a ODBC 3.81, il comportamento non è definito.
Casi d'uso
Questa sezione illustra i casi d'uso per l'esecuzione asincrona e il meccanismo di polling.
Integrare dati da più origini ODBC
Un'applicazione di integrazione dati recupera in modo asincrono i dati da più origini dati. Alcuni dati provengono da origini dati remote e alcuni dati provengono da file locali. L'applicazione non può continuare fino al completamento delle operazioni asincrone.
Anziché eseguire ripetutamente il polling di un'operazione per determinare se è completa, l'applicazione può creare un oggetto evento e associarlo a un handle di connessione ODBC o a un handle di istruzione ODBC. L'applicazione chiama quindi le API di sincronizzazione del sistema operativo per attendere un oggetto evento o molti oggetti evento (sia eventi ODBC che altri eventi di Windows). ODBC segnalerà l'oggetto evento al termine dell'operazione asincrona ODBC corrispondente.
In Windows, gli oggetti evento Win32 verranno usati e che forniranno all'utente un modello di programmazione unificato. I gestori driver in altre piattaforme possono usare l'implementazione dell'oggetto evento specifica per tali piattaforme.
L'esempio di codice seguente dimostra l'uso della notifica asincrona di connessione e dichiarazione:
// This function opens NUMBER_OPERATIONS connections and executes one query on statement of each connection.
// Asynchronous Notification is used
#define NUMBER_OPERATIONS 5
int AsyncNotificationSample(void)
{
RETCODE rc;
SQLHENV hEnv = NULL;
SQLHDBC arhDbc[NUMBER_OPERATIONS] = {NULL};
SQLHSTMT arhStmt[NUMBER_OPERATIONS] = {NULL};
HANDLE arhDBCEvent[NUMBER_OPERATIONS] = {NULL};
RETCODE arrcDBC[NUMBER_OPERATIONS] = {0};
HANDLE arhSTMTEvent[NUMBER_OPERATIONS] = {NULL};
RETCODE arrcSTMT[NUMBER_OPERATIONS] = {0};
rc = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &hEnv);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
rc = SQLSetEnvAttr(hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3_80,
SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
// Connection operations begin here
// Alloc NUMBER_OPERATIONS connection handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &arhDbc[i]);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Enable DBC Async on all connection handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetConnectAttr(arhDbc[i], SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Application must create event objects
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
arhDBCEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, initial state is not-signaled
if (!arhDBCEvent[i]) goto Cleanup;
}
// Enable notification on all connection handles
// Event
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetConnectAttr(arhDbc[i], SQL_ATTR_ASYNC_DBC_EVENT, arhDBCEvent[i], SQL_IS_POINTER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Initiate connect establishing
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLDriverConnect(arhDbc[i], NULL, (SQLTCHAR*)TEXT("Driver={ODBC Driver 11 for SQL Server};SERVER=dp-srv-sql2k;DATABASE=pubs;UID=sa;PWD=XYZ;"), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhDBCEvent, TRUE, INFINITE); // Wait All
// Complete connect API calls
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_DBC, arhDbc[i], & arrcDBC[i]);
}
BOOL fFail = FALSE; // Whether some connection opening fails.
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcDBC[i]) )
fFail = TRUE;
}
// If some SQLDriverConnect() fail, clean up.
if (fFail)
{
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (SQL_SUCCEEDED(arrcDBC[i]) )
{
SQLDisconnect(arhDbc[i]); // This is also async
}
else
{
SetEvent(arhDBCEvent[i]); // Previous SQLDriverConnect() failed. No need to call SQLDisconnect().
}
}
WaitForMultipleObjects(NUMBER_OPERATIONS, arhDBCEvent, TRUE, INFINITE);
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (SQL_SUCCEEDED(arrcDBC[i]) )
{
SQLCompleteAsync(SQL_HANDLE_DBC, arhDbc[i], &arrcDBC[i]);; // To Complete
}
}
goto Cleanup;
}
// Statement Operations begin here
// Alloc statement handle
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLAllocHandle(SQL_HANDLE_STMT, arhDbc[i], &arhStmt[i]);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Enable STMT Async on all statement handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLSetStmtAttr(arhStmt[i], SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Create event objects
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
arhSTMTEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, initial state is not-signaled
if (!arhSTMTEvent[i]) goto Cleanup;
}
// Enable notification on all statement handles
// Event
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetStmtAttr(arhStmt[i], SQL_ATTR_ASYNC_STMT_EVENT, arhSTMTEvent[i], SQL_IS_POINTER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Initiate SQLExecDirect() calls
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLExecDirect(arhStmt[i], (SQLTCHAR*)TEXT("select au_lname, au_fname from authors"), SQL_NTS);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhSTMTEvent, TRUE, INFINITE); // Wait All
// Now, call SQLCompleteAsync to complete the operation and get return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_STMT, arhStmt[i], &arrcSTMT[i]);
}
// Check return values
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcSTMT[i]) ) goto Cleanup;
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
//Do some binding jobs here, set SQL_ATTR_ROW_ARRAY_SIZE
}
// Now, initiate fetching
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLFetch(arhStmt[i]);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhSTMTEvent, TRUE, INFINITE);
// Now, to complete the operations and get return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_STMT, arhStmt[i], &arrcSTMT[i]);
}
// Check return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcSTMT[i]) ) goto Cleanup;
}
// USE fetched data here!!
Cleanup:
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhStmt[NUMBER_OPERATIONS])
{
SQLFreeHandle(SQL_HANDLE_STMT, arhStmt[i]);
arhStmt[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhSTMTEvent[i])
{
CloseHandle(arhSTMTEvent[i]);
arhSTMTEvent[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhDbc[i])
{
SQLFreeHandle(SQL_HANDLE_DBC, arhDbc[i]);
arhDbc[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhDBCEvent[i])
{
CloseHandle(arhDBCEvent[i]);
arhDBCEvent[i] = NULL;
}
}
if (hEnv)
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
hEnv = NULL;
}
return 0;
}
Verificare se un driver supporta la notifica asincrona
Un'applicazione ODBC può determinare se un driver ODBC supporta la notifica asincrona chiamando SQLGetInfo. Il Gestore driver ODBC chiamerà di conseguenza il SQLGetInfo del driver con SQL_ASYNC_NOTIFICATION.
SQLUINTEGER InfoValue;
SQLLEN cbInfoLength;
SQLRETURN retcode;
retcode = SQLGetInfo (hDbc,
SQL_ASYNC_NOTIFICATION,
&InfoValue,
sizeof(InfoValue),
NULL);
if (SQL_SUCCEEDED(retcode))
{
if (SQL_ASYNC_NOTIFICATION_CAPABLE == InfoValue)
{
// The driver supports asynchronous notification
}
else if (SQL_ASYNC_NOTIFICATION_NOT_CAPABLE == InfoValue)
{
// The driver does not support asynchronous notification
}
}
Associazione di un handle di eventi Win32 a un handle ODBC
Le applicazioni sono responsabili della creazione di oggetti evento Win32 usando le funzioni Win32 corrispondenti. Un'applicazione può associare un handle di eventi Win32 a un handle di connessione ODBC o a un handle di istruzione ODBC.
Gli attributi di connessione SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE e SQL_ATTR_ASYNC_DBC_EVENT determinano se ODBC viene eseguito in modalità asincrona e se abilita la modalità di notifica per un handle di connessione. Gli attributi per le istruzioni SQL_ATTR_ASYNC_ENABLE e SQL_ATTR_ASYNC_STMT_EVENT determinano se ODBC viene eseguito in modalità asincrona e se ODBC abilita la modalità di notifica per un'istruzione handle.
SQL_ATTR_ASYNC_ENABLE o SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE | SQL_ATTR_ASYNC_STMT_EVENT o SQL_ATTR_ASYNC_DBC_EVENT | Modalità |
---|---|---|
Abilitare | non nullo | Notifica asincrona |
Abilitare | nullo | Polling asincrono |
Disabilitare | qualunque | Sincrono |
Un'applicazione può disabilitare in modo temporale la modalità operativa asincrona. ODBC ignora i valori di SQL_ATTR_ASYNC_DBC_EVENT se l'operazione asincrona a livello di connessione è disabilitata. ODBC ignora i valori di SQL_ATTR_ASYNC_STMT_EVENT se l'operazione asincrona a livello di istruzione è disabilitata.
Chiamata sincrona di SQLSetStmtAttr e SQLSetConnectAttr
SQLSetConnectAttr supporta operazioni asincrone, ma la chiamata di SQLSetConnectAttr per impostare SQL_ATTR_ASYNC_DBC_EVENT è sempre sincrona.
SQLSetStmtAttr non supporta l'esecuzione asincrona.
Scenario di interruzione per errore
Quando SQLSetConnectAttr viene chiamato prima di stabilire una connessione, il Gestore dei driver non è in grado di determinare quale driver usare. Pertanto, Gestione driver restituisce l'esito positivo per SQLSetConnectAttr, ma l'attributo potrebbe non essere pronto per l'impostazione nel driver. Gestione driver imposta questi attributi quando l'applicazione chiama una funzione di connessione. Il Gestore driver potrebbe generare un errore perché il driver non supporta le operazioni asincrone.
Ereditarietà degli attributi di connessione
In genere, le istruzioni di una connessione erediteranno gli attributi di connessione. Tuttavia, l'attributo SQL_ATTR_ASYNC_DBC_EVENT non è ereditabile e influisce solo sulle operazioni di connessione.
Per associare un handle di evento a un handle di connessione ODBC, un'applicazione ODBC chiama l'API ODBC SQLSetConnectAttr e specifica SQL_ATTR_ASYNC_DBC_EVENT come attributo e l'handle di evento come valore dell'attributo. Il nuovo attributo ODBC SQL_ATTR_ASYNC_DBC_EVENT è di tipo SQL_IS_POINTER.
HANDLE hEvent;
hEvent = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event
FALSE, // initial state is non-signaled
NULL // no name
);
In genere, le applicazioni creano oggetti evento di reimpostazione automatica. ODBC non reimposta l'oggetto evento. Le applicazioni devono assicurarsi che l'oggetto non sia in stato segnalato prima di chiamare qualsiasi funzione ODBC asincrona.
SQLRETURN retcode;
retcode = SQLSetConnectAttr ( hDBC,
SQL_ATTR_ASYNC_DBC_EVENT, // Attribute name
(SQLPOINTER) hEvent, // Win32 Event handle
SQL_IS_POINTER); // Length Indicator
SQL_ATTR_ASYNC_DBC_EVENT è un attributo del solo Gestore driver che non verrà impostato nel driver.
Il valore predefinito di SQL_ATTR_ASYNC_DBC_EVENT è NULL. Se il driver non supporta la notifica asincrona, ottenere o impostare SQL_ATTR_ASYNC_DBC_EVENT restituirà SQL_ERROR con SQLSTATE HY092 (identificatore di attributo/opzione non valido).
Se l'ultimo valore SQL_ATTR_ASYNC_DBC_EVENT impostato su un handle di connessione ODBC non è NULL e l'applicazione ha abilitato la modalità asincrona impostando l'attributo SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE con SQL_ASYNC_DBC_ENABLE_ON, la chiamata a qualsiasi funzione di connessione ODBC che supporta la modalità asincrona riceverà una notifica di completamento. Se l'ultimo valore SQL_ATTR_ASYNC_DBC_EVENT impostato su un handle di connessione ODBC è NULL, ODBC non invierà alcuna notifica all'applicazione, indipendentemente dal fatto che la modalità asincrona sia abilitata.
Un'applicazione può impostare SQL_ATTR_ASYNC_DBC_EVENT prima o dopo l'impostazione dell'attributo SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE.
Le applicazioni possono impostare l'attributo SQL_ATTR_ASYNC_DBC_EVENT su un handle di connessione ODBC prima di chiamare una funzione di connessione (SQLConnect, SQLBrowseConnecto SQLDriverConnect). Poiché Gestione driver ODBC non conosce il driver ODBC che verrà usato dall'applicazione, restituirà SQL_SUCCESS. Quando l'applicazione chiama una funzione di connessione, il Gestore driver ODBC verificherà se il driver supporta la notifica asincrona. Se il driver non supporta la notifica asincrona, il Gestore dei driver ODBC restituirà SQL_ERROR con SQLSTATE S1_118 (Il Driver non supporta la notifica asincrona). Se il driver supporta la notifica asincrona, il Gestione driver ODBC chiamerà il driver e imposterà gli attributi corrispondenti SQL_ATTR_ASYNC_DBC_NOTIFICATION_CALLBACK e SQL_ATTR_ASYNC_DBC_NOTIFICATION_CONTEXT.
Analogamente, un'applicazione chiama SQLSetStmtAttr in un handle di istruzione ODBC e specifica l'attributo SQL_ATTR_ASYNC_STMT_EVENT per abilitare o disabilitare la notifica asincrona a livello di istruzione. Poiché una funzione di istruzione viene sempre chiamata dopo aver stabilito la connessione, SQLSetStmtAttr restituirà immediatamente SQL_ERROR con SQLSTATE S1_118 (il driver non supporta la notifica asincrona) se il driver corrispondente non supporta le operazioni asincrone o supporta l'operazione asincrona, ma non la notifica asincrona.
SQLRETURN retcode;
retcode = SQLSetStmtAttr ( hSTMT,
SQL_ATTR_ASYNC_STMT_EVENT, // Attribute name
(SQLPOINTER) hEvent, // Win32 Event handle
SQL_IS_POINTER); // length Indicator
SQL_ATTR_ASYNC_STMT_EVENT, che può essere impostato su NULL, è un attributo esclusivo per il Gestore dei driver che non verrà impostato nel driver.
Il valore predefinito di SQL_ATTR_ASYNC_STMT_EVENT è NULL. Se il driver non supporta la notifica asincrona, il recupero o l'impostazione dell'attributo SQL_ATTR_ASYNC_ STMT_EVENT restituirà SQL_ERROR con SQLSTATE HY092 (identificatore di attributo/opzione non valido).
Un'applicazione non deve associare lo stesso handle di evento a più handle ODBC. In caso contrario, una notifica andrà persa se due chiamate di funzione ODBC asincrone vengono completate su due handle che condividono lo stesso handle di eventi. Per evitare che un handle di istruzione erediti lo stesso handle di eventi dall'handle di connessione, ODBC restituisce SQL_ERROR con SQLSTATE IM016 (Impossibile impostare l'attributo dell'istruzione nell'handle di connessione) se un'applicazione imposta SQL_ATTR_ASYNC_STMT_EVENT su un handle di connessione.
Chiamare funzioni ODBC asincrone
Dopo aver abilitato la notifica asincrona e avviato un'operazione asincrona, l'applicazione può chiamare qualsiasi funzione ODBC. Se la funzione appartiene al set di funzioni che supportano l'operazione asincrona, l'applicazione riceverà una notifica di completamento al termine dell'operazione, indipendentemente dal fatto che la funzione non sia riuscita o abbia esito positivo. L'unica eccezione è che l'applicazione chiama una funzione ODBC con un handle di connessione o istruzione non valido. In questo caso, ODBC non otterrà l'handle dell'evento e lo imposta sullo stato segnalato.
L'applicazione deve assicurarsi che l'oggetto evento associato si trova in uno stato non segnalato prima di avviare un'operazione asincrona sull'handle ODBC corrispondente. ODBC non reimposta l'oggetto evento.
Ricezione di notifiche da ODBC
Un thread di applicazione può chiamare WaitForSingleObject per attendere un handle di evento o chiamare WaitForMultipleObjects per attendere una matrice di handle di eventi e restare sospeso fino a quando uno o tutti gli oggetti evento vengono segnalati o fino al trascorrere dell'intervallo di timeout.
DWORD dwStatus = WaitForSingleObject(
hEvent, // The event associated with the ODBC handle
5000 // timeout is 5000 millisecond
);
If (dwStatus == WAIT_TIMEOUT)
{
// time-out interval elapsed before all the events are signaled.
}
Else
{
// Call the corresponding Asynchronous ODBC API to complete all processing and retrieve the return code.
}