Search code examples
c#delphicode-translation

Translation from C# to Delphi


I need help translating part of the C# code to Delphi. I would very much appreciate any help or advice.

unsafe {
    fixed (byte* bpt = &buff[140]) {
        byte b1 = bpt[0];
        byte b2 = bpt[1];
        byte b3 = bpt[2];
        byte b4 = bpt[3];
    }
}

I tried several ways, defining a record with 4 byte vars, defining tbytearray, but nothing seems to be right.

Any thoughts on this?

Thanks.

UPDATE

Since it seems that I haven't defined my questions right, since this is my first question, here is the complete function on C#.

        private void ProcessCommAlarm_V40(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            MyDebugInfo AlarmInfo = new MyDebugInfo(DebugInfo);

            byte[] buff = new byte[dwBufLen];
            System.Runtime.InteropServices.Marshal.Copy(pAlarmInfo, buff, 0, (int)dwBufLen);           

            string sBuff = "";

            if (dwBufLen - 140 > 0 && (dwBufLen - 140) % 4 == 0)
            {
                int msgType = BitConverter.ToInt32(buff, 0);
                string sMsgType = "";
                bool bAcceptedMsg = false;
                int nTargetType = 0; 

                switch (msgType)
                {
                    case 2:
                        sMsgType = "Video Loss"; bAcceptedMsg = true; nTargetType = 1; break;

                    case 3:
                        sMsgType = "Motion"; nTargetType = 1; break;
                }                

                if (bAcceptedMsg)
                {
                    int nTotalElements = BitConverter.ToInt32(buff, 12);

                    if ((nTargetType == 1 && nTotalElements <= CHCNetSDK.MAX_CHANNUM_V30) || (nTargetType == 2 && nTotalElements <= CHCNetSDK.MAX_DISKNUM_V30))
                    {
                        
                        int len = nTotalElements * 4;
                        byte[] channels = new byte[len];

                        unsafe
                        {
                            fixed (byte* bpt = &buff[140])
                            {
                                byte b1 = bpt[0];
                                byte b2 = bpt[1];
                                byte b3 = bpt[2];
                                byte b4 = bpt[3];

                                IntPtr dwDataAddress = new IntPtr(b1 + b2 * (1 << 8) + b3 * (1 << 16) + b4 * (1 << 24));
                                System.Runtime.InteropServices.Marshal.Copy(dwDataAddress, channels, 0, len);
                            }
                        }

                        for (int i = 0; i <= len - 4; i += 4)
                        {                            
                            sBuff += string.Format("Channel No: {0}", BitConverter.ToInt32(channels, i));
                            sBuff += Environment.NewLine;
                        }
                    }
                }
            }
        }

Now, this is what I have done so far, which includes the change given my @remy-lebeau.

procedure ProcessCommAlarm_V40(pAlarmer: LPNET_DVR_ALARMER; pAlarmInfo: PChar; dwBufLen: DWORD; dwUser: DWORD);
var
  buff: array of byte;
  msgType: Integer;
  sMsgType: String;
  bAcceptedMsg: Boolean;
  nTargetType: Integer;
  sBuff: String;
  nTotalElements: Integer;
  channels: array of byte;
  len: Integer;
  b1,b2,b3,b4:byte;
  bpt: pbyte;
  dwDataAddress: IntPtr;
  I: Integer;
begin
  sBuff := '';

  SetLength(buff, dwBufLen);
  CopyMemory(buff, pAlarmInfo, dwBufLen);

  if (dwBufLen - 140 > 0) and ((dwBufLen - 140) mod 4 = 0) then begin
    msgType := Integer(buff[0]);
    sMsgType := '';
    bAcceptedMsg := false;
    nTargetType := 0;

    case (msgType) of
      2:
        begin
          sMsgType := 'Video loss';
          bAcceptedMsg := true;
          nTargetType := 1;
        end;
    end;

    if (bAcceptedMsg) then
    begin
      nTotalElements := buff[12];
      if ((nTargetType = 1) and (nTotalElements <= MAX_CHANNUM_V30)) or ((nTargetType = 2) and (nTotalElements <= MAX_DISKNUM_V30)) then begin
        len := nTotalElements * 4;
        SetLength(channels,len);

        bpt := @buff[140];
        b1 := bpt[0];
        b2 := bpt[1];
        b3 := bpt[2];
        b4 := bpt[3];

        dwDataAddress := IntPtr(b1 + b2 * (1 shl 8) + b3 * (1 shl 16) + b4 * (1 shl 24));

        sBuff := sBuff + IntToStr(dwDataAddress);
        sBuff := sBuff + #10 + #13;

        CopyMemory(Channels, @dwDataAddress, len);

        I:=0;
        while (i <= len - 4) do begin
          sBuff := sBuff + 'Channel No: ' + IntToStr(channels[i]);
          sBuff := sBuff + #13 + #10;
          i:= i + 4;
        end;
      end;
    end
  end;
end;

Can someone please recheck the translation, because Delphi code is not reproducing the same results as C#.

UPDATE:

After reading the detailed explanation @Remy wrote down below, I was able to reproduce the same results on both applications in C# and in Delphi.

The code which now is working is below.

procedure ProcessCommAlarm_V40(pAlarmer: LPNET_DVR_ALARMER; pAlarmInfo: IntPtr; dwBufLen: DWORD; dwUser: IntPtr);
var
  buff: array of byte;
  channels: array of byte;
  msgType: Integer;
  sMsgType: String;
  bAcceptedMsg: Boolean;
  nTargetType: Integer;
  sBuff: String;
  nTotalElements: Integer;
  len: Integer;
  b1,b2,b3,b4:byte;
  bpt: pbyte;
  dwDataAddress: IntPtr;
  I: Integer;
  x:pbyte;
begin
  Form1.Memo1.Lines.Add(FormatDateTime('H:m:s',now));
  sBuff := '';

  SetLength(buff, dwBufLen);
  CopyMemory(buff, Pointer(pAlarmInfo), dwBufLen);

  if (dwBufLen - 140 > 0) and ((dwBufLen - 140) mod 4 = 0) then begin
    msgType := Integer(buff[0]);
    sMsgType := '';
    bAcceptedMsg := false;
    nTargetType := 0;

    case (msgType) of
      2:
        begin
          sMsgType := 'Video loss';
          bAcceptedMsg := true;
          nTargetType := 1;
        end;
    end;

    if (bAcceptedMsg) then
    begin
      nTotalElements := buff[12];
      if ((nTargetType = 1) and (nTotalElements <= MAX_CHANNUM_V30)) or ((nTargetType = 2) and (nTotalElements <= MAX_DISKNUM_V30)) then begin
        len := nTotalElements * 4;
        SetLength(channels,len);

        bpt := @buff[140];
        b1 := bpt[0];
        b2 := bpt[1];
        b3 := bpt[2];
        b4 := bpt[3];

        dwDataAddress := IntPtr(b1 + b2 * (1 shl 8) + b3 * (1 shl 16) + b4 * (1 shl 24));
        CopyMemory(Channels, Pointer(dwDataAddress), len);

        I:=0;
        while (i <= len - 4) do begin
          sBuff := sBuff + 'Channel No: ' + IntToStr(channels[i]);
          sBuff := sBuff + #13 + #10;
          i:= i + 4;
        end;
      end;
    end
  end;
end;

Solution

  • Given buff: array[0..MaxLimit] of Byte; where MaxLimit is >= 143;

    In Delphi 2009 and later:

    var
      bpt: PByte;
      b1, b2, b3, b4: Byte;
    begin
      bpt := @buff[140];
      b1 := bpt[0];
      b2 := bpt[1];
      b3 := bpt[2];
      b4 := bpt[3];
    end;
    

    In Delphi 2007 and earlier:

    var
      bpt: PChar;
      b1, b2, b3, b4: Byte;
    begin
      bpt := PChar(@buff[140]);
      b1 := Byte(bpt[0]);
      b2 := Byte(bpt[1]);
      b3 := Byte(bpt[2]);
      b4 := Byte(bpt[3]);
    end;
    

    Alternatively:

    var
      bpt: PByte;
      b1, b2, b3, b4: Byte;
    begin
      bpt := @buff[140];
      b1 := bpt^; Inc(bpt);
      b2 := bpt^; Inc(bpt);
      b3 := bpt^; Inc(bpt);
      b4 := bpt^; Inc(bpt);
    end;
    

    UPDATE: That being said, I see a number of problems with your translation.

    • in the C# code, the dwUser parameter is declared as IntPtr, which is a pointer-sized integer. Delphi also has IntPtr. But in your Delphi code, you have declared the parameter as a DWORD instead. That will work in a 32-bit build, but not in a 64-bit build. If the parameter ever receives a value more than 32-bits, like a memory address, it will get truncated. Use DWORD_PTR instead, if not IntPtr.

    • in the C# code, the pAlarmInfo parameter is declared as IntPtr. But in your Delphi code, you have declared the parameter as a PChar instead. Though that can work, an untyped Pointer would have been more appropriate, if not IntPtr.

    • in Delphi, using array of bytes is OK for the buff and channels variables, though TBytes or TArray<Byte> would be more preferred in modern Delphi versions.

    • using the Win32 CopyMemory() function is fine, but you should consider using Delphi's System.Move() instead. In any case, your 1st call to CopyMemory() is correct (though, if pAlarmInfo is declared as IntPtr, you will have to type-cast it to Pointer). However, your 2nd call is wrong. The C# code is copying len bytes from the memory address held in dwDataAddress, whereas you are copying len bytes from the address of dwDataAddress itself. You need to type-cast dwDataAddress to a pointer and copy from the pointed-to address instead.

    • your translation of the assignments of msgType and nTotalElements are wrong. The C# code is extracting a 4-byte integer from buff at indexes 0..3 and 12..15, respectively. Your Delphi code is reading 1 byte at indexes 12 and 15 and scaling their values up to an Integer. Not the same thing.

    • you completely omitted case 3 from the case msgType of statement. Probably because it was not setting bAcceptedMsg = true. I suspect that missing assignment might be a bug in the C# code.

    • #10 + #13 should be #13 + #10, or simply #13#10. Or, you should use Delphi's System.sLineBreak constant instead.

    • your translation of reading the channel numbers is wrong. Like further above, the C# code is extracting a 4-byte integer from channels at index i..i+3, respectively. Your Delphi code is reading 1 byte at index i and scaling its value up to an Integer.