In the recently published blog article (Query Progress Callback) I’ve showed how to implement a query callback. For that example I’ve passed a pointer of the form into the callback function, requiring a unique callback function for every Formclass to get the right typecast.
An easier way would be to write a TAdsQuery descendant component that has all required methods already implemented. So let’s begin with a new class, named TAdsQueryEx and a Callback function that casts the given pointer to an instance of that class:
TAdsQueryEx = class(TAdsQuery)
public
function QueryCallback( usPercent: word):longint;
end;
//...
// Callback used to cancel a query
{$IFDEF ADSDELPHI4_OR_NEWER}
function AdsQueryExCallback(usPercent:word; CallbackID:Int64):longint; stdcall;
begin
{$IFDEF FPC} {$HINTS OFF} {$ENDIF}
result := TAdsQueryEx(Pointer(CallbackID)).QueryCallback(uspercent);
{$IFDEF FPC} {$HINTS ON} {$ENDIF}
end;
{$ELSE}
function AdsQueryExCallback(usPercent:word; CallbackID:Integer):longint; stdcall;
begin
result := TAdsQueryEx(Pointer(CallbackID)).QueryCallback(uspercent);
end;
{$ENDIF}
In the constructor, we register that function and in the destructor we unregister ist:
constructor TAdsQueryEx.Create(AOwner: TComponent);
begin
inherited;
{$IFDEF ADSDELPHI4_OR_NEWER}
inherited AdsRegisterCallbackFunction101(@AdsQueryExCallback,Int64(self));
{$ELSE}
inherited AdsRegisterCallbackFunction(@AdsQueryExCallback,SIGNED32(self));
{$ENDIF}
end;
destructor TAdsQueryEx.Destroy;
begin
inherited AdsClearProgressCallback();
inherited;
end;
Now we need to make sure that this callback function is not being overwritten from outside of our component. Unfortunately the AdsXX API wrappers are not virtual, so a simple override does not work. We need to reintroduce the methods:
TAdsQueryEx = class(TAdsQuery)
public
procedure AdsRegisterCallbackFunction( Value : TAdsCallbackFunction; ulCallbackID : Longint ); reintroduce;
{$IFDEF ADSDELPHI4_OR_NEWER}
procedure AdsRegisterCallbackFunction101( Value : TAdsCallbackFunction101; qCallbackID : Int64 ); reintroduce;
{$ENDIF}
procedure AdsClearCallbackFunction; reintroduce;
end;
//...
procedure TAdsQueryEx.AdsRegisterCallbackFunction(Value: TAdsCallbackFunction;
ulCallbackID: Integer);
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
{$IFDEF ADSDELPHI4_OR_NEWER}
procedure TAdsQueryEx.AdsRegisterCallbackFunction101(
Value: TAdsCallbackFunction101; qCallbackID: Int64);
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
{$ENDIF}
procedure TAdsQueryEx.AdsClearCallbackFunction;
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
Finally we need some kind of event handler which is called inside the callback function.
type
TAdsQueryCallbackEvent = function (usPercent:word):Boolean of object;
TAdsQueryEx = class(TAdsQuery)
private
FOnQueryCallback: TAdsQueryCallbackEvent;
public
function QueryCallback( usPercent: word):longint;
published
property OnQueryCallback: TAdsQueryCallbackEvent read FOnQueryCallback write SetOnQueryCallback;
end;
//...
function TAdsQueryEx.QueryCallback(usPercent: word): longint;
var
bResult: Boolean;
begin
if assigned(FOnQueryCallback) then
bResult:=FOnQueryCallback(usPercent)
else bResult:=False;
if bResult then Result:=1
else Result:=0;
end;
That’s it. Let’s put it all together.
unit AdsTableEX;
interface
uses
SysUtils, Classes, windows, DB, adsdata, adsfunc, adstable, ace;
type
TAdsQueryCallbackEvent = function (usPercent:word):Boolean of object;
TAdsQueryEx = class(TAdsQuery)
private
FOnQueryCallback: TAdsQueryCallbackEvent;
procedure SetOnQueryCallback(const Value: TAdsQueryCallbackEvent);
public
constructor Create( AOwner: TComponent ); override;
destructor Destroy; override;
function QueryCallback( usPercent: word):longint;
procedure AdsRegisterCallbackFunction( Value : TAdsCallbackFunction; ulCallbackID : Longint ); reintroduce;
{$IFDEF ADSDELPHI4_OR_NEWER}
procedure AdsRegisterCallbackFunction101( Value : TAdsCallbackFunction101; qCallbackID : Int64 ); reintroduce;
{$ENDIF}
procedure AdsClearCallbackFunction; reintroduce;
published
property OnQueryCallback: TAdsQueryCallbackEvent read FOnQueryCallback write SetOnQueryCallback;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Advantage', [TAdsQueryEx]);
end;
// Callback used to cancel a query
{$IFDEF ADSDELPHI4_OR_NEWER}
function AdsQueryExCallback(usPercent:word; CallbackID:Int64):longint; stdcall;
begin
{$IFDEF FPC} {$HINTS OFF} {$ENDIF}
result := TAdsQueryEx(Pointer(CallbackID)).QueryCallback(uspercent);
{$IFDEF FPC} {$HINTS ON} {$ENDIF}
end;
{$ELSE}
function AdsQueryExCallback(usPercent:word; CallbackID:Integer):longint; stdcall;
begin
result := TAdsQueryEx(Pointer(CallbackID)).QueryCallback(uspercent);
end;
{$ENDIF}
{ TAdsQueryEx }
procedure TAdsQueryEx.AdsClearCallbackFunction;
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
procedure TAdsQueryEx.AdsRegisterCallbackFunction(Value: TAdsCallbackFunction;
ulCallbackID: Integer);
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
constructor TAdsQueryEx.Create(AOwner: TComponent);
begin
inherited;
{$IFDEF ADSDELPHI4_OR_NEWER}
inherited AdsRegisterCallbackFunction101(@AdsQueryExCallback,Int64(self));
{$ELSE}
inherited AdsRegisterCallbackFunction(@AdsQueryExCallback,SIGNED32(self));
{$ENDIF}
end;
destructor TAdsQueryEx.Destroy;
begin
inherited AdsClearProgressCallback();
inherited;
end;
{$IFDEF ADSDELPHI4_OR_NEWER}
procedure TAdsQueryEx.AdsRegisterCallbackFunction101(
Value: TAdsCallbackFunction101; qCallbackID: Int64);
begin
raise Exception.Create('This function is not available in TAdsQueryEx');
end;
{$ENDIF}
function TAdsQueryEx.QueryCallback(usPercent: word): longint;
var
bResult: Boolean;
begin
if assigned(FOnQueryCallback) then
bResult:=FOnQueryCallback(usPercent)
else bResult:=False;
if bResult then Result:=1
else Result:=0;
end;
procedure TAdsQueryEx.SetOnQueryCallback(const Value: TAdsQueryCallbackEvent);
begin
FOnQueryCallback := Value;
end;
end.