Announcement

Collapse
No announcement yet.

Verständnis Frage bei Verketteten Records

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

  • Verständnis Frage bei Verketteten Records

    Hallo,<br>
    <br>
    meine Problem ist diese records zu füllen<br>
    besonders den Zeiger von "NEXT"<br>
    wenn der "Caller" den Speicher 'Allociert'.<br>
    Diese sind so änhlich wie von der<br>
    IPHelperApi wo Windows die Record dieser<br>
    Art füllt.<br>
    <br>
    code:<br>
    <PRE>
    PADDRESS_INFO = ^TADDRESS_INFO;
    TADDRESS_INFO = record
    Next: PADDRESS_INFO;
    Address: array [0..99] of Char;
    end;

    PUSER_INFO = ^TUSER_INFO;
    TUSER_INFO = record
    Next: PUSER_INFO;
    KundenNr: DWord;
    Name: array [0..39] of Char;
    Key: array [0..7] of Char;
    FromAddressList: TADDRESS;
    ToAddressList: TADDRESS;
    end;

    function QueryKundenEx(p: PUSER;var OutBufLen: Cardinal): DWORD; stdcall;
    var ic: Integer;
    begin
    if P = nil then
    begin
    OutBufLen = 10 * SizeOf(TUser);
    //10 User sind vorhanden der Call muss p
    //initialieseren mit
    //GetMem(p,OutBufLen);
    //FillChar(p^,OutBufLen,#0;
    //und die function erneutaufrufen...
    Result := 0;
    end else
    begin
    for ic:=0 to 9 do
    begin
    p^.KundenNr := i;
    StrPCopy(p^.Name,'Kunde '+IntToStr(ic));
    StrPCopy(p^.Key,'Key'+IntToStr(ic));
    if ic <> 9 then
    begin
    P^.Next := p;
    Inc(p^.Next); //ist das so richtig?
    P := P^.Next;
    end
    //hier das füllen von FromAddressList
    //und ToAddressList
    //aber wie ist das hier mit dem
    //Speicher von next (der erste ist ja
    //int) ist der schon 'Allociert'
    //oder muss das die function
    end;
    end;
    end;
    </PRE>
    <br>
    M.f.G. H.Leesch

  • #2
    Ja, da du mit P^ immer in einen gemeinsam allozierten Block schreibst. Allerdings würde ich es anders machen.

    <pre>
    var
    N: PUser;
    begin
    N := nil;
    for I := 0 to 9 do
    begin
    P.KundenNr := I;
    StrLCopy(P.Name, ..., SizeOf(P.Name));
    StrLCopy(P.Key, ..., SizeOf(P.Key));
    if N <> nil then N.Next := P;
    N := P;
    Inc(P);
    end;
    end;<br>

    </pre>

    Nur frage ich mich warum du mit einer solchen Struktur arbeitest, wäre nicht besser:

    <pre>

    type
    PAddress = ^TAdress;
    TAddress = array[0..9] of TUser;

    procedure QueryKunden(Address: PAddress);
    begin
    if Address = nil then
    begin
    ....
    end else
    for I := 0 to 9 do
    begin
    Address[I].KundenNr := I;
    StrLCopy(Address[I].Name, ..., ...);
    StrLCopy(Address[I].Key, ..., ...);
    end;
    end;
    </pre>

    Gruß Hage

    Comment


    • #3
      Hallo,<br>
      <br>
      das for i:=0 to 9 ist nur im diesem Beispiel so später ist das ganze Dynamisch und die Daten kommen aus ein SQL DB, die Funktion ist in einer Dll, die SQL DBsoll mal Interbase oder Oracle sein.
      <pre>
      die DLL

      function QueryKunden(p: PUSER;var OutBufLen: Cardinal): DWORD; stdcall;
      var Add: pADDRESS;
      NAdd: PAddress;
      NUser: PUser;
      iSize: Cardinal;
      i: Integer;
      begin
      Lock;
      iSize := 0;
      try

      try
      if hlCheckConnect then
      begin
      IB_T_Main.Active := true;
      with IB_SQL do
      begin
      SQL.Text := QueryKundenCountSQLText;
      ExecQuery;
      if not Eof then iSize := FieldByName('COUNT').AsInteger * SizeOf(TUSER);
      Close;
      end;
      IB_T_Main.Commit;
      end;
      except
      on E: Exception do SetError(E.Message);
      end;

      if (p = nil) or (iSize < OutBufLen) then
      begin
      OutBufLen := iSize;
      Result := BUFFER_OVER_FLOW;
      end else
      begin
      i := 0;
      try
      if hlCheckConnect then //pruefen ob SQL Server laeuft
      begin
      IB_T_Main.Active := true;
      IB_SQL.SQL.Text := QueryKundenSQLText;
      IB_SQL.ExecQuery;
      NUser := Nil;
      while not IB_SQL.Eof do
      begin
      Inc(i);
      p^.KundenNr := IB_SQL.FieldByName('NR').AsInteger;
      StrLCopy(p^.Name,PChar(IB_SQL.FieldByName('NAME'). AsString),MAX_NAME);
      StrLCopy(p^.Key,PChar(IB_SQL.FieldByName('REGKEY') .AsString),MAX_KEY);

      IB_SQL_F.SQL.Text := QueryKundenFromSQLText+IntToStr(p^.KundenNr);
      IB_SQL_F.ExecQuery;
      Add := @p^.FromAddressList;
      NAdd := nil;
      while not IB_SQL_F.Eof do
      begin
      StrLCopy(Add^.Address,PChar(IB_SQL_F.FieldByName(' EMAIL').AsString),MAX_ADDRESS);
      IB_SQL_F.Next;
      if NAdd <> nil then NAdd^.Next := Add;
      if not IB_SQL_F.Eof then
      begin
      NAdd := Add;
      Add := AllocMem(SizeOf(TAddress));
      FillChar(Add^,SizeOf(TAddress),#0);
      end;
      // verwende ich "Inc(p)" Zeigt Add^.Next auf P^.ToAddressList
      //und es kommt im prog zu fehlermeldungen
      //
      end;
      IB_SQL_F.Close;

      IB_SQL_T.SQL.Text := QueryKundenToSQLText+IntToStr(p.KundenNr);
      IB_SQL_T.ExecQuery;
      Add := @p^.ToAddressList;
      NAdd := nil;
      while not IB_SQL_T.Eof do
      begin
      StrLCopy(Add^.Address,PChar(IB_SQL_T.FieldByName(' EMAIL').AsString),MAX_ADDRESS);
      IB_SQL_T.Next;
      if NAdd <> nil then NAdd^.Next := Add;
      if not IB_SQL_T.Eof then
      begin
      NAdd := Add;
      Add := AllocMem(SizeOf(TAddress));
      FillChar(Add^,SizeOf(TAddress),#0);
      end;
      end;
      IB_SQL_T.Close;

      IB_SQL.Next;
      if NUser <> nil then NUser^.Next := p;
      if not IB_SQL.Eof then
      begin
      NUser := p;
      Inc(p);
      end;
      end;
      IB_SQL.Close;
      IB_T_Main.Commit;
      end;
      except
      on E: Exception do
      begin
      SetError(E.Message);
      end;
      end;
      Result := i;
      end;
      finally
      UnLock;
      end;
      end;
      </pre>

      &#10

      Comment


      • #4
        das Programm:<br>
        <br>
        <pre>
        function QueryKunden(p: PUSER;var OutBufLen: Cardinal): DWORD; stdcall; external 'sql.dll';

        procedure TForm1.Button3Click(Sender: TObject);
        var PMem,User: PUSER;
        Add: PADDRESS;
        Res: DWord;
        OutBufLen: ULONG;
        begin
        User := nil;
        OutBufLen := 0;
        Res := QueryKunden(User, OutBufLen);
        if Res = BUFFER_OVER_FLOW then
        begin
        User := AllocMem(OutBufLen);
        Res := QueryKunden(User, OutBufLen);
        if Res <> BUFFER_OVER_FLOW then
        begin
        pMem := User;
        repeat
        Memo1.Lines.Add(IntToStr(User.KundenNr)+' / '+User.Name+' / '+User.Key);
        Add := @User.FromAddressList;
        repeat
        Memo1.Lines.Add('From: '+Add.Address);
        Add := Add.Next;
        until Add = nil;
        Add := @User.ToAddressList;
        repeat
        Memo1.Lines.Add('To: '+Add.Address);
        Add := Add.Next;
        until Add = nil;
        Application.ProcessMessages;
        User := User.Next;
        until User = nil;
        ReAllocMem(pMem,0); //wird hier auch der speicher von neu Allocierten Add.Next freigegeben?
        end;
        end;
        end;</pre>
        <br>
        <br>
        M.f.G. H.Leesch<br>
        <br>
        ps bitte kommentare lesen im source..

        Comment


        • #5
          Ah, jetzt wirds kritisch.<br>
          Also du hast z.b. mit GetMem() einen Speicherblock alloziert der 10 Kunden fassen kann. Dann speicherst du diese Kunden in diesen Block und verknüpfst sie über ^.Next. Soweit so gut. Aber im nachhinein nutzt du ReallocMem() um z.b. 20 Kundendaten in diesen Record zu speichern. Der Speicherblock wird nun doppelt so groß und mit sehr hoher Wahrscheinlichkeit an eine andere Speicheradresse kopiert. In diesem Moment stimmen die ^.Next Zeiger NICHT mehr !!<br>
          Im Falle meines dynamischen Arrays können dort keine Fehler entstehen.

          Da in den TUser Records zusätzliche Zeiger auf die Adressen sind, und diese per eigenem AllocMem() alloziert wurden, müssen diese auf separat mit FreeMem() oder ReallocMem(,0); freigegeben werden.<br>

          Ich rate dir eine Pascal konformere Struktur zu verwenden. Solche Zeiger-Verwirrspiele bekannt aus C/C++ sind extrem schlecht zu supporten.<br>

          Angenommen du nutzt D5,D5 oder D7 dann nutzte dynamische Arrays, etwa so:

          <pre>

          type
          TAddress = packed record
          ...
          end;<bR>

          TAddressArray = array of TAddress;<br>

          TUser = packed record
          ...
          FromAddress: TAddressArray;
          ToAdress: TAddressArray;
          end;<br>

          TUserArray = array of TUser;<br>

          function AddUser(var Users: TUserArray): Integer;
          // hängt einen neuen TUser an Users ran und gibt Index darauf zurück
          begin
          Result := Length(Users);
          SetLength(Users, Result +1);
          end;<br>

          function AddAddress(var Addresses: TAddressArray): Integer;
          begin
          Result := Length(Addresses);
          SetLength(Addresses, Result +1);
          end;<br>

          procedure WorkOnUser(var Users: TUserArray);
          begin
          for I := Low(Users) to High(Users) do
          for J := Low(Users[I].FromAdress) to High(Users[I].FromAddress) do .....

          I := AddUser(Users);
          Users[I].KundenNr := ....;
          J := AddAddress(Users[I].FromAddress);
          Users[I].FromAddress[J].Name := ...;
          end;<br>

          </pre>

          falls dich das Ansprechen über die Indizes stört, kann du auch mit Zeigern auf diese Arrays arbeiten. <br>

          <Pre>

          PUser := @Users[0];
          PAssress = @PUser.FromAdress[0];
          Inc(PUser);
          Inc(PAddress);

          </pre>

          sind also möglich.

          Der Unterschied dabei ist das der Compiler dafür sorgt die Arrays + Unterarrays wieder freizugeben.

          Gruß Hage

          Comment


          • #6
            Achso, die Record-Elemente kannste dann auch als String, eg. LongString, definieren. Auch diese dyn. Strings werden dann vom Compiler freigegeben.<br>

            Solche verketteten Records machen nur dann Sinn wenn,
            <li>Geschwindigkeit enorm wichtig ist, und sehr viele Record-Einfüge/Entfernen Operationen aufreten
            <li>man Shunkweise Records allozieren will um die Speichermanageraufrufe zu reduzieren
            <li>man nicht einfachverkettete Records benötigt, also z.b. Bäume/Tries oder schnelle Suchlisten benötigt.<br>

            In deinem Falle scheint dies aber nicht so zu sein. Für durchschnittlich 1000 solcher Records ist ein solcher Aufwand nicht nötig, da dort keine großen Performancegewinne zu erwarten sind. Es müssten schon 100.000 solcher records permanent allosziert/realloziert/umsortiert usw. werden.

            Gruß Hage

            Comment


            • #7
              Hallo,

              danke für die antwort werde darüber nachdenken. Mich wurde aber Interesieren wie ich das ganze in Delphi realiesiere und es nur mit einem FreeMem freigebe.

              M.f.G. H. Leesc

              Comment


              • #8
                Hallo,<br>
                <br>
                habe ein biz denke... die Dll<br>
                <br>
                so geht das ganze..<br>
                <pre>
                function QueryKundenEx(p: PUSER;var OutBufLen: Integer): DWORD; stdcall;
                var NextFree: PADDRESS;
                Add: pADDRESS;
                NAdd: PAddress;
                NUser: PUser;
                iKunden: Integer;
                iAddFrom: Integer;
                iAddTo: Integer;
                iSize: Integer;
                i: Integer;
                begin
                Lock;
                iSize := 0;
                iKunden := 0;
                try

                try
                if hlCheckConnect then
                begin
                IB_T_Main.Active := true;
                with IB_SQL do
                begin
                SQL.Text := QueryKundenCountSQLText;
                ExecQuery;
                if not Eof then iKunden := FieldByName('COUNT').AsInteger;
                iSize := iKunden * SizeOf(TUSER);
                Close;
                end;
                with IB_SQL do
                begin
                SQL.Text := QueryAddFromCountSQLText;
                ExecQuery;
                if not Eof then iAddFrom := FieldByName('COUNT').AsInteger - iKunden; //-1 pro Kunden
                iSize := iSize + (iAddFrom * SizeOf(TADDRESS));
                Close;
                end;
                with IB_SQL do
                begin
                SQL.Text := QueryAddToCountSQLText;
                ExecQuery;
                if not Eof then iAddTo := FieldByName('COUNT').AsInteger - iKunden; //-1 pro kunde
                iSize := iSize + (iAddTo * SizeOf(TADDRESS));
                Close;
                end;
                IB_T_Main.Commit;

                end;
                except
                on E: Exception do SetError(E.Message);
                end;

                if (p = nil) or (iSize < OutBufLen) then
                begin
                OutBufLen := iSize;
                Result := BUFFER_OVER_FLOW;
                end else
                begin
                i := 0;
                try
                if hlCheckConnect then
                begin
                Inc(p,iKunden-1);//Letzten benoetigten Zeiger ermitteln
                NextFree := @p^.ToAddressList;
                Dec(p,iKunden-1);
                Inc(NextFree); //Ersten freien zeiger (Speicher) zuweisen

                IB_T_Main.Active := true;
                IB_SQL.SQL.Text := QueryKundenSQLText;
                IB_SQL.ExecQuery;
                NUser := Nil;
                while not IB_SQL.Eof do
                begin
                Inc(i);
                p^.KundenNr := IB_SQL.FieldByName('NR').AsInteger;
                StrLCopy(p^.Name,PChar(IB_SQL.FieldByName('NAME'). AsString),MAX_NAME);
                StrLCopy(p^.Key,PChar(IB_SQL.FieldByName('REGKEY') .AsString),MAX_KEY);

                IB_SQL_F.SQL.Text := QueryKundenFromSQLText+IntToStr(p^.KundenNr);
                IB_SQL_F.ExecQuery;
                Add := @p^.FromAddressList;
                NAdd := nil;
                while not IB_SQL_F.Eof do
                begin
                StrLCopy(Add^.Address,PChar(IB_SQL_F.FieldByName(' EMAIL').AsString),MAX_ADDRESS);
                IB_SQL_F.Next;
                if NAdd <> nil then NAdd^.Next := Add;
                if not IB_SQL_F.Eof then
                begin
                NAdd := Add;
                Add := NextFree; //ersten freien speicher zuwiesen
                //hinter den 10 Usern...
                Inc(NextFree);
                end;
                end;
                IB_SQL_F.Close;

                IB_SQL_T.SQL.Text := QueryKundenToSQLText+IntToStr(p.KundenNr);
                IB_SQL_T.ExecQuery;
                Add := @p^.ToAddressList;
                NAdd := nil;
                while not IB_SQL_T.Eof do
                begin
                StrLCopy(Add^.Address,PChar(IB_SQL_T.FieldByName(' EMAIL').AsString),MAX_ADDRESS);
                IB_SQL_T.Next;
                if NAdd <> nil then NAdd^.Next := Add;
                if not IB_SQL_T.Eof then
                begin
                NAdd := Add;
                </pre&gt

                Comment


                • #9
                  <pre>
                  Add := NextFree;
                  Inc(NextFree);
                  end;
                  end;
                  IB_SQL_T.Close;

                  IB_SQL.Next;
                  if NUser <> nil then NUser^.Next := p;
                  if not IB_SQL.Eof then
                  begin
                  NUser := p;
                  Inc(p);
                  end;
                  end;
                  IB_SQL.Close;
                  IB_T_Main.Commit;
                  end;
                  except
                  on E: Exception do
                  begin
                  SetError(E.Message);
                  MessageBox(0,PChar(E.Message),'error',0);
                  end;
                  end;
                  Result := i;
                  end;
                  finally
                  UnLock;
                  end;
                  end;
                  </pre>

                  Nochmals danke ... werde mir deiner Hinweise annehmen. Wollte nur wissen wies funkts....<br>
                  H.Leesc

                  Comment

                  Working...
                  X