====== "Spontan" auf eine dynamische Library zugreifen ======
Ein Beispiel, wie man auf eine dynamische Library dynamisch zugreift - ohne sie vorher durch den Linker in das Programm eingebunden zu haben. Die DLL/SharedObject wird zur Laufzeit dynamisch geladen, genutzt und dann wieder aus dem Speicher entladen.
In diesem Beispiel greife ich auf den DB2-Client zu und lese dort DB-Aliase aus, die in dem DB2-Client als Datenquelle konfiguriert sind.\\ Die Informationen zu den dafür benötigten CALLs stammen von IBM:\\ [[https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/odbc/src/tpc/db2z_fndatasources.html]]
check_db2.h
#ifdef OS_WINDOWS
#define DB2CLI_DLL_FILENAME "db2cli64.dll"
#else
#define DB2CLI_DLL_FILENAME "libdb2.so"
#endif
#ifdef OS_WINDOWS
typedef HINSTANCE H_DLL_INSTANCE;
#else
typedef void* H_DLL_INSTANCE;
#endif
// Ich packe die Funktionen für die Untersuchung der DB2 Konfiguration beispielsweise in die Klasse "check_db2".
class check_db2:
{
public:
check_db2(); // Constructor
void check_db2client();
string get_image_path(H_DLL_INSTANCE hinstance);
// Hier nur als Beispiel:
private:
#ifdef OS_WINDOWS
bool check_db2_registry();
void check_odbc();
#else
bool check_db2_environment();
void check_db2_calls();
#endif
};
check_db2.cpp
#include "check_db2.h"
#include "sqlcli1.h"
void check_db2::check_db2client()
{
SQLRETURN rc = 0;
SQLHENV henv = NULL;
SQLCHAR source[SQL_MAX_DSN_LENGTH + 1] = "", description[255] = "";
SQLSMALLINT buffl = 0, desl = 0;
// Die DLL wird "on the fly" angesprochen.
H_DLL_INSTANCE db2dll_hinstance = NULL;
// Zuerst brauchen wir 4x "Anker"-Variablen für die 4x zukünftigen CALLs:
typedef SQLRETURN (*DB2DLL_CALL_T_SQLAllocHandle)(SQLSMALLINT, SQLHANDLE, SQLHANDLE *);
DB2DLL_CALL_T_SQLAllocHandle db2dll_call_SQLAllocHandle = NULL;
typedef SQLRETURN(*DB2DLL_CALL_T_SQLSetEnvAttr)(SQLHENV, SQLINTEGER, SQLPOINTER, SQLINTEGER);
DB2DLL_CALL_T_SQLSetEnvAttr db2dll_call_SQLSetEnvAttr = NULL;
typedef SQLRETURN(*DB2DLL_CALL_T_SQLDataSources)(SQLHENV, SQLUSMALLINT, SQLCHAR FAR *, SQLSMALLINT, SQLSMALLINT FAR *, SQLCHAR FAR *, SQLSMALLINT, SQLSMALLINT FAR *);
DB2DLL_CALL_T_SQLDataSources db2dll_call_SQLDataSources = NULL;
typedef SQLRETURN(*DB2DLL_CALL_T_SQLFreeHandle)(SQLSMALLINT, SQLHANDLE);
DB2DLL_CALL_T_SQLFreeHandle db2dll_call_SQLFreeHandle = NULL;
output::println("Info zum DB2-Client:");
// Prüfen, ob die DLL bereits geladen ist:
#ifdef OS_WINDOWS
db2dll_hinstance = GetModuleHandle(DB2CLI_DLL_FILENAME);
#else
// Bei RTLD_LAZY werden Funktionen erst bei Bedarf geladen, bei RTLD_NOW sofort.
// Bei RTLD_NOLOAD testet man, ob die DLL bereits geladen ist oder nicht.
db2dll_hinstance = dlopen(DB2CLI_DLL_FILENAME, RTLD_NOLOAD);
#endif
string msg;
string dll_path;
if (db2dll_hinstance != NULL) // Bereits geladen!
{
msg = DB2CLI_DLL_FILENAME " ist bereits geladen";
}
else
{
// Versuch, die DLL zu laden:
#ifdef OS_WINDOWS
db2dll_hinstance = LoadLibrary(DB2CLI_DLL_FILENAME);
#else
db2dll_hinstance = dlopen(DB2CLI_DLL_FILENAME, RTLD_LAZY);
#endif
if (db2dll_hinstance == NULL) // Der Lade-Versuch war nicht erfolgreich.
{
#ifdef OS_WINDOWS
msg = DB2CLI_DLL_FILENAME " konnte nicht dynamisch geladen werden; GetLastError()=" + std::to_string(GetLastError());
#else
string dlerror_msg = dlerror();
msg = DB2CLI_DLL_FILENAME " konnte nicht dynamisch geladen werden; dlerror()=[" + dlerror_msg + "]";
#endif
output::println(msg);
}
else // Der Lade-Versuch war erfolgreich.
{
msg = DB2CLI_DLL_FILENAME " wurde dynamisch geladen:";
dll_path = get_image_path(db2dll_hinstance); // Den Pfad zur geladenen DLL merken.
output::println(msg).println(dll_path);
}
#ifdef OS_UNIX
dlerror(); // reset errors
#endif
if (db2dll_hinstance == NULL) // Bei Fehler gleich aussteigen.
return;
}
// Die 4x "Anker"-Variablen an die 4x CALLs aus der DLL binden:
#ifdef OS_WINDOWS
db2dll_call_SQLAllocHandle = (DB2DLL_CALL_T_SQLAllocHandle) GetProcAddress(db2dll_hinstance, "SQLAllocHandle");
db2dll_call_SQLSetEnvAttr = (DB2DLL_CALL_T_SQLSetEnvAttr) GetProcAddress(db2dll_hinstance, "SQLSetEnvAttr");
db2dll_call_SQLDataSources = (DB2DLL_CALL_T_SQLDataSources) GetProcAddress(db2dll_hinstance, "SQLDataSources");
db2dll_call_SQLFreeHandle = (DB2DLL_CALL_T_SQLFreeHandle) GetProcAddress(db2dll_hinstance, "SQLFreeHandle");
#else
dlerror();
db2dll_call_SQLAllocHandle = (DB2DLL_CALL_T_SQLAllocHandle) dlsym(db2dll_hinstance, "SQLAllocHandle");
db2dll_call_SQLSetEnvAttr = (DB2DLL_CALL_T_SQLSetEnvAttr) dlsym(db2dll_hinstance, "SQLSetEnvAttr");
db2dll_call_SQLDataSources = (DB2DLL_CALL_T_SQLDataSources) dlsym(db2dll_hinstance, "SQLDataSources");
db2dll_call_SQLFreeHandle = (DB2DLL_CALL_T_SQLFreeHandle) dlsym(db2dll_hinstance, "SQLFreeHandle");
#endif
if (!(db2dll_call_SQLAllocHandle && db2dll_call_SQLSetEnvAttr && db2dll_call_SQLDataSources && db2dll_call_SQLFreeHandle)) // Wenn nicht alle 4 vorhanden, gleich aussteigen.
{
output::println("Fehler: nicht alle erforderlichen Funktionen wurden in " DB2CLI_DLL_FILENAME " gefunden.");
#ifdef OS_WINDOWS
FreeLibrary(db2dll_hinstance);
#else
auto rc_dlclose = dlclose(db2dll_hinstance);
#endif
db2dll_hinstance = NULL;
output::println(output_type_t::success, DB2CLI_DLL_FILENAME " wurde dynamisch entladen.");
}
rc = db2dll_call_SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); // Allocate an environment handle
if (rc == SQL_SUCCESS)
rc = db2dll_call_SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, 0); // set attribute to enable application to run as ODBC 3.0 application
else
return;
output::println("Folgende Aliase sind im DB2-Client definiert:");
string db2source;
// Alle DB2-Aliase aus dem DB2-Client auslesen:
while ((rc = db2dll_call_SQLDataSources(henv, SQL_FETCH_NEXT, source, SQL_MAX_DSN_LENGTH + 1, &buffl, description, 255, &desl)) != SQL_NO_DATA_FOUND)
{
if ((buffl > 0) && (desl > 0))
{
// printf("%-30s %s\n", source, description);
db2source = (char *) source;
output::println(db2source);
}
}
output::println();
rc = db2dll_call_SQLFreeHandle(SQL_HANDLE_ENV, henv); // DLL entladen.
henv = (SQLHANDLE) 0;
}
string check_db2::get_image_path(H_DLL_INSTANCE hinstance)
{
#ifdef OS_WINDOWS
char imagePath[MAX_PATH] = {0};
GetModuleFileName(hinstance, imagePath, sizeof(imagePath));
#else
char imagePath[PATH_MAX] = {0};
typedef void(*DB2DLL_CALL_T_SQLAllocHandle)();
DB2DLL_CALL_T_SQLAllocHandle db2dll_call_SQLAllocHandle = (DB2DLL_CALL_T_SQLAllocHandle) dlsym(hinstance, "SQLAllocHandle");
const char *dlsym_error = dlerror();
if (!dlsym_error) // Nur wenn keine Fehler bei der Auflösung des Funktionspointers aufgetreten...
{
Dl_info info;
if (dladdr(db2dll_call_SQLAllocHandle, &info)) // Nur wenn Informationen über die Funktion abrufbar sind...
{
if (info.dli_fname) // Nur wenn der Filename unter den Informationen verfügbar ist...
{
strncpy(imagePath, info.dli_fname, PATH_MAX - 1);
}
}
}
#endif
return imagePath;
}
----
Stand: 20.12.2019\\
--- //[[feedback.jk-wiki@kreick.de|: Jürgen Kreick]]//
EOF