I'm encountering some problems with converting Delpi7 pascal to C#, maybe a fresh set of eyes will see what I can't.


Pascal

procedure TForm1.Button5Click(Sender: TObject);
var Data: TData;
    i: integer;
    s: string;
begin
if not (CheckBox1.Checked) then
  begin StatusBar1.SimpleText:='Bidirectional operation not allowed'; exit; end;
for i:=0 to 255 do Data[i]:=0;
Data:=Receive($02,8);
if (Data[1]=0) then
  begin StatusBar1.SimpleText:='Failed'; exit; end;
s:='';
for i:=1 to 8 do
  s:=s+Chr(Data[i+1]);
Edit1.Text:=s;
end;

C#

private void btnRX_Click(object sender, EventArgs e)
        {
            byte[] Data = null;
            int i;
            string s;

            if (!(checkBox1.Checked))
            {
                toolStripStatusLabel1.Text = "Bidirectional operation not allowed";
                return;
            }
            for (i = 0; i <= 255; i++)
            {
                Data[i] = 0;
            }
            Data = Receive(0x02, 8);
            if ((Data[1] == 0))
            {
                toolStripStatusLabel1.Text = "Failed";
                return;
            }
            s = "";
            for (i = 1; i <= 8; i++)
            {
                s = s + (char)(Data[i + 1]);
            }
            txtBox1.Text = s;
        }

I crash at

Data[i] = 0;

Edited 5 Years Ago by SteveyD: n/a

Forgot to add this part to the Pascal code

type
  TData = array [0..255] of byte;

Think you got a null reference exeption.
You did not declare your byte array on line 3:
Use something like this in C#:

byte[] Data = new byte[256];

Edited 5 Years Ago by ddanbe: n/a

You've declared a placeholder for the byte values (line 3), but didn't actually allocate them. Change it to byte[] Data = new byte[256]; .

No idea what Receive is supposed to do, so can't help you with that.

Edited 5 Years Ago by Momerath: n/a

Thank you ddanbe and Momerath!

I didn't see that at all.

correcting this has allowed me to step into the next error, "MarshalDirectiveException was unhandled" at Data = Receive(0x02, 8); most likely due to the microcontroller not responding to the Receive command.
Guess I need an error trap now.

Thanks again.

New problem, too tired to concentrate.

Taking the text data from textBox1, it gets sent to the micro controller one byte at a time, So the text needs to be converted to its numeric equivalent

In Delphi, Ord provides the Ordinal value of an integer, character or ... or enumerations into their numeric equivalents. What is the command in C#?

The second part is the reverse, pulling the data from the micro controller as numeric values and converting it.

In Delphi, Chr converts an integer into a character. What is the command in C#?


Original Delphi Pascal code:

procedure TForm1.Button3Click(Sender: TObject);
var Data: TData;                   
    i, Len: integer;               
begin
Data[0]:=$02;                      
for i:=1 to 8 do Data[i]:=$20;     
for i:=1 to length(Edit1.Text) do
  Data[i]:=Ord(Edit1.Text[i]);     //copy Data
Len:=8;
if (Send(Len,Data)) then StatusBar1.SimpleText:='OK' else StatusBar1.SimpleText:='Failed';
end;

procedure TForm1.Button5Click(Sender: TObject);
var Data: TData;
    i: integer;
    s: string;
begin
if not (CheckBox1.Checked) then
  begin StatusBar1.SimpleText:='Bidirectional operation not allowed'; exit; end;
for i:=0 to 255 do Data[i]:=0;
Data:=Receive($02,8);
if (Data[1]=0) then
  begin StatusBar1.SimpleText:='Failed'; exit; end;
s:='';
for i:=1 to 8 do
  s:=s+Chr(Data[i+1]);
Edit1.Text:=s;

Code Converted to C#:

private void btnTX_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[256]; 
            int i , Len;                 
            Data[0] = 0x02;              
            for (i = 1; i <= 8; i++) Data[i] = 0x20;
            for (i = 1; i <= txtBox1.Text.Length; i++)
                Data[i] = ord(txtBox1.Text[i]);     // <---Compile error here
            Len = 8;
            if ((Send(Len, Data))) toolStripStatusLabel1.Text = "OK"; else toolStripStatusLabel1.Text = "Failed";
        }
                private void btnRX_Click(object sender, EventArgs e)
         {
             byte[] Data = new byte[256];
             int i;
             string s;

             if (!(checkBox1.Checked))
             { toolStripStatusLabel1.Text = "Bidirectional operation not allowed"; return; }
             for (i = 0; i <= 255; i++) Data[i] = 0;
             Data = Receive(0x02, 8);
             if ((Data[1] == 0))
             { toolStripStatusLabel1.Text = "Failed"; return; }
             s = "";
             for (i = 1; i <= 8; i++)
                 s:=s+Chr(Data[i+1])  //<-----Compile error here
             txtBox1.Text = s;
         }

Edited 5 Years Ago by SteveyD: n/a

Many options, have a look here:http://msdn.microsoft.com/en-us/library/f02979c7.aspx to convert a string to an int.
You could also use Convert it has among others the methods ToString and ToInt
Line 26 of your code could read: s = Data[i+1].ToString();
The assignment operator is = not :=!
In C# every type derives from Object, so you could even say something like MyStr = 123.ToString(); and MyStr would equal "123".

Edited 5 Years Ago by ddanbe: n/a

Thanks ddanbe

s = s + Data[i+1].ToString();

fixed line 26, line 8 still under research.

Well, look at the TryParse method here in the MSDN link I provided you in my previous post. There is some sample code.

I continue to get Marshal errors,

[DllImport("mrdsio.dll", CallingConvention=CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.Interface)]
        public static extern byte[] Receive(int Adr, int Len);

private void btnRX_Click(object sender, EventArgs e)
         {
             byte[] Data = new byte[256];
             int i;
             string s;

             if (!(checkBox1.Checked))
             { toolStripStatusLabel1.Text = "Bidirectional operation not allowed"; return; }
             for (i = 0; i <= 255; i++) Data[i] = 0;

             
             Data = Receive(0x02,8);  <--ERROR "Cannot marshal 'return value': Invalid managed/unmanaged type combination."


             if ((Data[1] == 0))
             { toolStripStatusLabel1.Text = "Failed"; return; }
             s = " ";
             for (i = 1; i <= 8; i++)
                 s = s+Data[i + 1].ToString();
             txtBox1.Text = s;
         }

How do I Marshal both Adr and Len?

The mrdsio.dll acts like a Inpout32.dll using pseudo i2c to send/receive address pointer and data from a micro controller.

#
[DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Ansi)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool Send(int Len, byte[] Data);

private void btnTX_Click(object sender, EventArgs e)
         {
             byte[] Data = new byte[256];
             int i, j, Len;                
             Data[0] = 0x02;                          // buffer address
             j = txtBox1.Text.Length;
             for (i = 1; i <= 8; i++) Data[i] = 0x20; // fill 8 blank spaces first
             Len =8;
             if ((Send(Len, Data))) toolStripStatusLabel1.Text = "OK"; else toolStripStatusLabel1.Text = "Failed";
         }

Using the above code, I can correctly send 8 spaces (0x20), changing the 0x20 to 0x21 and it correctly fills the 8 spaces with !!!!!!!! (8 exclamation marks), so the function is working.

How do I define

Data[i] = "The next character in the txtBox1.text.string"

Edited 5 Years Ago by SteveyD: n/a

for (int i = 0; i < textBox1.Text.Length, i++) {
    Data[i] = textBox1.Text[i];
}

Edited 5 Years Ago by Momerath: n/a

Thanks Momerath

that results in a an error
"Cannot implicitly convert type 'char' to 'byte'"

I'm missing something very basic here, just cant see it.

Ok, got the code sorted out, the data is sending to the micro controller, but the data is parsing out the very first Data char.

The Send function is now working, but the Receive function (the exact opposite function of Send) is still evoking errors.

Cannot marshal 'return value': Invalid managed/unmanaged type combination.

[DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern byte[] Receive(int Adr, int Len);

private void btnRX_Click(object sender, EventArgs e)
        {
            
            byte[] Data = new byte[256];
            int i;
            string s;
            int Adr = 0x02;
            int Len = 8;
            for (i = 0; i <= 255; i ++ ) Data[i] = 0;
            Data = Receive(Adr, Len);  <-------Error "Cannot marshal 'return value': Invalid managed/unmanaged type combination."
            if ((Data[1] == 0))
            {
                StatusLbl1.Text = "Failed, check Connection";
                return;
            }
            s = "";
            for (i = 1; i <= 8; i ++ )
            {
                s = s + (char)(Data[i + 1]);
            }
            txtBox1.Text = s;
            
        }

Edited 5 Years Ago by SteveyD: n/a

Still having problems with this RECEIVE function.

The SEND function is the exact opposite function of Receive, I'm perplexed as to why I can send the data, but not retrieve it.

[DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern bool Send(int Len, byte[] Data);

[DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern byte[] Receive(int Adr, int Len);

private void btn_SEND_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[256];
            int i, Len;
            Data[0] = 0x02;             // buffer address
            Len = 8;
             for (i = 0; i < txtBox1.Text.Length; i++)
            {
                byte vOut = Convert.ToByte(txtBox1.Text[i]);
                Data[i + 1] = vOut;
            }
            if ((Send(Len, Data))) StatusLbl1.Text = "OK"; else StatusLbl1.Text = "Failed";
       }



        private void btn_RECEIVE_Click(object sender, EventArgs e)
        {
            int i;
            int Adr = 0x02;
            int Len = 8;
            string s = "";
            byte[] Data = new byte[256];
            
            for (i = 0; i <= 255; i ++ ) Data[i] = 0;
            
            Data = Receive(Adr, Len); <----------Cannot marshal 'return value': Invalid managed/unmanaged type combination.
              
            i = 0;
            while (i < Len-1 && Data[i] != 0)
            {
                s = s + Convert.ToChar(Data[i]);
                i++;
            }
            txtBox1.Text = s;
         }

The original source code for the mrdsio.dll (.pas file) indicates that the function of Receive(Adr,Len) results in a String called OUTPUT.

Now if I am issuing Receive(Adr,Len) how would I get the mrdsio.dll's string of OUTPUT?

ddanbe

No, It will compile as posted 3 messages above, but the Receive function wont work and will crash if the Receive function is run.

Trying to convert how Receive is handled/marshalled/processed from the Delphi6 original to C# is my hangup.

The original code for the mrdsio.dll handles the address, byte array and returns the results.

type

  TData = array [0..255] of byte;

function Receive(Adr, Len: Integer): TData;

function TDM1.Receive(Adr, Len: Integer): TData;

var Output: TData;

...
...
Result:=Output;

Edited 3 Years Ago by mike_2000_17: Fixed formatting

Re-compiled as Unsafe, same results.

What about restructuring the original Delphi function

type
  TData = array [0..255] of byte;

function Receive(Adr, Len: integer): TData; stdcall; external 'mrdsio.dll';

//receive
procedure TForm1.Button5Click(Sender: TObject);
var Data: TData;
    i: integer;
    s: string;
begin
if not (CheckBox1.Checked) then
  begin StatusBar1.SimpleText:='Bidirectional operation not allowed'; exit; end;
for i:=0 to 255 do Data[i]:=0;
Data:=Receive($02,8);
if (Data[1]=0) then
  begin StatusBar1.SimpleText:='Failed, check Connection'; exit; end;
s:='';
for i:=1 to 8 do
  s:=s+Chr(Data[i+1]);
Edit1.Text:=s;
end;

to make it a little more .Net friendly. Something along the lines of this, I'm unsure of the structure.

function Receive(Adr, Len: integer; var Data:TData: result); stdcall; external 'mrdsio.dll';

Edited 5 Years Ago by SteveyD: n/a

Can you post your latest copy of both .NET and Delphi source?

This does not make sense:

private void btnRX_Click(object sender, EventArgs e)
         {
             byte[] Data = new byte[256];
             int i;
             string s;

             if (!(checkBox1.Checked))
             { toolStripStatusLabel1.Text = "Bidirectional operation not allowed"; return; }
             for (i = 0; i <= 255; i++) Data[i] = 0;
             Data = Receive(0x02, 8);

In the above code you create 256 byte array, then call Data = Receive(0x02, 8) which doesn't even use your existing array, it would replace the reference if the call to Receive() succeeded.

Also you can call Array.Zero(array); to clean out an array instead of iterating the bytes.

I don't understand this:

[DllImport("mrdsio.dll", CallingConvention=CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.Interface)]
        public static extern byte[] Receive(int Adr, int Len);

UnmanagedType.Interface would be along the lines of a COM-accessibly interface, not a byte array. Perhaps that is why you had this error: "Cannot marshal 'return value': Invalid managed/unmanaged type combination."?

[return: MarshalAs] applies to an external method signature will marshal the result. To marshal the individual parameters you should apply the attribute to parameters:

[DllImport("dwmapi.dll", PreserveSig = false)]
    static extern void DwmGetColorizationColor(
      out int pcrColorization,
      [MarshalAs(UnmanagedType.Bool)]out bool pfOpaqueBlend);

Does this method signature compile? I stopped with Delphi6 but this function doesn't have a return type:

function Receive(Adr, Len: integer; var Data:TData: result); stdcall; external 'mrdsio.dll';

You have two different versions of the signature in the same post. The other one looks valid

Lastly can you attach a copy of mrdsio.dll? From looking at the original Delphi code i'm thinking the P/Invoke signature are incorrect. The receive buffer is 256 bytes but it appears only 9 are used.
If you could upload the complete Delphi project, or at least a Delphi project that could communicate with the controller, it would probably help. I think we're probably missing something outside of the Tx/Rx methods.

This links contains the original Delphi example and a pdf which explains what its supposed to do.http://pira.cz/rds/mrdsio.zip

I have successfully converted the Delphi project to C# with the one exception of this Receive function.

here is the C# portions of the code for the Receive function;

[DllImport("mrdsio.dll", EntryPoint="Receive", CallingConvention = CallingConvention.StdCall)]

            unsafe public static extern byte[] Receive(int Adr,int Len);


private void btnRX_Click(object sender, EventArgs e)

        {

            byte[] Data = new byte[256];

            int i;

            string s;

            int Adr = 0x02;

            int Len = 8;

            if (!(checkBox1.Checked))

            { StatusLbl1.Text = "Bidirectional operation not allowed"; return; }

            for (i = 0; i <= 255; i++) Data[i] = 0;

                Data = DLL.Receive(Adr, Len);

            if ((Data[1] == 0))

            { StatusLbl1.Text = "Failed"; return; }

            s = " ";

            for (i = 1; i <= 8; i++)

                s = s + Data[i + 1].ToString();

            txtBox1.Text = s;

           }

Edited 5 Years Ago by SteveyD: n/a

[return: MarshalAs(UnmanagedType.Struct)]
    [DllImport("mrdsio.dll")]
    static extern TData Receive(int Adr, int Len);

    [StructLayout(LayoutKind.Explicit, Size=256)]
    unsafe struct TData
    {
      [FieldOffset(0)]
      public fixed byte m_buffer[256];
    }
    void simpleButton3_Click(object sender, EventArgs e)
    {
      TData buffer = Receive(0x02, 8);
      Debugger.Break();
    }

Edited 5 Years Ago by sknake: n/a

SKnake,

running your snippet in the compiled exe no longer returns errors, something "good" is happening, I've been tinkering on this side project for over 18 months. I've not dealt with single dimension byte arrays before.

looking at the locals with the debugger;
the m_buffer value is reporting as 0x0419e434 type byte*
the m_buffer[0] value is reporting as 2 type byte
and all the remaining buffer[1-256] values remain empty.

my last step now is to split the m_buffer value into its string equivalent, I'll dig into this tomorrow.

The value you see for m_buffer with the debugger is the memory address (pointer) to the array.
m_buffer[0] should be the same as the first parameter:

TData buffer = Receive(0x02, 8);

So receive on 0x02 means m_buffer[0] will be 0x02, or 2. This is a more friendly way of looking at the result:

[return: MarshalAs(UnmanagedType.Struct)]
    [DllImport("mrdsio.dll")]
    static extern TData Receive(int Adr, int Len);

    [StructLayout(LayoutKind.Explicit, Size=256)]
    unsafe struct TData
    {
      [FieldOffset(0)]
      fixed byte m_buffer[256];


      /// <summary>
      /// Alternate constructor for testing data
      /// </summary>
      /// <param name="buffer"></param>
      public TData(byte[] buffer)
      {
        if (buffer == null) throw new ArgumentNullException("buffer");
        if (buffer.Length != 256) throw new ArgumentException("must be 256 bytes", "buffer");

        fixed (byte* p = m_buffer)
        {
          IntPtr ptr = new IntPtr(p);
          Marshal.Copy(buffer, 0, ptr, buffer.Length);
        }
      }

      public byte Address
      {
        get
        {
          fixed (byte* p = m_buffer)
          {
            return p[0];
          }
        }
      }

      public byte BytesRead
      {
        get
        {
          fixed (byte* p = m_buffer)
          {
            return p[1];
          }
        }
      }

      public bool HasData
      {
        get { return (BytesRead > 0); }
      }

      public string TextData
      {
        get
        {
          if (HasData)
          {
            byte[] target = new byte[BytesRead];
            fixed (byte* src = m_buffer, dst = target)
            {
              byte* ps = src + 2;
              byte* pt = dst + 0;
              for (int i = 0; i < target.Length; i++)
              {
                *pt = *ps;
                pt++;
                ps++;
              }
              return Encoding.Default.GetString(target);
            }
          }
          else
          {
            return string.Empty;
          }
        }
      }
    }

    private void button4_Click(object sender, EventArgs e)
    {
      TData dat = Receive(0x02, 8);
      string s = dat.TextData;
      Debugger.Break();
    }

    private void button5_Click(object sender, EventArgs e)
    {
      string s = "abc123!!!";
      byte[] stringBytes = Encoding.Default.GetBytes(s);

      byte[] buffer = new byte[256];
      buffer[0] = 0x02; //starting addr
      buffer[1] = (byte)stringBytes.Length;
      Array.Copy(stringBytes, 0, buffer, 2, stringBytes.Length);


      TData data = new TData(buffer);
      Debug.WriteLine("Address: " + data.Address.ToString());
      Debug.WriteLine("BytesRead: " + data.BytesRead.ToString());
      Debug.WriteLine("HasData: " + data.HasData.ToString());
      Debug.WriteLine("TextData: " + data.TextData.ToString());
    }

makes perfect sense stepping through your code,

private void button5_Click(object sender, EventArgs e)
{
byte[] buffer = new byte[256];
TData data = new TData(buffer);
TData dat = Receive(0x02, 8);
string s = dat.TextData;
byte[] stringBytes = Encoding.Default.GetBytes(s);
buffer[0] = 0x02; //starting addr
buffer[1] = (byte)stringBytes.Length;
Array.Copy(stringBytes, 0, buffer, 2, stringBytes.Length);
 

Debug.WriteLine("Address: " + data.Address.ToString());
Debug.WriteLine("BytesRead: " + data.BytesRead.ToString());
Debug.WriteLine("HasData: " + data.HasData.ToString());
Debug.WriteLine("TextData: " + data.TextData.ToString());
}

would this report the actual data returned from Receive(0x02,8)?

Yes. The 256-byte buffer:
0: Start Address (First parameter of receive, 0x02). Access: TData.Address
1: Length of text data (0=no data returned). Access: TData.BytesRead
2..255: Null-terminated string: Access.TData.TextData

I'm assuming you should verify the returned address matches the requested address:

private void button6_Click(object sender, EventArgs e)
    {
      int addr = 0x02;
      TData dat = Receive(addr, 8);
      if (dat.Address != addr)
      {
        MessageBox.Show("Unexpected response...");
      }
      string s = dat.TextData;
      Debugger.Break();
    }

I don't really understand what the Address parameter does though.

The address pointer is actually an address on an MCU eeprom bank.

The mrdsio.dll handles the exchange between the MCU and the PC using the Receive(Adr,Len) with TData as its return

The C# code is only passing the Receive(Adr,Len) to the .dll and the TData being returned to the mrdsio.dll is not reaching the C# level. This is where I'm stuck.

I'm resisting the desire to re-write the mrdsio.dll as a C# .dll as I am not familiar with the procedures needed to communicate with an MCU in C#.

The code I gave you should work for the Receive() function -- is it not working on your end?

By the way this only works on x86 (32-bit) .NET applications. If you compile your C# application as "Any CPU" it will run fine on a 32-bit OS but will fail on 64-bit OS'. Make sure you set the build options to 32-bit.

This question has already been answered. Start a new discussion instead.