[Dev-C++] Fremde DLL statisch linken

Quidoff

Lieutenant
Registriert
Feb. 2005
Beiträge
897
Um unter Windows 2000/XP direkt auf meinen Parallel Port (Adresse 0x378) zugreifen zu können, brauche ich eine spezielle DLL, die mir den direkten Zugriff auf diese Ports gestattet. Eine solche DLL habe ich auch schon gefunden.
Inpout32.dll
Diese DLL exportiert zwei Funktionen (Out32 und Inp32).
Out32 schreibt Daten auf den Port. Die Definition ist. void Out32(short adresse, short data);
Inp32 liest Daten von einem Port. Diese Funktion brauch ich zunächst nicht.

Ich habe ein Programm geschrieben, das soweit auch erstmal funktioniert (habe es mit LED's am Parallel Port in der Schule getestet). In meinem Programm habe ich die DLL dynamisch geladen. Das will ich jetzt auch statisch probieren. (Ich benutze Dev-C++ 4.9.9.2 mit mingw Compiler)
Soviel habe ich bis jetzt gemacht:
Aus der DLL eine def Datei erzeugt mit
Code:
pexports -o inpout32.dll > io.def
pexports ist Teil der minGW utilities.

Das ist der Inhalt der def Datei:
Code:
LIBRARY INPOUT32.dll
EXPORTS
Inp32                           @1
Out32                           @2

Anschließend muss ich daraus die import library für Dev-C++ erstellen.
Mit folgendem Befehl:
Code:
dlltool --def io.def --dllname inpout32.dll --output-lib io.a
Die Datei io.a (2.802 Bytes) wird gelinkt.

Und das ist der Code:
PHP:
#include <iostream>
#include <windows.h>
using namespace std;

//typedef void _stdcall (*Out32)(short a, short b);
extern "C" __cdecl void Out32(short PortAddress, short data);

int main() {
    //HINSTANCE hLib = LoadLibrary("inpout32.dll");
    //Out32 outport = (Out32) GetProcAddress(hLib, "Out32");
    SetConsoleTitle("huhn");
    
    cout << "short: " << sizeof(short) << "bytes" << endl;
    
    int i=0;
    for(;i<10;i++) {
            cout << i << endl;
            Out32(0x0378, 0);
            //(outport)(0x378, i);
    }
    
    cout << i;
    
    cin.get();
    cin.get();
    return 0;
}

Ausgegeben wird folgendes:
Code:
short: 2bytes
0
1
2
3
4
11
Nach dem Drücken von [Enter] gibts dann auch noch nen Fehler "Das Programm hat einen Fehler verursacht und muss beendet werden."

Warum wird das i verändert? Ich denke es hat was mit der Größe der Parameter zu tun. Bin mir aber nicht sicher.

Wie gesagt dynamisch (das auskommentierte) funktioniert es perfekt.
 
Zuletzt bearbeitet:
Hallo Quidoff,

ich vermute mal das das Out32 in den Stack deines Programms schreibt. Am besten kannst Du das nachvollziehen wenn Du mal den Assemblercode dazu durchsteppst.

Anscheinend wird wohl das cout in der For Schleife durch einen Seiteneffekt der Out32 Funktion dazu gebracht keine Ausgaben mehr zu machen.

Purify kann da auch weiterhelfen um solche Fehler zu analysieren.

Wenn Du keinen Quellcode zu der DLL hast wird es wohl das einfachste sein beim dynamischen Linken zu bleiben.

MfG

Arnd
 
Der Quellcode ist bei der DLL dabei.

Woran erkenn ich denn, ob Out32 in den Stack schreibt?
Ich kenn mich in Assembler-Code nicht allzugut aus.

//edit
Mir fällt auf, dass im Code nirgens dllexport oder so steht. Die Funktionen sind so deklariert:
Code:
void _stdcall Out32(short PortAddress, short data);
short  _stdcall Inp32(short PortAddress);
Wenn ich die Deklaration in meinem Programm mit __stdcall mache, dann bekomme ich folgenden Fehler:
Code:
  [Linker error] undefined reference to `Out32@8'
 
Zuletzt bearbeitet:
Entweder am seltsamen Verhalten des Programms, oder indem Du per Debugger dem Assemblercode auf die Finger schaust :-).

Probier doch mal Purify aus. Hier gibt es eine Trial, man muss sich aber registrieren:

https://www14.software.ibm.com/weba...load&id=2005-08-01+06:35:49.402277R&fam=&cat=

Das Tool zeigt Dir ziemlich schnell an wo das Problem liegt, sofern es etwas mit Überschreibern oder ähnlichem Fehlverhalten zu tun hat.

Mit stdcall und cdecl änderst Du ja glaube ich auch die Reihenfolge der Parameterübergabe.
Der dllexport sollte ja wohl auch in der DLL stehen. Du müsstest in Deinem Code dllimport verwenden.

MfG

Arnd
 
Zuletzt bearbeitet:
Ich habe das Programm jetzt nochmal umgeschrieben und habe dllimport verwendet. Außerdem habe ich nochmal ne Variable j hinzugefügt, die sich in der Tat "komisch" verhält.

PHP:
#include <iostream>
#include <windows.h>
using namespace std;

//typedef void _stdcall (*Out32)(short a, short b);
extern "C" __cdecl __declspec(dllimport) void Out32(short PortAddress, short data);

int main() {
    //HINSTANCE hLib = LoadLibrary("inpout32.dll");
    //Out32 outport = (Out32) GetProcAddress(hLib, "Out32");
    SetConsoleTitle("huhn");
    
    cout << "short: " << sizeof(short) << "bytes" << endl;
    
    int i=0;
    int j=0;
    for(;i<10;i++) {
            cout << i << endl;
            j++;
            Out32(0x0378, 0);
            //(outport)(0x378, i);
    }
    
    cout << endl <<  i << endl << j;
    
    cin.get();
    cin.get();
    return 0;
}

Ausgabe:
Code:
short: 2bytes
0
1
2
3
4

11
2293624

//edit
Also so wie ich das jetzt verstanden habe bedeutet "Out32 @2", dass 2Byte für Parameter und Rückgabewert reserviert werden müssen. Die zwei Parameter short benötigen aber 8Bytes. Deshalb bekomme ich (wenn ich oben __stdcall statt __cdecl schreibe) auch einen Fehler, dass der Linker keine Referenz zu Out32@8 findet.
 
Zuletzt bearbeitet:
Das Linker problem hat eher nichts mit dem Absturz und der fehlerhaften Ausgabe zu tun. Das bedeutet eher geht oder geht nicht.

Probier mal das hier aus und prüfe nachher den Inhalt der Dummyarrays:

Code:
 int i=0;
    int j=0;
    for(;i<10;i++) {
            cout << i << endl;
            j++;
            char lOverwrite1[1024];
            memset(lOverwrite1,0,sizeof lOverwrite1);           
            Out32(0x0378, 0);
            char lOverwrite2[1024];            
            memset(lOverwrite2,0,sizeof lOverwrite2);           
            //(outport)(0x378, i);
    }

Wenn in den Overwrite Variablen etwas anderes als 0 steht, hast Du einen Überschreiber in der DLL. Das kann aber auch am statischen linken liegen.

Ich kenne ähnliche Effekte wenn man die MFC statisch linkt. Dann werden plötzlich keine Ressourcen mehr gefunden.

MfG

Arnd
 
Also das ist hier alles sehr merkwürig:
PHP:
#include <iostream>
#include <windows.h>
using namespace std;

//typedef void _stdcall (*Out32)(short a, short b);
extern "C" __cdecl void Out32(short PortAddress, short data);

int main() {
    //HINSTANCE hLib = LoadLibrary("inpout32.dll");
    //Out32 outport = (Out32) GetProcAddress(hLib, "Out32");
    
    cout << "short: " << sizeof(short) << "bytes" << endl;
    
    int i=0;
    int j=0;
    for(;i<10;i++) {
            cout << i << endl;
            j++;
            char lOverwrite1[1024];
            memset(lOverwrite1,0,sizeof lOverwrite1);           
            Out32(0x0378, 0);
            char lOverwrite2[1024];            
            memset(lOverwrite2,0,sizeof lOverwrite2);           
            //(outport)(0x378, i);
    }
    
    cout << i;
    
    cin.get();
    cin.get();
    return 0;
}
So ist die Ausgabe:
Code:
short: 2bytes
0
1
2
3
4
5
6
Das Programm stürzt schon innerhalb der Schleife mit der oben genannten Fehlermeldung ab.
2. Versuch:
PHP:
#include <iostream>
#include <windows.h>
using namespace std;

//typedef void _stdcall (*Out32)(short a, short b);
extern "C" __cdecl void Out32(short PortAddress, short data);

int main() {
    //HINSTANCE hLib = LoadLibrary("inpout32.dll");
    //Out32 outport = (Out32) GetProcAddress(hLib, "Out32");
    
    cout << "short: " << sizeof(short) << "bytes" << endl;
    
    int i=0;
    int j=0;
    for(;i<10;i++) {
            cout << i << endl;
            j++;
            char lOverwrite1[1024];
            //memset(lOverwrite1,0,sizeof lOverwrite1);           
            Out32(0x0378, 0);
            char lOverwrite2[1024];            
            //memset(lOverwrite2,0,sizeof lOverwrite2);           
            //(outport)(0x378, i);
    }
    
    cout << i;
    
    cin.get();
    cin.get();
    return 0;
}
So ist die Ausgabe:
Code:
short: 2bytes
0
1
2
3
4
5
6
7
8
9
10
Also wie erwünscht. Es gibt auch keine Fehlermeldung. Ich gehe davon aus, dass die beiden "Buffer" soviel Speicher um die Funktion herum reservieren, dass die Veränderungen der Funktion nur diesen Bereich betreffen und deshalb nicht die Variable i oder einen anderen Teil des Programms überschreiben, sodass eine Fehlermeldung entsteht.
 
Zuletzt bearbeitet:
Genau das war der Sinn der beiden Buffer :-). D.h. nur das in der DLL etwas faul ist. Als Dauerlösung geht das natürlich nicht. Solche Fehler haben die Eigenart zu wandern, d.h. wenn Du mehr Funktionen benutzt und Dein Programm komplexer wird, treten die Fehler dann irgendwann woanders auf.

Probier mal Purify, das hilft oft weiter. Es kann natürlich auch sein das das Problem im Treiber zu der DLL sitzt, dann wird die Fehlersuche etwas komplexer. Der Code der DLL hat mir auf den ersten Blick nicht problematisch ausgesehen.

MfG

Arnd
 
Zuletzt bearbeitet:
Ok, aber bekomm ich das Programm nicht auch irgendwo anders her?
Bei IBM werd ich ja auch nach Telefon# und Adresse gefragt.

Muss das sein?
 
Legal nicht. Ich habe die Telefonnummer gar nicht angegeben. Das einzige was dann passiert ist, das ich einmal eine EMail bekommen habe. Und meine Registrierung liegt schon ein paar Jahre zurück.
Es hindert dich doch niemand, einfach nur Nullen oder einen Strich einzutragen. Das einzige was Du brauchst ist eine EMail Adresse.

MfG

Arnd
 
Ok. Ich habe mich da mal eben mit einigen fiktiven Daten angemeldet. Es sei denn es gibt in Deutschlang wirklich eine "Muhkuhstrasse" ;)

Noch ist keine eMail angekommen. Ich werde mich melden, wenn es Neuigkeiten gibt.

//edit
Ach man bekommt ja gar keinen Bestätigungscode. Na da hätte ich lange warten können.
Lade Purify gerade runter. Wird trotzdem ein Weilchen dauern, bis ich mich wieder melde.

Danke schonmal.

//edit2
So. Ich hab das Programm runtergeladen, installiert und getestet.
Mit dem Ergebnis kann ich allerdings nicht viel anfangen. Wie gesagt: Ich habe nur ein begrenztes Wissen was Debugging angeht.
Ich habe also das Programm "Rational Purify" gestartet und bei "Run" "Error and leak data" ("Detect memory errors ans leaks in natively compiled C/C++ applications") ausgewählt. Die Ausgabe mit dem code an den Trick mit den char Arrays ist wie folgt:
Code:
[I] Starting Purify'd C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht\lauflicht.exe at 23.02.2006 18:19:47
        Instrumented executable: C:\Programme\Rational\PurifyPlus\cache\lauflicht$Purify_C_Dokumente und Einstellungen_Christoph Korn_Desktop_Lauflicht.exe
        Working directory:       C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht
        Command line arguments:  <none>
        Process ID:              0x354
        Thread ID: 0x394
[I] Starting main
[E] EXU: Unhandled exception  {1 occurrence}
        Exception code: 0xc0000005 [Error: access violation reading from 0x0000000b]
        Exception address: ???            [ip=0x0000000B]
        Filter:  [C:\Programme\Rational\PurifyPlus\cache\PURERT.DLL ip=0x3F0021F3]
        Exception location
            ???            [C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht\lauflicht.exe ip=0x00449059]
[I] Summary of all memory leaks... {132 bytes, 1 block}
[I] MPK: Potential memory leak of 132 bytes from 1 block allocated in _setusermatherr [MSVCRT.dll]
        Offset 0x00000009 referenced by 0x01c73dd8, a location in a malloc'd block
        Distribution of potentially leaked blocks
               132 bytes from 1 block of 132 bytes (0x01c73dd8)
        Allocation location
            malloc         [C:\WINNT\system32\MSVCRT.dll]
            _setusermatherr [C:\WINNT\system32\MSVCRT.dll]
            _getmainargs   [C:\WINNT\system32\MSVCRT.dll]
            ???            [C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht\lauflicht.exe ip=0x0040114F]
            ???            [C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht\lauflicht.exe ip=0x00401238]
            ???            [C:\Dokumente und Einstellungen\Christoph Korn\Desktop\Lauflicht\lauflicht.exe ip=0x00449059]
[I] Program terminated at 23.02.2006 18:19:54
Da steht erstmal nix von inpout32.dll. Aber was hat das genau zu bedeuten?

Hier nochmal der verwendete Code:
PHP:
#include <iostream>
#include <windows.h>
using namespace std;

//typedef void _stdcall (*Out32)(short a, short b);
extern "C" __cdecl void Out32(short PortAddress, short data);

int main() {
    //HINSTANCE hLib = LoadLibrary("inpout32.dll");
    //Out32 outport = (Out32) GetProcAddress(hLib, "Out32");
    
    cout << "short: " << sizeof(short) << "bytes" << endl;
    
    int i=0;
    for(;i<10;i++) {
            cout << i << endl;
//            char lOverwrite1[1024];
            //memset(lOverwrite1,0,sizeof lOverwrite1);           
            Out32(0x0378, 0);
//            char lOverwrite2[1024];            
            //memset(lOverwrite2,0,sizeof lOverwrite2);           
            //(outport)(0x378, i);
    }
    
    cout << i;

    cin.get();
    cin.get();
    return 0;
}
 
Zuletzt bearbeitet:
Es war ja immerhin einen Versuch wert :-). Aber die Ausgaben helfen wirklich nicht viel weiter.
Die Exception kann man wohl ignorieren da sie im Purify selber auftritt, die Memoryleaks sind zwar unschön sind aber wohl in der Runtimelibrary von daher auch erst mal nicht relevant.
Ich würde mal einen Breakpoint auf die Out32 Methode legen und schauen ob dann Ausgaben vom Purify kommen.

Ansonsten hilft dann wohl nur debuggen der DLL und des Treibers. Eben notfalls auf Assemblerebene. Eine andere Lösung fällt mir da sonst nicht ein.
Es ist auch nicht so kompliziert, du musst nur auf AssemblerEbene durchsteppen und gleichzeitig immer den Sicherheitsbuffer kontrollieren ab wann dort Müll reingeschrieben wird. Dann weisst Du schon mal welche Stelle im Code der DLL das Problem verursacht.

Den Assemblercode zu verstehen ist nicht mal unbedingt nötig. Oft reicht auch schon der umgebende Code und die verwendeten Daten um das Problem zu verstehen.

MfG

Arnd
 
Zuletzt bearbeitet:
Impout32.dll hat doch die sourcen mitgeliefert..!!!
was wilsste mehR???
einfach sourcen kopieren schon haste statisch gelinkt ohne DLL depend und du kanst source debugging machen...
Mfg Shade37337
 
@shade37337:
Das mit dem Source-Code hab ich auch schon probiert. Ich bekomme aber an folgender Stelle, folgenden Fehler:
PHP:
			unsigned int error;
			DWORD BytesReturned;        
			BYTE Buffer[3];
			unsigned short * pBuffer;
			pBuffer = (unsigned short *)&Buffer[0];
			*pBuffer = LOWORD(PortAddress);
			Buffer[2] = LOBYTE(data);

			error = DeviceIoControl(hdriver,
                            IOCTL_WRITE_PORT_UCHAR,
                            &Buffer,
                            3,
                            NULL,
                            0,
                            &BytesReturned,
							NULL);
Code:
86 inpout32drv.cpp [Warning] passing negative value `-0x063bfdff8' for converting 2 of `BOOL DeviceIoControl(void*, DWORD, void*, DWORD, void*, DWORD, DWORD*, _OVERLAPPED*)'
Wenn ich es auskommentiere, dann geht es kompilieren und die Ausgabe ist wie erwünscht.
Is ja klar, wenn nix gemacht wird. Der Fehler sollte also bei der DeviceIoControl() Funktion liegen.

@Arnd
Wie genau setze ich einen Breakpoint auf die Fkt? Muss ich das in der IDE so einstellen?
 
Das mit dem Brechpunkt sieht gut aus. Und das Problem liegt wohl in der Parameterübergabe. Ein &Buffer wenn Buffer ein Array ist, ist falsch.
Mach mal das & weg.

Code:
 error = DeviceIoControl(hdriver,
                            IOCTL_WRITE_PORT_UCHAR,
                            Buffer,
                            3,
                            NULL,
                            0,
                            &BytesReturned,
                            NULL);

Sicherheitshalber kannst Du auch mal einfach den Buffer grösser machen aber trotzdem 3 als Länge übergeben.

@Shade37337

Man kann übrigens auch in einer DLL debuggen.

MfG

Arnd
 
Zuletzt bearbeitet:
Der Fehler bleibt trotz vergößertem Buffer immernoch der selbe.


//edit
Das Programm zeigt zwar die Warnung, lässt sich aber trotzdem kompilieren und wird wie erwünscht ausgeführt. Ob das Programm wirklich funktioniert kann ich aber nicht sagen, weil ich zur Zeit das Gerät zum testen nicht habe.

//edit2
Was hat denn IOCTL_WRITE_PORT_UCHAR zu bedeuten? Ist mir ein wenig suspekt, dass man das auf der MSDN Seite nicht findet?

//edit3
Ich weiß jetzt warum der Compiler vor der Übergabe eines negativen Wertes warnt. In einer eingebundenen Datei steht folgendes:
PHP:
#define IOCTL_READ_PORT_UCHAR	 -1673519100 //CTL_CODE(40000, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_WRITE_PORT_UCHAR	 -1673519096 //CTL_CODE(40000, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
Sind die Kommentare Hinweis darauf, wie man auf diese Zahl kommt? Die Zahl müsste aber doch trotzdem positiv sein.
 
Zuletzt bearbeitet:
Du brauchst das DDK von MS. Kannste dir für 10€ von MS zuschicken lassen.
 
Hm. Das ist mir dafür ein bisschen zu teuer. Und am Ende bringst bei mir vllt. gar nichts, weil genau die selbe Zahl rauskommt. Wie kommt man denn überhaupt auf die Zahl 40000 oder 0x801? Laut MSDN ist der erste Parameter der Gerätetyp:
MSDN schrieb:
The values used by Microsoft are in the range 0-32767; the values 32768-65535 are reserved for use by OEMs and IHVs.
Quelle

Ich teste das Programm am besten erstmal in der Schule. Vllt funktioniert es ja trotz Warnung. Erstellen kann ich die DLL ja trotzdem. Und mit meiner DLL zeigt das Programm auch die gewünschte Ausgabe. Ich brauche das Programm nurmal zu testen. Wenns läuft, dann kann die Warnung wohl ignoriert werden.
 
Zuletzt bearbeitet:
Hallo Quidoff,

das & entfernen hat ja auch nichts mit der Warning zu tun, sondern damit das dein Programm sich seltsam verhält. Läuft es denn jetzt besser? D.h. kommen alle Ausgaben korrrekt?

Die Warning wirst Du ganz einfach umgehen können, wenn Du die negative Zahl im Hexadezimalformat angibst. Ein DWORD ist unsigned und passt daher nicht für negative Zahlen. Notfalls setze ein (DWORD) als cast davor.

Der Kommentar lässt vermuten das CTL_CODE ein Macro ist, das aus den angegebenen Parametern eine Zahl erstellt, die vom Treiber ausgewertet wird.

Schau Dir mal den Code aus dem Hardwaretreiber an. Bei &Buffer, schreibt der ziemlich stark in die Wüste.

Code:
      case IOCTL_WRITE_PORT_UCHAR:
            if (inBuffersize >= 3) 
			{
                WRITE_PORT_UCHAR((PUCHAR)address[0], data[2]);
				pIrp->IoStatus.Information = 10;
            } 
			else 
			{
			ntStatus = STATUS_BUFFER_TOO_SMALL;
            pIrp->IoStatus.Information = 0; 
            ntStatus = STATUS_SUCCESS;
			}
            break;

MfG

Arnd
 
Zuletzt bearbeitet:
Hm...
Also das Programm funktioniert jetzt sowohl mit als auch ohne & einwandfrei (d.h. heißt im moment nur, dass die Ausgabe stimmt. Ob sich tatsächlich was am Parallel Port tut, werde ich erst in der Schule testen können.)
Ich erhalte den gleichen Fehler wie oben, wenn ich die Fkt im Programm mit __cdecl statt mit __stdcall deklariere.

//edit
Ich habe mich außerdem nur mit der inpout32drv.cpp beschäftigt.
Außerdem habe ich den Teil mit der os_version Überprüfung rausgenommen, weil bei mir 0 statt 2 rauskam und er deswegen die Fkt gar nicht aufgerufen hat.

Ich meine jetzt an dieser Stelle:
PHP:
	switch(sysver)
	{
	case 1:
			//_outp( PortAddress,data); //die fkt. gibts unter windows 2000 nicht mehr. deshalb kommen compiler fehler
	break;

	case 2: //hier hab ich einfach ein default: draus gemacht
			unsigned int error;
			DWORD BytesReturned;        
			BYTE Buffer[5];
			unsigned short * pBuffer;
			pBuffer = (unsigned short *)&Buffer[0];
			*pBuffer = LOWORD(PortAddress);
			Buffer[2] = LOBYTE(data);

            Opendriver();
			error = DeviceIoControl(hdriver,
                            IOCTL_WRITE_PORT_UCHAR,
                            &Buffer,//das & hab ich weggemacht
                            3,
                            NULL,
                            0,
                            &BytesReturned,
							NULL);
	break;
	}
 
Zuletzt bearbeitet:
Zurück
Oben