Autor Beitrag
CenBells
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1547

Win 7
Delphi XE5 Pro
BeitragVerfasst: Do 20.01.05 13:19 
Hallo Leute,

für eine Software von mir habe ich einen Firebirdserver im Netz stehen. Dieser Firebirdserver verwaltet eine Datenbank, in der Informationen zu den Software-Updates gespeichert werden.

Die Patches sind in einem Blob-Feld gespeichert. Die Anwender laden diese Patches herunter und führen lokal das Updateprogramm aus. Da diese Patches mitunter recht groß werden können und die IBX-Komponenten selbst in Version 7.08 keine Möglichkeit bieten den Fortschritt anzuzeigen, habe ich mir für das "Herunterladen" der Patches eine Methode geschrieben, die einem das Anzeigen des Fortschritts durch eine Callback-Funktion erleichtert.

Hier das Interface und die Methode

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
uses
  Windows, SysUtils, Variants, Classes, Graphics, 
  IBHeader, IBBlob, IBIntf, IB, IBErrorcodes;

type
  TCBBlobCallBackMode = (bcbmStart, bcbmProgress, bcbmEnd);
  TCBBlobCallBack = procedure (ATotal, AReceived: Integer;
    AMode: TCBBlobCallBackMode) of Object;

//------------------------------------------------------------------------------
function cbGetBlobWithCallBack(ABlobID: TISC_Quad;
  ADBHandle: PISC_DB_Handle;
  ATRHandle: PISC_TR_Handle;
  AFileName: String; ACallBack: TCBBlobCallBack): Boolean;

...

interface

//------------------------------------------------------------------------------
function cbGetBlobWithCallBack(ABlobID: TISC_Quad;
  ADBHandle: PISC_DB_Handle;
  ATRHandle: PISC_TR_Handle;
  AFileName: String; ACallBack: TCBBlobCallBack): Boolean;
var
  LBlobHandle: TISC_BLOB_HANDLE;
  LSeg, LSize, LTotal: LongInt;
  LType: Short;
  LBuffer: PChar;
  LCurPos: LongInt;
  LBytesRead, LSegLen: Word;
  LLocalBuffer: PChar;
  LStream: TMemoryStream;
begin
  result := false;
  LBlobHandle := nil;

  // open the blob file; especially get the BlobHandle
  GetGDSLibrary.isc_open_blob2(StatusVector, ADBHandle, ATRHandle,
    @LBlobHandle, @ABlobID, 0nil);

  try
    // get the informations of the blob;
    // segment count, segment size, total size, blob type
    IBBlob.GetBlobInfo(@LBlobHandle, LSeg, LSize, LTotal, LType);

    // raise the first callback
    if assigned(ACallBack) then
      ACallBack(LTotal, 0, bcbmStart);

    // assign the variables and allocate memory
    LBuffer := nil;
    ReallocMem(LBuffer, LTotal);
    LLocalBuffer := LBuffer;
    LCurPos := 0;
    LSegLen := Word(DefaultBlobSegmentSize);
    while (LCurPos < LTotal) do
    begin
      if (LCurPos + LSegLen > LTotal) then
        LSegLen := LTotal - LCurPos;
      // receive the segments
      if not ((GetGDSLibrary.isc_get_segment(StatusVector,
                 @LBlobHandle, @LBytesRead, LSegLen, LLocalBuffer) = 0or
              (StatusVectorArray[1] = isc_segment)) then
        IBDatabaseError;
      Inc(LLocalBuffer, LBytesRead);
      Inc(LCurPos, LBytesRead);
      // raise the callback
      if assigned(ACallBack) then
        ACallBack(LTotal, LBytesRead, bcbmProgress);
      LBytesRead := 0;
    end;

    // raise the last callback
    if assigned(ACallBack) then
      ACallBack(LTotal, LBytesRead, bcbmEnd);

    // save the file
    LStream := TMemoryStream.Create;
    try
      LStream.WriteBuffer(LBuffer^, LTotal);
      LStream.SaveToFile(AFileName);
    finally
      FreeAndNil(LStream);
    end;
  finally
    // close the blob
    GetGDSLibrary.isc_close_blob(StatusVector, @LBlobHandle);
    result := true;
  end;
end;


Und hier ein Beispielaufruf und das nutzen des Callbacks.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
// ich habe auf dem Formular eine TISQL-Komponente liegen
// Die TISQL-Komponente habe ich vor dem getBlob mit ExecSQL aufgemacht
// Man kann auch TIBCUstomDataset-Komponenten verwenden
procedure TTestForm.getBlob(ADestfile: String);
begin
  // der aufruf unter verwendung von TIBSQL
  cbGetBlobWithCallBack(IBSQLUpdates.FieldByName('Update_File').AsQuad,
       IBSQLUpdates.DBHandle, IBSQLUpdates.TRHandle, ADestFile, blobCallBack);

  {// die variante mit TIBDataset
  cbGetBlobWithCallBack(IBDSUpdates.Current.ByName('Update_File').AsQuad,
    IBUpdates.DBHandle, IBUpdates.TRHandle, ADestFile, blobCallBack);}

end;


// nun noch der Callback
// zu testzwecken habe ich eine Progressbar auf das Formular gelegt
procedure TTestForm.blobCallBack(ATotal, AReceived: Integer;
    AMode: TCBBlobCallBackMode);
begin
  case AMode of 
  bcbmStart:    Progressbar1.Max   := ATotal;
  bcbmProgress: ProgressBar1.Value := AReceived;
  bcbmEnd:      ProgressBar1.Value := ATotal;
  end;
end;


Damit ist es nun möglich dem Anwender den Fortschritt beim Empfangen eines großen Blobfeldes anzuzeigen.

Gruß und viel Erfolg
Ken

_________________
Eine Klasse beschreibt die Struktur und das Verhalten einer Menge gleichartiger Objekte.