Link to home
Start Free TrialLog in
Avatar of plumothy
plumothyFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Delphi DLL for Interbase UDF

I am having trouble with a Delphi DLL in which I declare a function for use as a UDF in Interbase.

Here is my DLL code:
library TestIBFunctions;

uses
  System.SysUtils,
  System.Classes,
  ib_util;

{$R *.res}

function GetGUID: PAnsiChar; cdecl; export;
var
  guid: TGUID;
begin
  result := ib_util_malloc(16);
  CreateGUID(guid);
  Move(guid, result^, SizeOf(guid));
end;

(* example function from embardadero web site *)
function Left(sz: PChar; var Cnt: Integer): PChar; export;
var
  i: Integer;
begin
  if (sz = nil) then
    result := nil
  else begin
    i := 0;
    while ((sz[i] <> #0) and (i < cnt)) do Inc(i);
    result := ib_util_malloc(i+1);
    Move(sz[0], result[0], i);
    result[i] := #0;
  end;
end;

exports
  GetGUID,
  Left;

begin
end.

Open in new window


Here is my database DDL:
SET SQL DIALECT 3;

SET NAMES UTF8;

CREATE DATABASE 'LOCALHOST:E:\TestDB\TEST_DB.IB'
USER 'SYSDBA' PASSWORD 'masterkey'
PAGE_SIZE 4096
DEFAULT CHARACTER SET UTF8;

DECLARE EXTERNAL FUNCTION F_GETGUID

    RETURNS CHAR(16) CHARACTER SET OCTETS FREE_IT
    ENTRY_POINT 'GetGUID' MODULE_NAME 'TestIBFunctions';

DECLARE EXTERNAL FUNCTION F_LEFT
    CSTRING(64),
    INTEGER
    RETURNS CSTRING(64) FREE_IT
    ENTRY_POINT 'Left' MODULE_NAME 'TestIBFunctions';

Open in new window



The Left function is an example from Embarcadero. It works exactly as expected:
select f_left('abcdefg', 3) from rdb$database returns the result 'abc'.

However, the GetGUID function does not. I have done something wrong but can't see what it is.
select f_getguid() from rdb$database returns this error message:
Arithmetic overflow or division by zero has occurred.
arithmetic exception, numeric overflow, or string truncation.

I am running:
 - 64bit Windows 10 Pro
 - Delphi 10.1 Berlin Professional
 - Interbase XE7
SOLUTION
Avatar of David Johnson, CD
David Johnson, CD
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of plumothy

ASKER

Thank for for your response.

A Delphi TGUID is 16 bytes (cardinal, word, word, array[0..7] of byte). My GetGUID function returns those 16 bytes. That's what I want my Interbase UDF to see, not the Hexadecimal representation of them.
Cause you export PAnsiChar - you need extra one byte for null (terminated string). So this should work...
function GetGUID: PAnsiChar; cdecl; export;
var
  guid: TGUID;
begin
  result := ib_util_malloc(SizeOf(TGUID)+1);
  CreateGUID(guid);
  ZeroMemory(result, SizeOf(TGUID)+1);
  Move(guid, result^, SizeOf(TGUID));
end;

Open in new window

Sinisa Vuk, thank you.

I have tried your amendments to the function, but I still get the same error from Interbase.

Is the null necessary? Interbase is expecting CHAR(16), which is 16 bytes. Doesn't this mean it will read 16 bytes and no more, therefore it never sees the null 17th byte anyway?

I believe it is quite possible for any one of the 16 bytes in the GUID to be zero (ie null). If Interbase stopped reading the function result at a zero byte (ie null) then it would not read the remaining bytes.

My understanding of Interbase UDFs is that if you want to return a null-terminated string then the you declare it as returning CSTRING(nn). I have declared it as returning CHAR(16) CHARACTER SET OCTETS because I want all 16 bytes (including any nulls).

Maybe what I am trying to do is not possible?
you are expecting an automatic conversion of a GUID to a string via a memory copy ?

i wouldn't expect that much myself, but explicitly convert it to a string
and then copy that to the result


function GetGUID: PAnsiChar; cdecl; export;
var
  guid: TGUID;
  s: string;
begin
  CreateGUID(guid);
  s := GuidToString(guid);
  result := ib_util_malloc(SizeOf(s)+1);
  StrPCopy(Result, s);
end;

Open in new window


if the formatting of the guid is not to your liking, you use intToStr64/IntToStr to format the D1, D2, D3, D4 fields of the guid to your own liking
Geert Gruwez, thank you for your response.

No, I am not expecting automatic conversion to a string. In fact, I do not want a string at all. I want the 16 bytes.

Having spent several days on this, I am beginning to think it simply cannot be done.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Geert Gruwez,

The function returns PAnsiChar only as a means of getting the 16 bytes  back to Interbase.
Interbase UDFs can only accept CSTRING or  SQL data types so CHAR(16) CHARACTER SET OCTETS seemed the best bet. The database treats it as if it is a fixed length (16-byte) ascii string.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Assisted solutions were not 100% but did inspire me to keep trying and helped guide me towards finally devising my own solution.