CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 6 of 6
  1. #1
    Join Date
    Feb 2011
    Posts
    13

    Using Delphi6 Dll's in C# - encountering an error

    Greetings again

    I originally posted this question at Daniweb, and I thought I'd try here for a larger group of eyes.

    I've been trying to use a Delphi6 DLL in a C# program, I've been encountering a a problem I hope someone can solve for me.

    The Send and Receive functions are exact opposites of each other;
    Send passes text to the DLL where is is then passed to an MCU.
    Receive collects the Data returned from the MCU through the DLL.

    The Delphi code works fine, and the C# "Send" function works fine as well.

    The C# Receive function is where my problem is. I'm not sure if is a Marshalling issue, Struct, or something else.


    C# code;
    Code:
    [DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
                public static extern byte[] Receive(int Adr, int Len);
    
            private void btnRX_Click(object sender, EventArgs e)
            {
                byte[] Data = new byte[8];
                int i;
                string s;
                int Adr = 0x02;		// buffer address
                int Len = 8;
                if (!(checkBox1.Checked))
                { toolStripStatusLabel1.Text = "Bidirectional operation not allowed"; return; }
                for (i = 0; i <= 7; i++) //Data[i] = 0;
                    Data = Receive(Adr, Len);  <----------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;
            }
    
    [DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
                public static extern bool Send(int Len, byte[] Data);
    
    private void btnSend_Click(object sender, EventArgs e)
            {
                byte[] Data = new byte[8];
                int i, Len;
                Data[0] = 0x02;             // buffer address
                Len = 8;
                for (i = 1; i <= 8; i++) Data[i] = 0x20; // fill 8 blank spaces first
                Data[0] = 0x02;
                Len = 8;
                for (i = 0; i < txtBox1.Text.Length; i++)
                {
                    byte vOut = Convert.ToByte(txtBox1.Text[i]);
                    Data[i + 1] = vOut;
                }
                if ((DLL.Send(Len, Data))) StatusLbl1.Text = "OK"; else StatusLbl1.Text = "Failed";
            }
    Delphi6 code;

    Code:
    type
      TData = array [0..255] of byte;
    
    function Send(Len: integer; Data: TData): boolean; stdcall; external 'mrdsio.dll';
    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;
    
    //send
    procedure TForm1.Button3Click(Sender: TObject);
    var Data: TData;                   //Data[0] = address, Data[1..] = rds data
        i, Len: integer;               //Len = length of rds data
    begin
    Data[0]:=$02;                      //PS buffer address
    for i:=1 to 8 do Data[i]:=$20;     //fill by spaces at first
    for i:=1 to length(Edit1.Text) do
      Data[i]:=Ord(Edit1.Text[i]);     //copy PS to Data
    Len:=8;
    if (Send(Len,Data)) then StatusBar1.SimpleText:='Data Sent' else StatusBar1.SimpleText:='Failed, Check Connection';
    end;

  2. #2
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Using Delphi6 Dll's in C# - encountering an error

    Generally Win32 functions shouldn't return arrays, instead they should take a pointer to an array, and the array size as a parameter. Then the callee allocates the array and pass the pointer and size of the array to the function.

    An example of this is any Win32 api that 'returns' string data (i.e. character array). A good example of this is the GetModuleFileName api.

    The GetModuleFileName signature in Msdn is listed as:
    Code:
    DWORD WINAPI GetModuleFileName(
      __in_opt  HMODULE hModule,
      __out     LPTSTR lpFilename,
      __in      DWORD nSize
    );
    The GetModuleFileName C# signature as listed on pinvoke.net is:

    Code:
    [DllImport("kernel32.dll", SetLastError=true)]
    [PreserveSig]
    public static extern uint GetModuleFileName
    (
        [In]
        IntPtr hModule,
    
        [Out] 
        StringBuilder lpFilename, 
    
        [In]
        [MarshalAs(UnmanagedType.U4)]
        int nSize
    );
    Search around pinvoke.net to see if you can find any examples of functions that return an array. There might be some examples, but you have to be conscious of memory cleanup. If you return an array, you'll also need to provide a function that cleans up the allocated memory; otherwise you'll run into issues if the dll allocates the array and you try to clean in up in the client. This is why nearly all Win32 api's force the clients to pass in the allocated array (which the client is responsible to clean up).

  3. #3
    Join Date
    Feb 2011
    Posts
    13

    Re: Using Delphi6 Dll's in C# - encountering an error

    Thanks for the reply

    This is looking like a interesting challenge. Never messed with IntPtr before.

    I have the source code for the mrds.dll (also written in Delphi6) maybe that will help me..

    So I should be looking through Pinvoke.net for a dll file that matches
    public static extern byte[] Receive(int Adr, int Len); as close as possible?

  4. #4
    Join Date
    Feb 2011
    Posts
    13

    Re: Using Delphi6 Dll's in C# - encountering an error

    Looking at the source code for the mrdsio.dll, it would appear that the function
    Code:
    function TDM1.Receive(Adr, Len: Integer): TData;
    (from the source code) already does its Delphi version of the Pinvoke and has a
    Code:
     var Output: TData;
    returned to it which is then used by the Delphi example file
    Code:
     function function Receive(Adr, Len: integer): TData; stdcall; external 'mrdsio.dll';
    as
    Code:
    var Data: TData;
    Could it be I'm not picking up the
    Code:
     var Data: TData;
    correctly?
    perhaps as a
    Code:
     [return:MarshalAs( ????)]

    here is the description from the mrdsio information file

    Receive Reads data from the device.
    Adr: Starting address
    Len: Total length of the RDS data being read

    The function returns a TData array where the item with index 0 contains the starting
    address and the item with index 1 contains the length of the data received. Following
    items (index 2 and higher) contain the data received. If the Receive operation fails, the
    length value returned in the array is set to zero.
    Last edited by SteveyD; July 10th, 2011 at 03:25 PM. Reason: added description info

  5. #5
    Join Date
    Feb 2011
    Posts
    13

    Re: Using Delphi6 Dll's in C# - encountering an error

    Arjay

    Let me see if I follow this correctly, I'm sure I have the concept totally messed up in my head and its leading me in circles.

    Code:
    function Receive(Adr, Len: integer): TData; stdcall; external 'mrdsio.dll';
    is the current Delphi6 function where;
    Adr is the memory address location,
    Len is the length of data to be collected.
    TData is the byte array returned.

    Adr and Len are sent to the DLL using C# like this,
    Code:
     [DllImport("mrdsio.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
                public static extern byte[] Receive(int Adr, int Len);
    Now is it a byte[] array I'm asking for or should it be something else?

  6. #6
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Using Delphi6 Dll's in C# - encountering an error

    I'm at a disadvantage since I don't code in Delphi.

    Based on what you've shown, the pinvoke signature seems reasonable.

    So load it up in C# and try it.

    Another way to approach this is to create a test dll in Delphi that has the exact same signature as the real function, and then pinvoke to the test dll.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured