4

I have this function defined in a delphi code:

procedure TestFLASHWNew(
    name: array of string; 
    ID: array of Integer;
    var d1:double
); stdcall;

How can I define and call it from C#?

5
  • 1
    I think you need to change the way those arrays are declared. If I'm not mistaken, dynamic arrays like that isn't really compatible with anything but Delphi. I could be wrong though. Commented May 11, 2011 at 15:59
  • 3
    Do you have access to the Delphi source? I ask this because that signature cannot be called from C# without an extraordinary amount of hacking. string is not an interop-type, it's native Delphi. And Pascal open arrays are also a no-go. The double parameter is fine though!! Commented May 11, 2011 at 16:00
  • I have thye source the delphi source code. Commented May 11, 2011 at 16:02
  • Are the two arrays the same length? So each name and ID item are really pairs? Commented May 11, 2011 at 16:05
  • Yes they are. The array size is not fixed but I always know thier size (Both in C# and Delphi code). Commented May 11, 2011 at 17:40

2 Answers 2

5

This is a bit of a messy P/Invoke because you can't (to the best of my admittedly limited knowledge) use any of the built-in easy marshalling techniques. Instead you need to use Marshal.StructureToPtr like this:

C#

[StructLayout(LayoutKind.Sequential)]
public struct MyItem
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string Name;
    public int ID;
}

[DllImport(@"mydll.dll")]
private static extern void TestFLASHWNewWrapper(IntPtr Items, int Count, ref double d1);

static void Main(string[] args)
{
    MyItem[] items = new MyItem[3];
    items[0].Name = "JFK";
    items[0].ID = 35;
    items[1].Name = "LBJ";
    items[1].ID = 36;
    items[2].Name = "Tricky Dicky";
    items[2].ID = 37;

    IntPtr itemsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyItem))*items.Length);
    try
    {
        Int32 addr = itemsPtr.ToInt32();
        for (int i=0; i<items.Length; i++)
        {
            Marshal.StructureToPtr(items[i], new IntPtr(addr), false);
            addr += Marshal.SizeOf(typeof(MyItem));
        }

        double d1 = 666.0;
        TestFLASHWNewWrapper(itemsPtr, items.Length, ref d1);
        Console.WriteLine(d1);
    }
    finally
    {
        Marshal.FreeHGlobal(itemsPtr);
    }
}

Delphi

TItem = record
  Name: PChar;
  ID: Integer;
end;
PItem = ^TItem;

procedure TestFLASHWNewWrapper(Items: PItem; Count: Integer; var d1: Double); stdcall;
var
  i: Integer;
  name: array of string;
  ID: array of Integer;
begin
  SetLength(name, Count);
  SetLength(ID, Count);
  for i := 0 to Count-1 do begin
    name[i] := Items.Name;
    ID[i] := Items.ID
    inc(Items);
  end;
  TestFLASHWNew(name, ID, d1);
end;

I've implemented it with a wrapper function that calls your TestFLASHWNew function but you'll no doubt want to re-work it.

I've assumed you are using a Delphi with Unicode strings. If not then change [MarshalAs(UnmanagedType.LPWStr)] to [MarshalAs(UnmanagedType.LPStr)].

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. I will check it tomorow and report back!
sloppy cut and paste. It's there now.
Thanks. How to pass a two dimentional array?
I can't answer that in a comment. It's worthy of a new question. That said I think I'd just flatten the array to 1d and pass the dimensions as parameters.
2

The Delphi functions has two issue to be called by non-Delphi code:

  • It uses Delphi strings, which are a proprietary implementation.
  • It uses open arrays, which again are a proprietary implementation.

Knowing how open arrays are implemented, and how the stack is setup to pass them could allow (it is documented) some "hacks" on the other side could be used to read that parameters from the stack. With strings it is a bit more difficult, because their handling is more complex.

What you could do - if you can't change the function - is to define some simpler wrapper around that function, that is callable from C# (or any other language), using PChars instead of strings and passing the array sizes explicitly.

3 Comments

Thanks. Can you please give some sample code or lead me to some sample code of how to use pass array size and using pchar? For example: How can I can create a pchar arrays and how I can convert them to string (on delphi side). I have the source code for DLL, but since it is very big, I can not change it. I can write a new function in delphi to convert input parameters from what is suitable for passing between C#and Delphi and export the new function.
Converting PChars to string in Delphi is pretty easy, just use a typecast or assignment, the compiler will handle it. To pass a variable size array you would need to pass a memory buffer which size is sizeof(Pchar) * number of elements, and number of elements also. Doing that in a "managed" language could be more difficult than it is in "unmanaged" ones.
Another options could be to wrap the Delphi DLL via COM, and then use COM datatypes to marshal data across calls, i.e. using SAFEARRAYS or Variants. See msdn.microsoft.com/en-us/library/aa719104(VS.71).aspx and msdn.microsoft.com/en-us/library/ms221145.aspx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.