edv:prg:cpp:example:spontan_auf_eine_dynamische_library_zugreifen
"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
— : Jürgen Kreick
EOF
edv/prg/cpp/example/spontan_auf_eine_dynamische_library_zugreifen.txt · Zuletzt geändert: 2024/04/09 13:24 von Jürgen Kreick