2

I need to validate the user input and check if the entered string is a valid GUID. How can I do that? Is there some sort of IsValidGuid validation function?

AmigoJack
  • 5,234
  • 1
  • 15
  • 31
peer____42
  • 45
  • 8

2 Answers2

4

You can call the WinAPI function CLSIDFromString and check (at its failure) if the returned value was not CO_E_CLASSSTRING (which stands for an invalid input string). Calling the built-in StringToGUID function is not reliable as it raises exception from which you're not able to get the reason of the failure.

The following function returns True if the input string is a valid GUID, False otherwise. In case of other (unexpected) failure it raises an exception:

const
  S_OK = $00000000;
  CO_E_CLASSSTRING = $800401F3;

type
  LPCLSID = TGUID;
  LPCOLESTR = WideString;

function CLSIDFromString(lpsz: LPCOLESTR; pclsid: LPCLSID): HRESULT;
  external 'CLSIDFromString@ole32.dll stdcall';

function IsValidGuid(const Value: string): Boolean;
var
  GUID: LPCLSID;
  RetVal: HRESULT;
begin
  RetVal := CLSIDFromString(LPCOLESTR(Value), GUID);
  Result := RetVal = S_OK;
  if not Result and (RetVal <> CO_E_CLASSSTRING) then
    OleCheck(RetVal);
end;
AmigoJack
  • 5,234
  • 1
  • 15
  • 31
TLama
  • 75,147
  • 17
  • 214
  • 392
2

How about this?

Uses System.SysUtils;

function IsValidGuid(GUID: String): Boolean;
begin
  try
    StringToGUID(GUID);
    Result := True;
  except
    Result := False;
  end;
end;

If the StringToGUID function is not successful in converting a string to a TGUID, it will raise an error. This is how I used it in my project and it works fine.

Another alternative:

function IsValidGuid(const GUID: string): Boolean;
var
  G: string;
  L: Integer;
begin
  Result := False;
  L := Length(GUID);
  if not (L in [36, 38])  then
    Exit;
  G := UpperCase(GUID);
  if (L = 38) and (G[1] = '{') and (G[L] = '}') then
    G := Copy(G, 2, L - 2);
  Result := (Length(G) = 36) and (G[09] + G[14] + G[19] + G[24] = '----');
  if Result then
  begin
    G := StringReplace(G, '-', '', [rfReplaceAll]);
    for L := 1 to 32 do
      Result := Result and (G[L] in ['0'..'9', 'A'..'F']);
  end;
end;
Mahan
  • 135
  • 7
  • The implementation of that function is `OleCheck(CLSIDFromString(PWideChar(WideString(S)), Result));` and as such it's the same as the answer from 2015 (which already mentioned this function and why it's not the most reliable). – AmigoJack Dec 06 '22 at 11:01
  • 1
    I think uncomplicated and more readable code is always better. That's why I posted that answer. – Mahan Dec 06 '22 at 12:14
  • ...at the price of also returning `FALSE` for other reasons, unbound to a valid or invalid format. – AmigoJack Dec 06 '22 at 14:17