Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Erklärung zu Code gesucht... (https://www.delphipraxis.net/80086-erklaerung-zu-code-gesucht.html)

TheMiller 2. Nov 2006 19:04


Erklärung zu Code gesucht...
 
Hallo,

ich habe hier einen Code zum Ermitteln des WinDirs. Der funzt prima. Doch ich verstehe nicht, wie er das Verzeichnis letztenendes bekommt, bzw was er macht. Möchte auch wissen, WAS ist programmiere *g*

Delphi-Quellcode:
function GetWinDir: String;
const
  UNLEN = MAX_PATH;
var
  Size: DWORD;
begin
  Size := UNLEN + 1;
  SetLength(Result, Size);
  if GetWindowsDirectory(PChar(Result), Size) <> 0 then
    SetLength(Result, Size)
  else
    Result := '';
end;
Danke!

DocE 2. Nov 2006 19:11

Re: Erklärung zu Code gesucht...
 
N'Abend,

was genau verstehst Du denn nicht?

Grüsse
...Doc

Namenloser 2. Nov 2006 19:13

Re: Erklärung zu Code gesucht...
 
GetWindowsDirectory(PChar(Result), Size)

Müsste wohl der teil sein, der den Windows-Verzeichnisnamen ins Result kopiert. Weil die WinAPI ja nicht mit strings sondern mit pchars arbeitet, steht da noch die maximale länge des strings. (denk ich mal mit meinem halbwegs gesunden Menschenverstand *g*)

Allerdings versteh ich nicht ganz, wieso da zweimal "setlength" steht o.O

TheMiller 2. Nov 2006 19:14

Re: Erklärung zu Code gesucht...
 
Sagen wir es mal so...

Der Befehl "GetWindowsDirectory" ist eine Funktion aus der KernlDll, oder? Aber wieso brauche ich dazu ein array?
Und die Frage, die jetzt neu in den Raum kam, das verstehe ich auch nich!

DocE 2. Nov 2006 19:19

Re: Erklärung zu Code gesucht...
 
Es wird ja kein Array benutzt. Die Länge des Strings (result) wird auf die maximal mögliche Länge eines Pfadnamens + 1 gesetzt (und damit der Speicher reserviert). Vereinfacht könnte man auch schreiben:

Delphi-Quellcode:
function GetWinDir: String;
var
  Size: DWORD;
begin
  Size := MAX_PATH + 1;
  SetLength(Result, Size);
  if GetWindowsDirectory(PChar(Result), Size) <> 0 then
    SetLength(Result, Size) // kann meines Erachtens weg
  else
    Result := '';
end;
Dann wird mit PChar(Result) die Adresse des Strings (im Speicher) an die Funktion "GetWindowsDirectory" übergeben, damit die dort den Pfad hineinschreiben kann.

Und eigentlich müsste es auch so klappen:

Delphi-Quellcode:
function GetWinDir: String;
var Size: DWORD;
begin
  Size := MAX_PATH + 1;
  SetLength(Result, Size);
  if GetWindowsDirectory(PChar(Result), Size) = 0 then
    Result := '';
end;

Grüsse
...Doc

NicoDE 2. Nov 2006 19:22

Re: Erklärung zu Code gesucht...
 
Zitat:

Zitat von DJ-SPM
Der funzt prima.

Nö, er erzeugt einen String mit vielen #0 am Ende.
Es sollte eher so aussehen:
Delphi-Quellcode:
function GetWinDir(): string;
begin
  // Reserviere direkt im Ergebnis den Speicherplatz für die Ausgabe der API-Funktion
  SetLength(Result, MAX_PATH);
  // Die API-Funktion füllt das Ergebnis und gibt die Länge zurück
  SetLength(Result, GetWindowsDirectory(PChar(Result), Length(Result)));
end;

TheMiller 2. Nov 2006 19:24

Re: Erklärung zu Code gesucht...
 
Hm, und wenn ich diese Funktion jetzt in eine DLL packen will, nehme ich in der Deklaration PChar statt String. Doch dann kommt die Meldung

Zitat:

Zitat von Böser Compiler
Konstantenobjekt kann nicht als Var-Parameter weitergegeben werden

Ist ja logisch, weil für den PChar ein reservierter Speicher erstellt wurde und der kann nicht variabel weitergegeben werden. Doch wie kann ich die Funktion jetzt doch in der DlL nutzen?

Danke! Den Code habe ich jetzt verstanden!

@NicoDE: Das heabe ich jetzt auch gesehen. Habe ihn direkt aus dem Post von Luckie benutzt!

TheMiller 16. Nov 2006 17:03

Re: Erklärung zu Code gesucht...
 
Ich habe immernoch das Problem, dass die Fehlermeldung

Konstantenobjekt kann nicht als Var-Parameter weitergegeben werden

ausgegeben wird, aber nur, wenn die Funktion in der DLL steht. Bräuchte dazu nochmal Hilfe...

Hier nochmal die Funktion:

Delphi-Quellcode:
function GetWinDir(): string;
begin
  // Reserviere direkt im Ergebnis den Speicherplatz für die Ausgabe der API-Funktion
  SetLength(Result, MAX_PATH);
  // Die API-Funktion füllt das Ergebnis und gibt die Länge zurück
  SetLength(Result, GetWindowsDirectory(PChar(Result), Length(Result)));
end;

marabu 16. Nov 2006 17:36

Re: Erklärung zu Code gesucht...
 
Bei mir lässt sich der Code in einem neuen DLL-Projekt problemlos kopieren.

Grüße vom marabu

TheMiller 16. Nov 2006 17:41

Re: Erklärung zu Code gesucht...
 
Delphi-Quellcode:
library SysCtrl;

{ Wichtiger Hinweis zur DLL-Speicherverwaltung: ShareMem muss sich in der
  ersten Unit der unit-Klausel der Bibliothek und des Projekts befinden (Projekt-
  Quelltext anzeigen), falls die DLL Prozeduren oder Funktionen exportiert, die
  Strings als Parameter oder Funktionsergebnisse übergeben. Das gilt für alle
  Strings, die von oder an die DLL übergeben werden -- sogar für diejenigen, die
  sich in Records und Klassen befinden. Sharemem ist die Schnittstellen-Unit zur
  Verwaltungs-DLL für gemeinsame Speicherzugriffe, BORLNDMM.DLL.
  Um die Verwendung von BORLNDMM.DLL zu vermeiden, können Sie String-
  Informationen als PChar- oder ShortString-Parameter übergeben. }


uses
  ShareMem,
  Windows,
  Registry,
  SysUtils,
  Dialogs,
  Classes,
  ShellAPI;

{$R *.res}

function GetWinDir(): PChar; stdcall;
begin
  // Reserviere direkt im Ergebnis den Speicherplatz für die Ausgabe der API-Funktion
  SetLength(Result, MAX_PATH);
  // Die API-Funktion füllt das Ergebnis und gibt die Länge zurück
  SetLength(Result, GetWindowsDirectory(PChar(Result), Length(Result)));
end;

exports
  GetWinDir;

begin
end.
Zitat:

Zitat von Compiler
[Fehler] SysCtrl.dpr(28): Konstantenobjekt kann nicht als Var-Parameter weitergegeben werden
[Fehler] SysCtrl.dpr(30): Konstantenobjekt kann nicht als Var-Parameter weitergegeben werden

Das sagt er bei mir. Delphi 7 Enterprise WindowsXP Prof.

Win32.API 16. Nov 2006 17:48

Re: Erklärung zu Code gesucht...
 
Versuchs mal so:

Delphi-Quellcode:
function GetWinDir(): PChar; stdcall;
var tmp : String;
begin
  SetLength(tmp, MAX_PATH);
  SetLength(tmp, GetWindowsDirectory(PChar(tmp), MAX_PATH));
  result := pchar(tmp);
end;
--win32

TheMiller 16. Nov 2006 17:51

Re: Erklärung zu Code gesucht...
 
Das funktioniert! Nur warum muss ich eine Zwischenvariable einfügen? Danke, gell!

Win32.API 16. Nov 2006 17:57

Re: Erklärung zu Code gesucht...
 
Weil intern der Speicher fuer dich von Delphi reserviert und freigegeben wird.

Man koennte das Ganze auch per Hand machen und mit getmem und freemem dem Speicher selber reservieren.

TheMiller 16. Nov 2006 17:59

Re: Erklärung zu Code gesucht...
 
Okay!

Danke euch allen

marabu 16. Nov 2006 18:01

Re: Erklärung zu Code gesucht...
 
Hallo du Schelm,

Zitat:

Zitat von DJ-SPM
Nur warum muss ich eine Zwischenvariable einfügen?

du hast ab Beitrag #10 einfach kühn die Signatur deiner Funktion geändert!

Freundliche Grüße

TheMiller 16. Nov 2006 18:04

Re: Erklärung zu Code gesucht...
 
*fg*

Joa, hatte aber damit nix zu tun. Hatte es an allen Stellen geändert. Die Änderungen kamen durch verschiedene Tests. Daher.

DP-Maintenance 16. Nov 2006 18:08

DP-Maintenance
 
Dieses Thema wurde von "Phoenix" von "Programmieren allgemein" nach "Windows API / MS.NET Framework API" verschoben.
Dat geht um API

ste_ett 16. Nov 2006 18:10

Re: Erklärung zu Code gesucht...
 
Einen PChar als Rückgabewert zu benutzen, kann böse ins Auge gehen. :)
Der Speicher für die vier Byte für den Pointer sind zwar reserviert, nicht aber der Speicherbereich, auf den der PChar in deinem Fall verweisst.
Bei einem String ist das kein Problem, aber bei PChar kann es zu Fehlern führen, da der Speicher sofort wieder überschrieben werden kann. :)

Entweder allokierst du in der Funktion Speicher, der hinterher wieder freigegeben wird, oder du übergibst einen PChar mit geung Speicher. MAX_PATH wird hier von MS empfohlen.

Delphi-Quellcode:
function GetWinDir(WinDirPath: PChar): UINT;
begin
  Result := GetWindowsDirectory(@WinDirPath[0], MAX_PATH);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Dir: array[0..MAX_PATH-1] of char;
begin
  if (GetWinDir(@Dir[0]) >0) then
  begin
    ShowMessage(Dir);
  end
  else
  begin
    ShowMessage(SysErrorMessage(GetLastError));
  end;
end;
Durch den statischen Array fällt zusätzlich noch das manuelle Allokieren von Speicher weg, das macht der Compiler für uns. :)

TheMiller 16. Nov 2006 18:14

Re: Erklärung zu Code gesucht...
 
Also, die Funktion funktioniert einwandtfrei, so wie ich sie habe. Auch der Speicher sollte doch durch SetLength erst maximiert, dann auch die größe des Strings "zugeschnitten" werden. Und einen PChar kann ich doch bedenkenlos aus der DLL exportieren, oder nicht?

Also hier klappt alles!

ste_ett 16. Nov 2006 18:16

Re: Erklärung zu Code gesucht...
 
Zitat:

Zitat von DJ-SPM
Und einen PChar kann ich doch bedenkenlos aus der DLL exportieren, oder nicht?

Also hier klappt alles!

Nein, das geht nicht. :)
S. meinen Post oben.

Es kann gut gehen, aber solche Stellen sidn Fehlerquellen, wo man sich z.B. bei Multi-Threading tot sucht. :)

Luckie 16. Nov 2006 18:17

Re: Erklärung zu Code gesucht...
 
Zitat:

Zitat von DJ-SPM
Und einen PChar kann ich doch bedenkenlos aus der DLL exportieren, oder nicht?

Siehe heir: http://www.michael-puff.de/Developer...ring_DLL.shtml

TheMiller 16. Nov 2006 18:26

Re: Erklärung zu Code gesucht...
 
Ok, hab grad dein verlinktes TUT gelesen.

Also habe ich immer einen Buffer, der erst nil ist, dann kommt ein Integer-Wert ins Spiel, der sagt, wie groß mein String ist (len := GetWindowsDirectory .....), danach reserviere ich genau soviel Speicher, wie len groß ist und kopiere den String in den Buffer, exportiere String(Buffer) und gebe den Speicher frei.

Richtig?

ste_ett 16. Nov 2006 20:10

Re: Erklärung zu Code gesucht...
 
Zitat:

Zitat von DJ-SPM
Ok, hab grad dein verlinktes TUT gelesen.

Also habe ich immer einen Buffer, der erst nil ist, dann kommt ein Integer-Wert ins Spiel, der sagt, wie groß mein String ist (len := GetWindowsDirectory .....), danach reserviere ich genau soviel Speicher, wie len groß ist und kopiere den String in den Buffer, exportiere String(Buffer) und gebe den Speicher frei.

Richtig?

Das ist eine Möglichkeit.
Die Andere wäre mein Beispiel-Code im Post oben. :)

TheMiller 16. Nov 2006 20:13

Re: Erklärung zu Code gesucht...
 
Ok, wollte nur wissen, ob ich Luckies Tut verstanden habe (vom Prinzip - hab ich doch, oder?)

Aber auf diese Weise ist es schon ordenticher zu programmieren. Das stimmt schon - obwohl es mehr Arbeit ist...


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:30 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