Forum: PC-Programmierung Eigenes Borland Builder C++ Programm stürzt ab


von Jonny (Gast)


Lesenswert?

Hallo!

Ich bin gerade dabei mit Baorland Builder C++ 5.0 eine Oberfläche für
einen CAN Adapter zu bauen.
Ich habe zwei Units (lpt.cpp und can.cpp)
In der Unit can.cpp habe ich alle notwendigen Routinen reingestellt.
Zum Ansteueren der LPT benutze ich die inpou32.dll Datei.
In der Unit can.cpp habe ich funktionen geschrieben, mit denen ich die
DLL lade bzw. wieder schließen kann.

z.B.:
-->can.cpp
class Tcan
{
void Tcan::LoadLIB(void)
{
/* Load the library */
hLib = LoadLibrary("inpout32.dll");

if (hLib == NULL)
{
 ShowMessage("LoadLibrary Failed.\n");
}
/* get the address of the function */

inp32 = (inpfuncPtr) GetProcAddress(hLib, "Inp32");

if (inp32 == NULL)
{
 ShowMessage("GetProcAddress for Inp32 Failed.\n");
}
oup32 = (oupfuncPtr) GetProcAddress(hLib, "Out32");

if (oup32 == NULL)
{
 ShowMessage("GetProcAddress for Oup32 Failed.\n");
}
}
}
-->can.h

class Tcan
{
private:

public:
typedef short _stdcall (*inpfuncPtr)(short portaddr);
typedef void _stdcall (*oupfuncPtr)(short portaddr, short datum);

HINSTANCE hLib;
inpfuncPtr inp32;
oupfuncPtr oup32;
void Tcan::LoadLIB(void);
}


Diese Funktion rufe ich dann in der anderen Unit auf (lpt.cpp)
#include "can.h"

void start(void)
{
Tcan * CAN = new Tcan;
Tcan->LoadLib();
}

Wenn diese Funktion dann ausführe, stürtz mein Programm total ab.
Adressverletzung!
Was mache ich da Falsch? Ich denke da Problem liegt bei der Funktion
start().
Wie kann ich diesen Ausdruck "Tcan * CAN = new Tcan;" global machen,
so dass ich nur einmal dies aufrufen muss?

von Rufus T. Firefly (Gast)


Lesenswert?

Das ist so falsch und der Compiler sollte sich eigentlich weigern, das
zu übersetzen:

  Tcan * CAN = new Tcan;
  Tcan->LoadLib();


Besser wäre

  CAN->LoadLib();


Im übrigen: Lass' Dein Programm im Debugger laufen, dann solltest Du
eigentlich herausfinden können, was schief geht.


Außerdem solltest Du in Deiner Funktion LoadLib nicht nur eine
Fehlermeldung ausgeben und dann weitermachen, sondern die Funktion
sinnvoll beenden:

  hLib = LoadLibrary("inpout32.dll");

  if (hLib == NULL)
  {
    ShowMessage("LoadLibrary Failed.\n");
  }

  inp32 = (inpfuncPtr) GetProcAddress(hLib, "Inp32");

Überleg' mal, was hier passiert, wenn hLib NULL ist.

von Jonny (Gast)


Lesenswert?

Ok ich habs kappiert!

So wie hier "CAN->LoadLib();" kann ich die Funktion nicht starten.
Mein Programm braucht dann immer diesen Audruck "Tcan * CAN = new
Tcan;". Wie kann ich dies Global machen, so dass ich diesen Ausdruck
nicht immer schreiben muss?

von Jonny (Gast)


Lesenswert?

Oumann ich hab Mist erzählt. Ich muss mich korrigieren.
Stimmt ich muss CAN->LoadLib(); schreiben.  Ok aber dieser Ausdruck
"Tcan * CAN = new Tcan;" möchte ich Global setzen. Ich will nicht
ständig diesen Ausruck schreiben. Wie geht das?

von Rufus T. Firefly (Gast)


Lesenswert?

Na, außerhalb Deines Funktionsrumpfes einfach hinschreiben

  Tcan CAN;

Dann aber bei Zugriffen auf CAN keine Pointerdereferenzierung mehr
durchführen, also anstelle von

  CAN->LoadLib();

musst Du

  CAN.LoadLib();

hinschreiben.

von Jonny (Gast)


Lesenswert?

Vielen Dank! Ich werde das mal ausprobieren. Und wie sieht es aus wenn
ich die Funktion verlasse bzw. Programm schließe, da muss dich der
Speicher dann wieder freigegeben werden --> delete. Wo muss ich das
dann ausführen?

von Rufus T. Firefly (Gast)


Lesenswert?

Du musst bei dieser Art der Variablendeklaration Dich weder um das
Anlegen mit new noch um das Löschen mit delete kümmern, das macht der
Compiler schon für Dich.

Allerdings solltest Du berücksichtigen, daß der Konstruktor der
Variablen aufgerufen wird, bevor main() aufgerufen wird - also nicht im
Konstruktor irgendwelche Initialisierungen erwarten, die erst in main()
durchgeführt werden.

von Jonny (Gast)


Lesenswert?

Hallo RUFUS!

Ich habe mal deinen Vorschlag in mein Programm eingebracht.
Leider funktioniert dies nicht. Wenn ich "Tcan CAN;" in die Header
von der Unit "lpt.h" einfüge, dann erscheint vom Borland Builder
diese Meldung "Typenname erwartet. Hmm...was kann das sein?

von Jonny (Gast)


Lesenswert?

Vielleicht muss ich "Tcan CAN;" einfach ganz am Anfang in die lpt.cpp
Datei reinschreiben.

Kann mir dazu jemand helfen?

von Jonny (Gast)


Lesenswert?

Hallo....

von Rufus T. Firefly (Gast)


Lesenswert?

"Wenn ich "Tcan CAN;" in die Header von der Unit "lpt.h"
einfüge,"

Was ist denn das für ein Quark?

Das gehört nicht in eine Headerdatei, sondern in eine C/CPP-Datei.

"Unit" ist als Begriff hier völlig fehl am Platze.

von AndreasH (Gast)


Lesenswert?

Hallo,

ich meine Du musst
TCan* Can;

am Anfang Deiner CPP-Unit einfügen. Damit ist es lokal und nicht
global.
Du kannst das auch in die Header-Datei machen. Das hat aber den
Nachteil, wenn Du die Header-Datei in anderen Units einbindest, meckert
es der Compiler als mehrfach deklariert an.

Irgendwo im Programm kommt dann:
void start(void)
{
CAN = new Tcan;
Tcan->LoadLib();
}

Und das:
  if (hLib == NULL)
  {
    ShowMessage("LoadLibrary Failed.\n");
  }

solltest Du wirklich noch einmal überdenken. Zum testen einfach mal die
dll löschen.
Spätestens dann merkst Du was Rufus meinte.
Wenn er es Dir direkt gesagt hätte, wäre der Lerneffekt flöten gegangen
:)


Grüße
Andreas

von Rufus T. Firefly (Gast)


Lesenswert?

Nein, darum geht es nicht.
Jonny möchte sein Objekt nutzen können, ohne new/delete verwenden zu
müssen.

Daher darf eben kein Pointer verwendet werden, sondern es muss eine
globale Variable verwendet werden.

Beispiel:

/* bla.h */

extern TCan CAN;

/* ende von bla.h */

/* bla.cpp */

// übliche #include-Orgie

#include "can.h" // irgendwo muss ja der Datentyp TCan definiert
werden
#include "bla.h"

TCan CAN;


int main()
{
  CAN.LoadLib();

  // etc. etc.

}

/* ende von bla.cpp */

Auf bla.h kann verzichtet werden, wenn es nur ein Sourcemodul gibt, das
auf die Variable CAN zugreifen soll - sonst ist bla.h in jedes
Sourcemodul einzubinden, das diese Variable verwendet.

Da LoadLib() durchaus fehlschlagen kann, ist hier eine sinnvolle
Fehlerbehandlung angesagt - so könnte beispielsweise LoadLib einen
Rückgabewert erhalten, der aussagt, ob's geklappt hat ...


Mit "units" hat all' das übrigens immer noch nichts zu tun.

von AndreasH (Gast)


Lesenswert?

"Jonny möchte sein Objekt nutzen können, ohne new/delete verwenden zu
müssen."

Wo steht das?
Ich habe das so verstanden, dass er die Variable CAN überall benutzen
will. Egal ob als Pointer oder direkt die Klasse.

Dafür muss in der Can.cpp stehen:
TCan* Can;

in der Can.h muss stehen:
extern TCan* Can;

Die Can.h muss dann in der lpt.c includiert werden.

Wenn dass extern jetzt weggelassen wird und ebenso das TCan* Can in der
Can.cpp dann würde der Compiler dass als doppelte Deklaration
anmeckern.
Insofern hat das schon was mit units zu tun. Im C++-Builder wird jede
zum Projekt zugehörige Datei unit genannt.
@Rufus T. Firefly: Oder was meintest Du mit units?

Wichtig ist das vor dem Zugriff in der lpt.cpp die Variable Can
initialisiert wurde. Sonst gibts einen Speicherfehler.
Also irgendwo "Tcan * CAN = new Tcan;" vorher. Das darf auch nur
einmal erfolgen. Insofern darf er das gar nicht mehrfach durchführen.
Zur Sicherheit kann er aber auch noch folgendes machen:
if (Can != NULL)
  Can->LoadLib();
else
  ShowMessage("Wir haben ein Problem");

Das "Tcan->LoadLib();" war übrigens Quatsch was ich da geschrieben
habe, so muss es sein: "Can->LoadLib()";

Grüße
Andreas

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.