DLL Wrap

masterjcl

Mitglied
Hallo

Ich habe ein kleines Problem ich habe eine DLL die falsche Parameter bekommt dies betrifft einen Kartenleser, ich möchte einige Parameter beim Call verändern. Dazu habe ich mir gedacht ich schreibe eine DLL die die Orginal wrapped. Leider funktioniert das ganz nicht so wie ich mir das gedacht habe. Ich benutze das Visual Studio. Die Orginal dll is ct32.dll diese dll hat 3 Funktionen. Leider steigt beim laden der dll die Anwendung aus.

Wrap class header und cpp
Code:
#pragma once
#include <windows.h>
class HEXWrap
{
/* DLL Importe */
typedef CHAR (WINAPI *CT_INIT)  (USHORT, USHORT);
typedef CHAR (WINAPI *CT_DATA)  (USHORT, UCHAR*, UCHAR*, USHORT, UCHAR*, USHORT*, UCHAR*);
typedef CHAR (WINAPI *CT_CLOSE) (USHORT);             

HINSTANCE hDLL; CT_INIT CT_init; CT_DATA CT_data; CT_CLOSE CT_close;


public:
	HEXWrap(void);
	~HEXWrap(void);

char HEX_CT_INIT(
	          USHORT, 
	          USHORT
			  );
 
 char HEX_CT_DATA( 
	           USHORT, 
	           UCHAR*, 
			   UCHAR* ,
			   USHORT, 
			   UCHAR* ,
			   USHORT*,
			   UCHAR* 
			   );
 

 char HEX_CT_CLOSE(USHORT);
 

};

// ------------------ cpp 

#include "StdAfx.h"
#include "HEXWrap.h"
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include <atlconv.h>
#include "windows.h"

HEXWrap::HEXWrap(void)
{
char           *dllname;     // Name der CTAPIDLL
dllname = "ct32.dll"; // dllname setzen
LPCWSTR lpcwstr_string = A2BSTR(dllname);
hDLL = LoadLibrary(lpcwstr_string);
/* DLL Handler setzen */
CT_init = (CT_INIT)GetProcAddress(hDLL,"CT_init");
CT_data = (CT_DATA)GetProcAddress(hDLL,"CT_data");
CT_close = (CT_CLOSE)GetProcAddress(hDLL,"CT_close");
if (hDLL == NULL){ PostQuitMessage (0); printf("DLL konnte nicht geladen werden !");exit(0);}//send exit
}

HEXWrap::~HEXWrap(void)
{
FreeLibrary(hDLL); /* DLL aus dem Speicher entfernen */
}



char HEXWrap::HEX_CT_INIT(
	          USHORT ctn, 
			  USHORT ptn){
return CT_init(ctn,ptn);
}
 
 char HEXWrap::HEX_CT_DATA( 
	           USHORT  ctn, 
	           UCHAR  *quelle, 
			   UCHAR  *ziel,
			   USHORT  komm_laenge, 
			   UCHAR  *kommando,
			   USHORT *retlaenge,
			   UCHAR  *ret_wert
			   )
 {
 return CT_data(ctn, quelle,  ziel,  komm_laenge,     kommando, retlaenge, ret_wert);
        

 }
 

 char HEXWrap::HEX_CT_CLOSE(USHORT ctn)
 {
 return CT_close(ctn);
 }


vorgenerierte cpp Klasse zum exportieren, meine Vermutung ist das er die nicht als dll funtionen einbindet, VS kompiliert sie aber und linkt sie auch.

Code:
// CTAPIHEX.cpp : Definiert die exportierten Funktionen für die DLL-Anwendung.
//
// CT API Wrapper
#include "stdafx.h"
#include  "HEXWrap.h"
#define DllExport __declspec(dllexport)
HEXWrap *hexwrap;
void checkInstance(){
	if(hexwrap==0){
	 hexwrap= new HEXWrap();
	}
}

  char  DllExport WINAPI CT_init(
	          USHORT ctn, 
	          USHORT ptn)
 {
 checkInstance();
 return hexwrap->HEX_CT_INIT(ctn,ptn);
 }
   char  DllExport WINAPI  CT_data( 
	           USHORT  ctn, 
	           UCHAR  *quelle, 
			   UCHAR  *ziel,
			   USHORT  komm_laenge, 
			   UCHAR  *kommando,
			   USHORT *retlaenge,
			   UCHAR  *ret_wert
			   )
 {
	 checkInstance();
	 return hexwrap->HEX_CT_DATA(ctn,quelle,ziel,komm_laenge,kommando,retlaenge,ret_wert);
 }
  
   char  DllExport WINAPI  CT_close(USHORT ctn)
 {
	 checkInstance();
	 return hexwrap->HEX_CT_CLOSE(ctn);

 }



wo liegt das Problem sollte doch eigentlich funktioniern


mfg
 
Hallo,

auf Anhieb kann ich erst mal keine Fehler erkennen, es sollte also funktionieren.

Trotzdem noch ein paar Anmerkungen:
Den Test "if (hDLL == NULL) ..." solltest du vor Verwendung GetProcAddress() ausführen; ebenso so auch vor dem Aufruf von FreeLibrary().
Dein checkInstance() ist nur die halbe Miete. Du solltest auch prüfen, ob der Aufruf von GetProcAddress() erfolgreich war und den Funktionsaufruf am besten noch in einen try-catch-Block packen, weil die Verwendung externer Ressourcen immer eine potentielle Fehlerquelle ist.
Das könnte dann so aussehen:
C++:
char DllExport WINAPI CT_init(USHORT ctn, USHORT ptn)
{
    checkInstance();
    
    if( hexwrap->HEX_CT_INIT )
    {
        try
        {
             return hexwrap->HEX_CT_INIT(ctn,ptn);
        }
        catch( ... )
        {
            return 0; // Rückgabewert für Fehlerzustand 
        }
    }
}
Wenn du die o.g. Tests mit einbaust, solltest du jetzt zumindest feststellen können, an welcher Stelle es hakt.

Gruß
MCoder
 
Vielen Dank für die schnelle Antwort,

Ich habe noch einmal eine Dummy dll geschrieben um das ganze mal auf den Punkt zu bringen tatsächlich findet er die per GetProcAdress ermittelten Einsprungadressen nicht.
Warum ist mir allerdings schleierhaft. Hier noch einmal den auf ein Minimum reduzierten Testcode
Es sind 2 Dateien
ct32wrap.cpp
Loadertest.cpp

ct32wrap.dll kompiliert mit cl -LD -GX ct32wrap.cpp
------------------------------------------------------
Code:
#include "windows.h"
#define DllExport __declspec(dllexport)

char DllExport WINAPI CT_init(USHORT ctn, USHORT ptn)
 {return 'i';}
char DllExport WINAPI  CT_data( 
                    USHORT  ctn, UCHAR  *quelle, UCHAR  *ziel,
                    USHORT  komm_laenge, UCHAR  *kommando,
                   USHORT *retlaenge,UCHAR  *ret_wert )
 {return 'd';}
char  DllExport WINAPI  CT_close(USHORT ctn )
 {return 'c';}

#include <stdio.h>
#include <windows.h>
#include <winbase.h>
BOOL APIENTRY DllMain( HANDLE ModulHandle, DWORD  InitOderExit, 
LPVOID lpReserved )
{
  switch( InitOderExit )
    {
    case DLL_PROCESS_ATTACH:
      fprintf( stderr, "DllMain: DLL_PROCESS_ATTACH\n" );
      break;
    case DLL_PROCESS_DETACH:
      fprintf( stderr, "DllMain: DLL_PROCESS_DETACH\n" );
      break;
    default:
      break;
    }
  return TRUE;// per default alles ok
}


--------- Loadertest.cpp kompiliert mit cl Loadertest.cpp
Code:
#include <windows.h>
#include <stdio.h>
 

typedef CHAR (WINAPI *CT_INIT) (USHORT, USHORT);
typedef CHAR (WINAPI *CT_DATA) (USHORT, UCHAR*, UCHAR*, 
USHORT, UCHAR*, USHORT*, UCHAR*);
typedef CHAR (WINAPI *CT_CLOSE) (USHORT);         
HINSTANCE hDLL; CT_INIT CT_init; CT_DATA CT_data; CT_CLOSE CT_close;

int main () {
       // DLL Datei laden
        hDLL = LoadLibrary("ct32wrap.dll");
 
        if (hDLL) {
           CT_init = (CT_INIT)GetProcAddress(hDLL,"CT_init");
           CT_data = (CT_DATA)GetProcAddress(hDLL,"CT_data");
           CT_close = (CT_CLOSE)GetProcAddress(hDLL,"CT_close");
           char back;
           try {back = CT_init(1,1);}
           catch(...){printf("Fehler beim Call der Adresse %i\n",CT_init);return 0;}
           
           printf("Ausgabe %i Adresse %i",back,CT_init);
           BOOL fFreeResult = FreeLibrary(hDLL);
        } 
        else {printf("Fehler DLL nicht geladen");}
 
 
 }

Ausgabe meine DLL:
DllMain: DLL_PROCESS_ATTACH
Fehler beim Call der Adresse 0
DllMain: DLL_PROCESS_DETACH

Ausgabe Orginal DLL:

Ausgabe 0 Adresse 268455648



mfg
 
Zuletzt bearbeitet:
Nimm mal das Tool "Depends.exe" (liegt irgendwo unter \Microsoft Visual Studio\Common\Tools oder so ähnlich) und öffne die DLL damit. Da kannst du dir die Namen der exportierten Funktionen rausholen. Die können durchaus anderes sein, wie beim Einbinden der DLL über eine Import-Lib.

Gruß
MCoder
 
Sehr komisch

Bei den zu exportierenden Funktionen stellt er ein Fragezeichen vorhan.

?CT_init@@YADGG@Z

im Orginal sieht das aber so aus

CT_init


Nichts desto trotz sollte doch das obige Beispiel als reiner DLL test funtionieren tut es aber nicht. Obwohl ich nur 3 Dummy Funktionen habe ohne irgendwelche Klassen oder anderen Schnickschnack eigentlich kann man das ganze sogar noch verkürzen.

Code:
#include "windows.h"
#define DllExport __declspec(dllexport)
char DllExport WINAPI CT_init(USHORT ctn,USHORT ptn) {return 'i';}

#include <stdio.h>
#include <windows.h>
#include <winbase.h>

BOOL APIENTRY DllMain(HANDLE ModulHandle,  DWORD  InitOderExit,
LPVOID lpReserved ){return TRUE;}

testcode
Code:
#include <windows.h>
#include <stdio.h>
 
 // Definition der DLL-Funktion, die verwendet werden soll
 

typedef CHAR (WINAPI *CT_INIT) (USHORT, USHORT);
HINSTANCE hDLL; 
CT_INIT CT_init; 
 int main () {
        // DLL Datei laden
        hDLL = LoadLibrary("ct32wrap.dll");
        printf("DLL Adresse: %i\n",hDLL);
        if (hDLL) {
           CT_init = (CT_INIT)GetProcAddress(hDLL,"CT_init");
           char back;
           try {back = CT_init(1,1);}
           catch(...){printf("Fehler beim Call der Adresse %i\n",CT_init);return 0;}
           printf("Ausgabe %i Adresse %i",back,CT_init);
           BOOL fFreeResult = FreeLibrary(hDLL);
        } 
        else {printf("Fehler DLL nicht geladen");}
  }



das geht aber auch nicht ich bin nicht so der c++ coder aber das macht mir echt Probleme
 
Zu exportierende Funktionen solltest du so deklarieren:
Code:
#define DllExport EXTERN_C __declspec(dllexport)
Bei deinen Test sieht man nicht, ob GetProcAddress() überhaupt einen gültigen Zeiger zurückgibt.
 
Bei den zu exportierenden Funktionen stellt er ein Fragezeichen vorhan.

?CT_init@@YADGG@Z

Das ist das C++ naming Schema.
Würd ich mich gar nicht erst damit plagen.
Wenn du eine Funktion aus ner .dll exportieren willst, bindest du direkt über die .lib
(also nicht mit GetProcAddress)

oder du machst es wie MCoder vorgeschlagen hat:

Zu exportierende Funktionen solltest du so deklarieren:

Code:
#define DllExport EXTERN_C __declspec(dllexport)


Aber selbst dann wird folgender Code unrichtig sein:

CT_init = (CT_INIT)GetProcAddress(hDLL,"CT_init");
CT_data = (CT_DATA)GetProcAddress(hDLL,"CT_data");
CT_close = (CT_CLOSE)GetProcAddress(hDLL,"CT_close");

Grund ist, dass nach dem c- naming Schema immer ein Unterstrich dem Funktionsnamen vorgestellt wird und die Parametergröße in Bytes nach einem @ nachgestellt.

Richtiger wäre in dem Fall also:

CT_init = (CT_INIT)GetProcAddress(hDLL,"_CT_init@8");
 
Mal nen Tipp generell zu deiner Klasse ...
(1) Initialisierungsliste
(2) Wenn du sagst char* test="abc"; ist das sehr gefährlich. Mach const char* test="abc"; und es geht i.o.
(3) <xyz.h> sind meist teile des C-Standard und sollten durch <cxyz> ersetzt werden.
(4) Binde nur Header ein die du brauchst! (vgl. <iostream>, <conio.h>)
(5) Dein Rumgewurschtel da bei LoadLibrary ... UNICODE, dann LoadLibraryW sonst LoadLibraryA, oder direkt TEXT-Makro nutzen. Dann bist du unabhängig. Aber nicht so einene Mist! L"" geht auch.
 
Wenn du deine .dll über eine .lib linkst, dann nimmst du am besten ein gemeinsames Headerfile.
Das sollte ungefähr so aussehen:


C++:
 // exports.h

#ifdef _MYXYDLL_EXPOTS
#define MYXYDLLSYMBOL __declspec(dllexport)
#else
#define MYXYDLLSYMBOL __declspec(dllimport)
#endif

extern "C" // "brauchst" du nur, wenn du GetProcAddress
                // mit einigermaßen verständlichen Namen verwenden willst
{

   int MYXYDLLSYMBOL WINAPI MyXyService(int a, int b);

};


Im .dll Projekt musst du natürlich logischerweise _MYXYDLL_EXPOTS definieren, bevor du den header einbindest, und MyXyService korrekt definieren...
Im importierenden Projekt musst du die vom .dll Projekt erstellte .lib zu den Linker dependencies hinzufügen..
 
Zuletzt bearbeitet:
Also, die Namensauflösung unter C++ ist etwas anders als unter C, d.h. würde ich an deiner Stelle, falls deine Funktionen keinen Gebrauch von C++ Sprachelementen macht, diese als extern "C" anlegen und damit die Namensauflösung auf C zurückbringen. Dann klappt es mit dem Laden vllt. auch ;)

Und nochwas ... das ist die bescheuerste Singleton-Implementierung, die ich je gesehen habe :D
 
Zurück