Benutzer-Werkzeuge

Webseiten-Werkzeuge


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

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki