Paulo Sousa
Paulo Sousa

Reputation: 23

Convert C# function to Delphi function

I need to convert this C# function to Delphi. I am converting a function that will generate a string based on the CPU id, BIOS id, and motherboard id.

Can anyone help me, please?

private string GetHash(string s)
{
    return ComputerInfo.GetHexString(new MD5CryptoServiceProvider().ComputeHash(new ASCIIEncoding().GetBytes(s)));
}

private string GetHexString(byte[] bt)
{
    string str1 = string.Empty;
    int myLenght = bt.Length - 8;
    for (int index = 0; index < myLenght; ++index)
    {
        int num1 = (int)bt[index];
        int num2 = num1 & 15;
        int num3 = num1 >> 4 & 15;
        string str2 = num3 <= 9 ? str1 + num3.ToString() : str1 + ((char)(num3 - 10 + 65)).ToString();
        str1 = num2 <= 9 ? str2 + num2.ToString() : str2 + ((char)(num2 - 10 + 65)).ToString();
        if (index + 1 != myLenght && (index + 1) % 2 == 0)
            str1 += "-";
    }

    return str1;
}

I tried several ways to convert the functions, but I still couldn't get the same values that I get in the original function. On my laptop, I get the String '1CB9-0485-D2FD-846C' but in Delphi, I get the string '3237-3939-3232-4433-4141-3835-4342-4536-4145-3344-3933-383736'.

function TAppForm.GetHexString(bt : TBytes) : string;
var
  str1: string;
  str2: string;
  myLength: Integer;
  index: Integer;
  num1: Integer;
  num2: Integer;
  num3: Integer;
begin
  str1 := string.Empty;
  myLength := Length(bt) - 8;
  for index := 0 to myLength do
  begin
    num1 := Integer(bt[index]);
    num2 := num1 and 15;
    num3 := num1  shr  4 and 15;
    If num3 <= 9 then
      str2 := str1 + num3.ToString()
    else
      str2 := str1 + Chr(num3 - 10 + 65);
    If num2 <= 9 then
      str1 := str2 + num2.ToString()
    else
      str1 := str2 + Chr(num2 - 10 + 65);

    if ((index + 1) <> myLength) and ((index + 1) mod 2 = 0) then
      str1  := str1 + '-';
  end;
  Result := str1;
end;

function TAppForm.MyGetMD5(Value : String) : String;
var
  workHash : TIdHashMessageDigest5;
begin
  workHash := TIdHashMessageDigest5.Create;
  try
    Result := workHash.HashStringAsHex(Value);
  finally
    FreeAndNil(workHash);
  end;
end;

function TAppForm.GetHash(s : string) : string;
var
  //bytes: TBytes;
  bytes: TBytes;
  smd5: string;
begin
  smd5 := MyGetMD5(s);
  bytes := TEncoding.ASCII.GetBytes(smd5);
  smd5 := GetHexString(bytes);
  Result := smd5;
end;

Thank you very much for your solution @olivier. In fact, it is very close to what I want.

However, the result I get in C# is "1CB9-0485-D2FD-846C" and with your solution I get "0126-28BE-D776-1788". I have tried to understand why, since the string that is requested hash is the same.

In C#, the request is as follows

GetHash("CPU >>" + ComputerInfo.CpuId() + "\nBIOS >>" + ComputerInfo.BiosId() + "\nBASE >>" + ComputerInfo.BaseId());

Result

CPU BFEBFBFF000306D4
BIOS INSYDE Corp.5.308F014604C20160325000000.000000 + 000TOSQCI - 1
BASE FF50Base BoardQC0C0Q8F3000710

In Delphi, the request is as follow

const
  sLineBreak = {$IFDEF LINUX} AnsiChar (#10) {$ENDIF}
               {$IFDEF MSWINDOWS} AnsiString (#13#10) {$ENDIF};

GetHash('CPU >>' + CpuId() + sLineBreak + 'BIOS >>' + BiosId() + sLineBreak + 'BASE >>' + BaseId());

Result

CPU BFEBFBFF000306D4
BIOS INSYDE Corp.5.308F014604C20160325000000.000000 + 000TOSQCI - 1
BASE FF50Base BoardQC0C0Q8F3000710

Upvotes: 1

Views: 742

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595369

The Delphi logic does not match the C# logic.

The C# code is doing the following:

  • converting a Unicode string to an ASCII byte array
  • then MD5-hashing those bytes
  • then encoding the bytes of that hash to a formatted hex string

The Delphi code is doing the following:

  • (implicitly) converting a Unicode string to an ASCII byte array
  • then MD5-hashing those bytes
  • then encoding the bytes of that hash to a plain hex string
  • then converting that hex string to an ASCII byte array
  • then encoding those bytes to a formatted hex string

So, of course you end up with different results, because the final hex string is being encoded from completely different inputs.

The correct translation should look more like this instead:

function TAppForm.GetHash(const s: string): string;
var
  workHash : TIdHashMessageDigest5;
begin
  workHash := TIdHashMessageDigest5.Create;
  try
    Result := GetHexString(workHash.HashString(s, IndyTextEncoding_ASCII));
  finally
    workHash.Free;
  end;
end;

function TAppForm.GetHexString(bt: TIdBytes): string;
var
  str1, str2: string;
  myLenght,
  index,
  num1, num2, num3: Integer;
begin
  str1 := '';
  myLenght := Length(bt) - 8;
  for index := 0 to myLenght-1 do
  begin
    num1 := Integer(bt[index]);
    num2 := num1 and 15;
    num3 := (num1 shr 4) and 15;
    if num3 <= 9 then
      str2 := str1 + IntToStr(num3)
    else
      str2 := str1 + Char(num3 - 10 + 65);
    if num2 <= 9 then
      str1 := str2 + IntToStr(num2)
    else
      str1 := str2 + Char(num2 - 10 + 65);
    if ((index + 1) <> myLenght) and ((index + 1) mod 2 = 0) then
      str1 := str1 + '-';
  end;
  Result := str1;
end;

Also, the '\n' char in C# is just a line feed. It is not the same as the #13#10 (carriage return + line feed) char sequence in Delphi on Windows. So, your input to GetHash() is not the same between C# and Delphi, either. You should not be using the sLineBreak constant at all, use just #10 by itself, eg:

GetHash('CPU >>' + CpuId() + #10'BIOS >>' + BiosId() + #10'BASE >>' + BaseId());

Upvotes: 2

Olivier
Olivier

Reputation: 18037

Here's a working implementation. It uses THashMD5, which is available in recent versions of Delphi.

uses
  System.Hash;

function GetHexString(const b: TBytes): string;
var
  len: Integer;
  index: Integer;
  num1: Byte;
  num2: Byte;
  num3: Byte;
begin
  Result := '';
  len := Length(b) - 8;
  for index := 0 to len - 1 do
  begin
    num1 := b[index];
    num2 := num1 and 15;
    num3 := (num1 shr 4) and 15;
    If num3 <= 9 then
      Result := Result + num3.ToString()
    else
      Result := Result + Chr(num3 - 10 + 65);
    If num2 <= 9 then
      Result := Result + num2.ToString()
    else
      Result := Result + Chr(num2 - 10 + 65);
    if ((index + 1) <> len) and ((index + 1) mod 2 = 0) then
      Result := Result + '-';
  end;
end;

function GetHash(const s: string): string;
var
    b, hash: TBytes;
    md5: THashMD5;
begin
    b := TEncoding.ASCII.GetBytes(s);

    md5 := THashMD5.Create;
    md5.update(b);
    hash := md5.HashAsBytes;

    Result := GetHexString(hash);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetHash('Test'));
end;

Both C# and Delphi implementations output 0CBC-6611-F554-0BD0 for input string Test.

Upvotes: 1

Related Questions