Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi DLL: Record aus DLL holen (https://www.delphipraxis.net/68099-dll-record-aus-dll-holen.html)

Die Muhkuh 24. Apr 2006 17:02


DLL: Record aus DLL holen
 
Moin moin,

ich hab das ein kleines Problemchen mit meiner DLL.

[ Das Codegerümpel is unten ]

Also, los geht's. Ich hab in einer DLL eine Sprache drin. Diese Sprache ist über einen Record aufgebaut. Das Auslesen klappt, wie es soll (per Debugger überprüft). Dann füge ich das ja in meine ObjectList hinzu ([1]) (TLanguageManager = class(TobjectList). Wenn ich nun auf diese Language zugreife ([2]), steht danach im Record nur Mist drinne.

Bis zu Zeitpunkt von [1] stimmt alles noch. Aber bei [2] ist dann Chaos in dem Record.

[edit]
Ich kann auch noch in der Procedure SearchLanguages das hier schreiben:

Delphi-Quellcode:
 raise Exception.Create(Items['German'].LanguageInfo.lngDate);
Da funktionierts einwandfrei... :gruebel:

[/edit]

Erstmal Aufbau der Records:

Delphi-Quellcode:
type
  TLanguageObj = record
    Caption: PChar;
    Hint: PChar;
  end;

  TLanguageInfoRec = record
    lngAuthor: PChar;
    lngLanguageVersion: PChar;
    lngProgramVersion: PChar;
    lngDate: PChar;
    lngLanguageName: PChar;
  end;

  TLangfrmMainRec = record
    lngmiFile: TLanguageObj;
    lngactClose: TLanguageObj;
  end;

  TLanguageRec = record
    lngfrmMain: TLangfrmMainRec;
  end;
So, dann DLL:

Delphi-Quellcode:
function GetLanguageRecord: TLanguageRec; stdcall;
begin
  Result.lngfrmMain.lngmiFile.Caption := 'Datei - DLL';
  Result.lngfrmMain.lngmiFile.Hint := 'Datei - DLL';

  Result.lngfrmMain.lngactClose.Caption := 'Programm beenden - DLL';
  Result.lngfrmMain.lngactClose.Hint := 'Beendet das Programm';
end;

function GetLanguageInfo: TLanguageInfoRec; stdcall;
begin
  Result.lngAuthor := 'Manuel Rauber';
  Result.lngLanguageVersion := '1.0';
  Result.lngProgramVersion := '0.1';
  Result.lngDate := '2006-04-24';
  Result.lngLanguageName := 'German';
end;

exports
  GetLanguageRecord,
  GetLanguageInfo;
Dann Auslesecode ( :mrgreen: ):

Delphi-Quellcode:
procedure TLanguageManager.SearchLanguages;
var
  SR: TSearchRec;
  Lang: TLanguage;
begin
  inherited Clear;

  if FindFirst(GetLangDir + '*.dll', faAnyFile, SR) = 0 then
  begin
    repeat
      FDLLCommunicator.Filename := GetLangDir + SR.Name;
      Lang := TLanguage.Create(Self);
      Lang.LanguageInfo := FDLLCommunicator.GetLanguageInfo;
      Lang.LanguageRec := FDLLCommunicator.GetLanguage;
      inherited Add(Lang); ////////////////////    [1]
    until FindNext(SR) <> 0;
  end;
end;
Passend dazu noch GetLanguageInfo:

Delphi-Quellcode:
function TLanguageDLLCommunicator.GetLanguageInfo: TLanguageInfoRec;
var
  func: TGetLanguageInfo;
begin
  if FOpened then
  begin
    @func := GetProcAddress(FHandle, 'GetLanguageInfo');

    if @func <> nil then
    begin
      Result := func;
    end;
  end
  else
    raise Exception.Create('DLL is not open');
end;
Und zu guter Letzt noch GetItem:

Delphi-Quellcode:
function TLanguageManager.GetItem(LangName: String): TLanguage;
var
  I: Integer;
begin
  for I := 0 to inherited Count - 1 do
  begin
    ////////////////////    [2]
    if (TLanguage(inherited Items[I]).LanguageInfo.lngLanguageName = LangName) then
    begin
      Result := TLanguage(inherited Items[I]);
      Break;
    end;
  end;
end;

schöni 24. Apr 2006 17:27

Re: DLL: Record aus DLL holen
 
Hallo!

Hast Du die Recorddefinition sowohl in der Dll als auch in der Delphi Unit? Wenn sie nämlich in der Dll fehlt, wundert mich nix. Die Records müssen sowohl in der Dll als auch in der Delphi-Unit definiert sein.

schöni

Die Muhkuh 24. Apr 2006 17:28

Re: DLL: Record aus DLL holen
 
Hi,

ich hab in der DLL die Unit eingebunden in der die Records definiert sind.

[edit] Vielleicht liegts ja auch an den PChars. Muss ich evtl. noch Speicher für die reservieren? Denn, wenn ich wieder auslesen, stehen irgendwelche Kästchen und komische Zeichen da [/edit]

[edit2] Ich sachmal so: Sobald die Procedure SearchLanguages fertig ist, steht nur noch Murks drinne :-? [/edit2]

Frickeldrecktuxer_TM 24. Apr 2006 18:35

Re: DLL: Record aus DLL holen
 
Zitat:

Zitat von Spider
[edit2] Ich sachmal so: Sobald die Procedure SearchLanguages fertig ist, steht nur noch Murks drinne :-? [/edit2]

Darf ich raten? Der zuletzt eingetragene Eintrag stimmt?
Die Strings sind in der DLL als Konstanten eingetragen. Die Adresse dieser Konstanten reichst du nun an die PChars weiter. Das Auslesen funktioniert wunderbar, solange an diesen Adressen tatsächlich was brauchbares steht, also solange die DLL geladen ist. Durch deine Schleife wird die erste gefundene DLL geladen, abgearbeitet und anschließend die zweite geladen. Ich kenne die Klasse zu FDLLCommunicator nicht, aber ich nehme mal an, du gibst die DLL korrekt wieder frei. Entsprechend entfernst du die Daten, auf die deine PChars zeigen. Du solltest wirklich die Strings dauerhaft im Adressbereich deines Hauptprozesses sichtbar machen. Ich weiß nicht genau, wie GetMem() sich über Module-Grenzen hinweg verhält, aber eigentlich sollte es damit gehen, um in der DLL Speicher zu allokieren, den der Hauptprozess sieht.

Die Muhkuh 24. Apr 2006 18:44

Re: DLL: Record aus DLL holen
 
Hier ist die komplette TLanguageDLLCommunicator-Klasse

Delphi-Quellcode:
{ TLanguageDLLCommunicator }

destructor TLanguageDLLCommunicator.Destroy;
begin
  FreeLibrary(FHandle);
  FOpened := false;
 
  inherited;
end;

function TLanguageDLLCommunicator.GetLanguageInfo: TLanguageInfoRec;
var
  func: TGetLanguageInfo;
begin
  if FOpened then
  begin
    @func := GetProcAddress(FHandle, 'GetLanguageInfo');

    if @func <> nil then
    begin
      Result := func;
    end;
  end
  else
    raise Exception.Create('DLL is not open');
end;

function TLanguageDLLCommunicator.GetLanguage: TLanguageRec;
var
  func: TGetLanguageRecord;
begin
  if FOpened then
  begin
    @func := GetProcAddress(FHandle, 'GetLanguageRecord');

    if @func <> nil then
    begin
      Result := func;
    end;
  end
  else
    raise Exception.Create('DLL is not open');
end;

procedure TLanguageDLLCommunicator.SetFileName(const Value: String);
begin
  try
    FreeLibrary(FHandle);
    FOpened := false;
  except
    { Do nothing }
  end;

  FFileName := Value;

  try
    FHandle := LoadLibrary(PChar(Value));

    if FHandle = 0 then
    begin
      raise Exception.Create('Can not load languagedll'#13#10+Value);
    end;

    FOpened := true;
  except
    raise Exception.Create('Can not load languagedll'#13#10+Value);
  end;
end;
Müsste ich GetMem für jeden einzelnen PChar aufrufen?
Wo müsste ich das dann aufrufen? In der DLL oder in meinem Programm?

[edit] Hast richtig geraten. Wenn ich nur eine DLL verwende, funktioniert das ganze Problemlos.

Könnte man nicht die Records für DLL -> Programm mit PChar machen, im Programm dann aber als String speichern?

Quasi zweimal den gleichen Records, nur verwendet das Programm Stringrecordun die DLL PChar-Records.
OK, geht nich... [/edit]

Die Muhkuh 24. Apr 2006 18:57

Re: DLL: Record aus DLL holen
 
ok, ich hab jetzt mal auf ShortStrings umgestellt (eigentlich dürften mir 255 Zeichen langen). Damit funktioniert das Problemlos.

Falls jemand noch ne Idee hat, meine Ohren sind offen ;)

Frickeldrecktuxer_TM 24. Apr 2006 19:01

Re: DLL: Record aus DLL holen
 
Zitat:

Zitat von Spider
Müsste ich GetMem für jeden einzelnen PChar aufrufen?

Du kannst genausogut einen einzigen Speicherblock allokieren, der groß genug für alle Strings inklusive Nullterminator (einen für jeden String, logischerweise) ist, aller Strings der Reihe nach (inklusive Nullterminator) reinkopieren und die Pointer für das verwendete Record passend setzen. Dürfte wahrscheinlich sogar effizienter beim Speicher sein, es ist aber mindestens schneller, weil nur ein Speicherblock angefordert wird.

Zitat:

Zitat von Spider
Wo müsste ich das dann aufrufen? In der DLL oder in meinem Programm?

Zitat:

Zitat von Frickeldrecktuxer_TM
Ich weiß nicht genau, wie GetMem() sich über Module-Grenzen hinweg verhält, aber eigentlich sollte es damit gehen, um in der DLL Speicher zu allokieren, den der Hauptprozess sieht.

Probier's aus und schau im Debugger, ob der Speicher wieder freigegeben wird, wenn das Modul finalisiert wird.

Zitat:

Zitat von Spider
Könnte man nicht die Records für DLL -> Programm mit PChar machen, im Programm dann aber als String speichern?

Kannste auch, dann brauchst du halt zwei Records, überlässt dafür aber dem Compiler das Kopieren des Speichers (Stimmt das? Schau zur Sicherheit lieber, ob beim Zuweisen UniqueString() aufgerufen wird). Ist genau das gleiche in grün, nur daß vorne noch 8 Byte extra für RefCount und StrLen stehen.
Für mich wäre es logischer, es bei einem Record zu belassen und meine Strings selber zu kopieren.

Zitat:

Zitat von Spider
Delphi-Quellcode:
{ TLanguageDLLCommunicator }

procedure TLanguageDLLCommunicator.SetFileName(const Value: String);
begin
  try
    FreeLibrary(FHandle);
    FOpened := false;
  except
    { Do nothing } // argh
  end;

  FFileName := Value;

  try
    FHandle := LoadLibrary(PChar(Value));

    if FHandle = 0 then
    begin
      raise Exception.Create('Can not load languagedll'#13#10+Value);
    end;

    FOpened := true;
  except
    raise Exception.Create('Can not load languagedll'#13#10+Value); // argh
  end;
end;

Was ist das denn bitte? Die mit "argh" gekennzeichneten Stellen sind genau das, wie man's nicht macht.
Hinweis: Calls ins Win32-API erzeugen keine Exceptions. Wenn "FOpened := False" oder "FreeLibrary(FHandle)" eine Exception werfen, hast du ganz andere Probleme, die du auf keinen Fall mit einem leeren Except-Block zu schlucken hast. Genauso verhält es sich mit dem zweiten "argh". Dein Code sollte keine Exception verursachen, außer deine eigene. Die fängst du ab und wirfst sie nochmal? Lass den try-Müll da weg, der hat an dieser Stelle nichts zu suchen. Wenn das Exception-Handling nötig wird, hast du ein so massives Problem, daß es einfacher sein wird, dein System neu zu installieren, als nach einer Fehlerursache zu forschen.

Die Muhkuh 24. Apr 2006 19:06

Re: DLL: Record aus DLL holen
 
:oops:

Ok, hast ja recht... Ich werde es verbessern ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz