Subject ISC_SERVICE_QUERY doesn't give me isc_info_truncated
Author Bambang P
Hello,

I want to get the user names in Firebird Server by using Service API.
My code runs well except 1 problem with isc_service_query:

If my result buffer is not big enough, isc_service_query doesn't give
me isc_info_truncated (as told in documentation) but it gives
isc_info_end signal instead. This makes my routine can not detect that
the buffer is not large enough so it can take actions accordingly.

Can somebody give me some pointer whether I made a silly mistake or it
is indeed a problem?

Below is a self contained demo I write in Delphi7. If I run on my
computer it only displays SYSDBA even though there are other users.

In the code, I set the buffer to 32 bytes long which will exactly
retrieve all SYSDBA information.

I pulls the declarations needed from ibase.h, so if anybody is kind enough to try,
you can just make a form with a TButton and TMemo and pasting my code
below:

--- 8< ----

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type

// Taken from IBASE.H
{
typedef long ISC_STATUS;
#define ISC_STATUS_LENGTH 20
typedef ISC_STATUS ISC_STATUS_ARRAY[ISC_STATUS_LENGTH];
}
ISC_STATUS = Longint;
ISC_STATUS_ARRAY = array[0..19] of ISC_STATUS;

{
typedef FRBRD * isc_svc_handle;
}
ISC_SVC_HANDLE = pointer;
PISC_SVC_HANDLE = ^ISC_SVC_HANDLE;

{
#if defined(_LP64) || defined(__LP64__) || defined(__arch64__)
typedef int ISC_LONG;
typedef unsigned int ISC_ULONG;
#else
typedef signed long ISC_LONG;
typedef unsigned long ISC_ULONG;
#endif
typedef ISC_LONG isc_resv_handle;
}

ISC_LONG = LongInt;
ISC_RESV_HANDLE = ISC_LONG;
PISC_RESV_HANDLE = ^ISC_RESV_HANDLE;


TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

const
{
#define isc_dpb_user_name 28
#define isc_dpb_password 29
#define isc_spb_user_name isc_dpb_user_name
#define isc_spb_password isc_dpb_password
#define isc_spb_current_version 2
#define isc_spb_version isc_spb_current_version
#define isc_action_svc_display_user 7 /* Displays a user record from the security database */
#define isc_info_svc_get_users 68 /* Returns the user information from isc_action_svc_display_users */
#define isc_info_end 1
#define isc_info_truncated 2

}
isc_spb_user_name : Char = #28;
isc_spb_password : Char = #29;
isc_spb_current_version : Char = #2;
isc_spb_version : Char = #2;
isc_action_svc_display_user : Char = #7;
isc_info_svc_get_users : Char = #68;
isc_info_end : Char = #1;
isc_info_truncated : Char = #2;

{/******************************************************
* Parameters for isc_action_{add|delete|modify)_user *
******************************************************/

#define isc_spb_sec_userid 5
#define isc_spb_sec_groupid 6
#define isc_spb_sec_username 7
#define isc_spb_sec_password 8
#define isc_spb_sec_groupname 9
#define isc_spb_sec_firstname 10
#define isc_spb_sec_middlename 11
#define isc_spb_sec_lastname 12
}
isc_spb_sec_userid:Char = #5;
isc_spb_sec_groupid:Char = #6;
isc_spb_sec_username:Char = #7;
isc_spb_sec_password:Char = #8;
isc_spb_sec_groupname:Char = #9;
isc_spb_sec_firstname:Char = #10;
isc_spb_sec_middlename:Char = #11;
isc_spb_sec_lastname:Char = #12;


var
Form1: TForm1;

function isc_service_attach( status_vector:ISC_STATUS_ARRAY;
service_length:Short;
service:PChar;
svc_handle:PISC_SVC_HANDLE;
spb_length:Short;
spb:PChar):ISC_STATUS;stdcall;external 'fbclient.dll';
function isc_service_detach( status_vector:ISC_STATUS_ARRAY;
svc_handle:PISC_SVC_HANDLE
):ISC_STATUS;stdcall;external 'fbclient.dll';

function isc_service_start( status_vector:ISC_STATUS_ARRAY;
svc_handle:PISC_SVC_HANDLE;
reserved:pisc_svc_handle;
spb_length:Short;
spb:PChar):ISC_STATUS;stdcall;external 'fbclient.dll';

function isc_service_query( status_vector:ISC_STATUS_ARRAY;
svc_handle:PISC_SVC_HANDLE;
reserved:PISC_RESV_HANDLE;
send_spb_length:Short;
send_spb:PChar;
request_spb_length:Short;
request_spb:PChar;
buffer_length:Short;
buffer:PChar
):ISC_STATUS;stdcall;external 'fbclient.dll';
function isc_vax_integer(
Buffer:PChar;
Length : Short
):ISC_LONG;stdcall;external 'fbclient.dll';


implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
StatusArray : ISC_STATUS_ARRAY;
ServiceName : PChar;
SPB : array[0..127] of Char;
SPBLength : Integer;
Handle : ISC_SVC_HANDLE;
Ptr : PChar;
UserNameAttach , PasswordAttach : PChar;
//
RequestSPB : Char;
UserListBuffer : array[0..32] of Char; // 33 bytes!!!
ResultLength : Integer;
//
Identifier :Char;
S : array[0..31] of Char;
SLength : Integer;
L : ISC_LONG;
begin
Self.Memo1.Lines.Clear;

//************************************
// ATTACH SERVICE MANAGER
//************************************

// Replace 192.168.0.1 with your own host.
ServiceName := '192.168.0.1:service_mgr';

// Build SPB for isc_service_attach
Ptr := SPB;
Ptr^ := isc_spb_version ; Inc(Ptr);
Ptr^ := isc_spb_current_version ; Inc(Ptr);

// User Name
UserNameAttach := 'SYSDBA';
Ptr^ := isc_spb_user_name;Inc( Ptr);
Ptr^ := Char( StrLen( UserNameAttach ) ); Inc( Ptr);
StrMove( Ptr , UserNameAttach , StrLen( UserNameAttach ) );
Ptr := Ptr + StrLen( UserNameAttach );

// Password
PasswordAttach := 'masterkey';
Ptr^ := isc_spb_password;Inc( Ptr);
Ptr^ := Char( StrLen( PasswordAttach ) ); Inc( Ptr);
StrMove( Ptr , PasswordAttach , StrLen( PasswordAttach ) );
Ptr := Ptr + StrLen( PasswordAttach );
SPBLength := Ptr - SPB;

Handle := NIL;
if isc_service_attach( StatusArray , StrLen(ServiceName),ServiceName,@Handle , SPBLength , SPB )
<> 0 then begin
raise Exception.Create('Exception on isc_service_attach, Error ' + IntToStr(StatusArray[1]));
end;

//*****************************
// START SERVICE
//********************************

// Build SPB
Ptr := SPB;
Ptr^ := isc_action_svc_display_user;Inc(Ptr);
SPBLength := Ptr - SPB;

if isc_service_start( StatusArray , @Handle , NIL , SPBLength , SPB ) <> 0
then begin
raise Exception.Create('Exception on isc_service_start, Error ' + IntToStr(StatusArray[1]));
end;

//*****************************
// QUERY RESULT
//********************************
RequestSPB := isc_info_svc_get_users;
if isc_service_query( StatusArray , @Handle , NIL , 0 , SPB , 1 , @RequestSPB,
SizeOf(UserListBuffer), UserListBuffer) <> 0 then begin
raise Exception.Create('Exception on isc_service_query, Error ' + IntToStr(StatusArray[1]));
end;

//*******************************************
// Parse the result and Display in Memo1
//******************************************
Ptr := UserListBuffer;
if Ptr^ <> isc_info_svc_get_users then begin
raise Exception.Create( 'Result Buffer not expected');
end;
Inc( Ptr );
//
ResultLength := isc_vax_integer( Ptr , SizeOf(SHORT) );
Ptr := Ptr + SizeOf( SHORT );
//
while ResultLength > 0 do begin
Identifier := Ptr^ ; Inc(Ptr); Dec(ResultLength);

if Identifier in [isc_spb_sec_username,
isc_spb_sec_firstname,
isc_spb_sec_middlename,
isc_spb_sec_lastname] then begin
SLength := isc_vax_integer( Ptr , SizeOf(SHORT ));
Inc( Ptr , SizeOf( SHORT) );
Dec( ResultLength , SizeOf( SHORT ) );
//
StrMove( S , Ptr , SLength );S[ Slength] := #0;
Inc( Ptr , SLength);
Dec( ResultLength , SLength );
//
if Identifier = isc_spb_sec_username then begin
Memo1.Lines.Add( 'USER NAME:' + String(S) );
end
else if Identifier = isc_spb_sec_firstname then begin
Memo1.Lines.Add( 'FIRST NAME:' + String(S) );
end
else if Identifier = isc_spb_sec_middlename then begin
Memo1.Lines.Add( 'MIDDLE NAME:' + String(S) );
end
else if Identifier = isc_spb_sec_lastname then begin
Memo1.Lines.Add( 'LAST NAME:' + String(S) );
end
;
end
else if Identifier in [isc_spb_sec_userid,isc_spb_sec_groupid] then begin
L := isc_vax_integer( Ptr , SizeOf( ISC_LONG ) );
Inc( Ptr , SizeOf( ISC_LONG ) );
Dec( ResultLength , SizeOf(ISC_LONG ) );
//
if Identifier = isc_spb_sec_userid then begin
Memo1.Lines.Add( 'USER_ID:' + IntToStr(L) );
end
else if Identifier = isc_spb_sec_groupid then begin
Memo1.Lines.Add( 'GROUP_ID:' + IntToStr(L) );
end
;
end
else if Identifier = isc_info_end then begin
Memo1.Lines.Add( 'End of User Name List');
if ResultLength > 0 then
raise Exception.Create( 'Unexpected End of Result');
end
else if Identifier = isc_info_truncated then begin
Memo1.Lines.Add( 'Result Buffer is not big enough!');
if ResultLength > 0 then
raise Exception.Create( 'Unexpected End of Result');
end
;
end;

// Detach Service
if isc_service_detach( StatusArray , @Handle ) <> 0 then begin
raise Exception.Create('Exception on isc_service_detach, Error ' + IntToStr(StatusArray[1]));
end;


end;

end.

--- 8< ----


--
Bambang P
http://bpranoto.tripod.com