1

I Think I have a big knowledge gap to jump over but don't get the Problem yet.

In my Application I use MS SQL Server to automatic create special GUIDs. The Default Value of that Column is :

(CONVERT([binary](16),newid()))

which leads to something like that

0x0184F4422C58EA4A95B366E2F90F5973

Everything works fine, but sometimes I need to write that Binary(16) into logging File. I Have a Function that generates that GUIDs for me, feel Free to use. For Sql query I use FireDAC Query. The Function looks like that:

function GetNewGUID: variant;
const
  cCREATENEWGUID = 'select (CONVERT([binary](16),newid())) as GUID';
var
  lquery: TFDQuery;
begin
  try
    lquery := TFDQuery.Create(nil);
    try
      lquery.Connection:=MyFDConnection;
      lquery.Open(cCREATENEWGUID);
      Result := lquery.FieldByName('GUID').AsVariant;
    finally
      lquery.Free;
    end;
  Except
    on e: Exception do
      // Some logging here
  end;
end;

That gives me perfectly working Variant. But when I like to display that as String I don't get it. I tried vartostr, that gives me ???? I tried TEncoding.ANSI.GetString, that gives me some strange chars

What I get so far, my Variant is VarArray and type of Array Byte.

5
  • So basically you want to convert a binary value to its hex representation? Commented Mar 17, 2021 at 9:53
  • Yes I Think so. I would like to give that Variant to a function and would like to get the 0x0184F4422C58EA4A95B366E2F90F5973 as String Commented Mar 17, 2021 at 10:01
  • 1
    stackoverflow.com/questions/64975833/… Commented Mar 17, 2021 at 11:22
  • 1
    Why would you use a database to generate a GUID, instead of using SysUtils.CreateGUID() and SysUtils.GUIDToString(), or even SysUtils.TGUIDHelper.NewGuid().ToString()?? Commented Mar 17, 2021 at 15:27
  • @RemyLebeau you where totally right. I had some troubles with that sort of GUID. I Changed my DB-Field from binary(16) to uniqeidentifier and use the TGUID class. Everything works fine. Commented Apr 14, 2021 at 7:49

3 Answers 3

1

Here is the code you need to convert a variant containing an array of byte to a string with hexadecimal representation of that array. The code avoid an unnecessary data copy:

function VarArrayOfByteToString(V : Variant) : String;
var
    P    : PByte;
    Size : Integer;
begin
    Result := '';
    P := VarArrayLock(V);
    if P <> nil then begin
        try
            Size := (VarArrayHighBound(V, 1) -
                     VarArrayLowBound(V, 1) + 1) *
                    TVarData(V).VArray^.ElementSize;
            while Size > 0 do begin
                Result := Result + IntToHex(P^, 2);
                Inc(P);
                Dec(Size);
            end;
        finally
            VarArrayUnlock(V);
        end;
    end;
end;

Example of use:

procedure TForm21.Button1Click(Sender: TObject);
var
    V    : Variant;
    S    : String;
begin
    V := GetNewGUID;
    S := VarArrayOfByteToString(V);
    Memo1.Lines.Add(S);
end;

There is a much faster way to get a GUID! The code is:

function CreateGuid : String;
var
    Guid   : TGUID;
begin
    if CoCreateGuid(Guid) <> 0 then   // Uses Winapi.Activex
        Guid := GUID_NULL;
    Result := GUIDToString(Guid);
end;

It returns a GUID as a string in the usual format:

{B9DAFFD0-87E1-4DA0-957B-DFF34CA047EB}

Of course you may remove the dash and curly brackets:

S := CreateGUID;
Delete(S, 38, 1);
Delete(S, 25, 1);
Delete(S, 20, 1);
Delete(S, 15, 1);
Delete(S, 10, 1);
Delete(S, 1, 1);
Sign up to request clarification or add additional context in comments.

10 Comments

"The code avoid an unnecessary data copy." Well, maybe, but it is still very inefficient since you build a string by adding two characters at a time (Result := Result + IntToHex(P^, 2);). If the resulting string has length 1000, you therefore do up to 500 copies of that string completely unnecessarily! Better to do it like in my answer here.
Hi, Thanks to your Reply but this was not the Question. I need the DBMS to create the GUID automatic. The GUID exists inside a Variant, so I need to Convert that Variant into Hex String.
@AndreasRejbrand The data is a GUID, 16 bytes not 1000!
@fisi-pjm You said:"I need to Convert that Variant into Hex String". That is exactly what the code I provided is doing. Read better. You also said: "I need the DBMS to create the GUID automatic.". The DBMS is calling the exact same function as you can call from Delphi. It is only interesting to create a GUID using DBMS function if you use it inside a SQL statement without first getting it back to your application.
@AndreasRejbrand I timed the execution time with your routine and with mine. Mine is much faster than yours for a GUID. The source code is there: wiki.overbyte.be/arch/MSSQLGUIDDemoMain.zip
|
1

Thanks to Olivier who Pointed me through the right Topic, "Getting VarByte in Hex notation". I found a Solution to get a Var Byte Array converted into a String. Thats the way I solved it:

First Change your Variant into TBytes. Using Code found here

Function VariantToBytes(Const Value: Variant): TBytes;
Var
  Size: Integer;
  pData: Pointer;
Begin
  Size := Succ(VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1));
  SetLength(Result, Size);
  pData := VarArrayLock(Value);
  Try
    Move(pData^, Pointer(Result)^, Size);
  Finally
    VarArrayUnlock(Value);
  End;
End;

Than change your Binary into String by using that function:

function MyBinToHex(ABuf: PByte; ALen: Cardinal): string; overload;
const
  HexDigits: array[0..$F] of Char = '0123456789ABCDEF';
var
   i: Integer;
begin
   if ALen = 0 then
    Exit('');
   SetLength(Result, 2 * ALen + 2);
   Result[1] := '0';
   Result[2] := 'x';
   hublogger.Debug(ALen.ToString);
   for i := 0 to ALen do
   begin
     Result[2*i + 3] := HexDigits[ABuf[i] shr 4];
     Result[2*i + 4] := HexDigits[ABuf[i] and $0F];
   end;
end;

function MyBinToHex(ABytes: TArray<Byte>): string; overload;
begin
  Result := MyBinToHex(PByte(ABytes), Length(ABytes));
end;

My Final call looks like that

var
 NewGUID : Variant;
 GUIDStr : String;
begin
 NewGUID := getNewGUID;
 GUIDStr := MyBinToHex(VariantToBytes(newgguid))

end;

6 Comments

You do know that Delphi has a type TGuid? You can replace all of above codes with AString := TGuid.NewGuid.ToString to get a properly formatted guid string.
Caveat: an empty array results in 0x.
Please note that this MyBinToHex is rather inefficient, since it builds a string by adding two chrs at a time. Better to preallocate, as in this answer.
@TomBrunberg Thanks for that hint. Yes I am aware of that TGuid, but unfortunately I have to keep in as a Variant to stay compatible with the rest of my Code.
@AndreasRejbrand Thx for that information. I changed my Code to fit my needs. Can you please look through the code again and say if my changes are ok? I killed my Brain to get to that Point :D
|
1

As I understand it you are asking how to convert a variant (holding a 16 byte array, a GUID) to a hex string representation. If you don't mind, I also include the creation of the GUID as it blends in smoothly. I believe I have maintained compatibility with your existing code.

Three tasks:

  • a) The creation of a GUID

  • b) Conversion of that GUID to a Variant

  • c) Conversion of the GUID to a string of hex characters

The complete process, from creating a GUID, converting it to an array of bytes (and to a variant), and to the end result of the hex string, is as follows:

var
  G: TGUID;
  B: TBytes;
  S: string;
  V: variant;
begin
  // task 1
  G := TGuid.NewGuid;

  // task 2
  B := G.ToByteArray;  
  V := B;  // needed only for your usage elsewhere in your code
  
  // task 3
  SetLength(S, 32);
  BinToHex(B, PWideChar(s), 16);
  S := '0x' + S;
  
end;

I left out error checking etc. as I wanted to focus on the core functionality. Add as you see needed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.