Announcement

Collapse
No announcement yet.

Probleme mit Array of PChar

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Probleme mit Array of PChar

    Hallo!

    Ich habe ein paar merkwürdige Probleme mit dynamischen Arrays vom Typ PChar.

    Ich überge an eine Funktion in einer DLL ein dynamisches Array vom Typ PChar mit Parametern:

    TDynArray = array of PChar;

    function SetParameters(pParameters: TDynArray): Boolean; cdecl; begin
    Parameters[0] := pParameters;
    result := True;
    end;

    Parameters ist ein globales Array in der DLL, was wie folgt deklariert ist:

    TParameters = array [0..1] of TDynArray;
    Parameters: TParameters;

    In Feld 0 werden die Parameter gespeichert; in Feld 1 die entsprechenden Beschreibungen zu den Parametern.
    Wenn ich allerdings nun die Funktion SetParameters mit einem Array von Parametern aufrufe und mir danach die neuen Parameter per

    function GetParameters: TParameters; cdecl;
    begin
    result := Parameters;
    end;

    zurückgeben lasse, dann scheint immer nur der erste Buchstabe eines Parameters gespeichert zu werden, da result[0][0] = 'b' ist, obwohl es per SetParameters eigentlich auf 'bla' gesetzt worden ist.

    Zusammenfassung:
    -----------------------
    Deklaration:
    TDynArray = array of PChar;
    TParameters = array [0..1] of TDynArray; // 0 -> parameter; 1 -> beschreibungen

    DLL:
    Parameters: TParameters;

    function SetParameters(pParameters: TDynArray): Boolean; cdecl; begin
    Parameters[0] := pParameters;
    result := True;
    end;

    function GetParameters: TParameters; cdecl;
    begin
    result := Parameters;
    end;

    Main program:
    pParameters: TDynArray;
    Parameters: TParameters;

    // set parameter - in this example only one
    EditParams.Text := 'bla';
    SetLength(pParameters, 1);
    pParameters[0] := PChar(EditParams.Text);
    SetParameters(pParameters);

    // get parameter
    Parameters := GetParameters;
    EditParams.Text := Parameters[0][0];
    // ->> EditParams.Text = 'b';
    ------------------------------

    Wenn ich im begin/end Teil der DLL Parameters[0][0] := 'test' setze, dann bekomme ich auch 'test' von GetParameters zurückgegeben. Es liegt also wahrscheinlich an der SetParameters Funktion.

    Ich hoffe jemand versteht mein Problem und kennt evtl. eine Lösung. Bin über jede Hilfe dankbar.

    Christian

  • #2
    Hallo,

    die Ursache für dieses Problem hat weniger mit der DLL zu tun, als mit den Besonderheiten für ein PChar. Das Problem ist auch in einem ganz normalen Programm (ohne DLL) reproduzierbar (wenn auch das Ergebnis dem Zufall überlassen ist). Erst dann, wenn man sich nicht über den harten Typcast via PChar(EditParams.Text) ins Knie schiesst, sondern den Speicherbereich explizit anfordert, dessen Zeiger gespeichert wird, verschwindet das Problem:
    <pre>
    type
    TDynArray = array of PChar;
    TParameters = array [0..1] of TDynArray;

    var
    Parameters: TParameters;

    function SetParameters(pParameters: TDynArray): Boolean;
    begin
    Parameters[0] := pParameters;
    result := True;
    end;

    function GetParameters: TParameters;
    begin
    result := Parameters;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    pParams : TDynArray;
    pTxt : PChar;
    begin
    EditParams.Text := 'bla';
    SetLength(pParams, 1);
    pTxt := StrAlloc(Length(EditParams.Text) + 1);
    StrPCopy(pTxt, EditParams.Text + #0);
    pParams[0] := pTxt;
    SetParameters(pParams);
    EditParams.Text := 'Kopiert.';
    end;

    procedure TForm1.Button2Click(Sender: TObject);
    var
    aParam : TParameters;
    begin
    aParam := GetParameters;
    EditParams.Text := aParam[0][0];
    end;
    </pre>
    P.S: Das Freigeben des angeforderten Speicherbereichs über StrDispose fehlt in dem Beispiel noch. In meinem Buch <i>Delphi Win32-Lösungen</i> kümmert sich das komlette Kapitel 3 auf den Seiten 87 bis 113 um dieses Thema

    Comment


    • #3
      Hallo,

      Danke für die Antwort!
      Der Teil, wo SetParameters aufgerufen wird hat mir sehr geholfen.

      Zu der SetParameters Funktion sollte vielleicht noch gesagt werden, dass man sie besser so abändert, da man sonst div. Zugriffsverletzungen erhält (hatte ich jedenfalls sonst):

      <pre>
      function SetNeededParameters(pParameters: TDynArray): Boolean; cdecl;
      var
      a: Integer;

      begin
      for a := 0 to Length(pParameters) - 1 do begin
      Parameters[0][a] := StrAlloc(StrLen(pParameters[a]) + 1);
      StrCopy(Parameters[0][a], pParameters[a]);
      end;

      result := True;
      end;
      </pre>

      Für mich stellt sich nun die Frage, an welchen Stellen der Speicher per StrDispose noch freigegeben werden muss?
      In der SetParameters Funktion Speicher freizugeben geht anscheinend nicht bzw. scheint damit auch nicht nötig zu sein. Ich bekomme jedenfalls Zugriffsverletzungen im späteren Programmverlauf, wenn ich den Speicher der PChars im pParameters Array freigebe.
      Nach dem Aufruf der SetParameters Funktion scheint das freigeben des Speichers wohl richtig zu sein:

      <pre>
      SetLength(Parameters, 1);
      Parameters[0] := StrAlloc(Length(EditSetParams.Text) + 1);
      StrPCopy(Parameters[0], EditSetParams.Text + #0);

      if not SetNeededParameters(Parameters) then
      ShowMessage('could not set parameters');

      for a := 0 to Length(Parameters) - 1 do begin
      StrDispose(Parameters[a]);
      end;
      </pre>

      Ist das überhaupt nötig bzw. so richtig?
      Muss man noch irgendwas Wichtiges bei StrDispose beachten?
      Hab das angesprochene Buch leider nicht und kann es mir momentan auch nicht leisten. (bin Zivi ;-))

      Vielen Dank nochmal für die Hilfe

      Comment

      Working...
      X