meyousikmann
meyousikmann

Reputation: 861

C# calling Delphi DLL that returns structure within structure

Yet another Delphi interop question......

I have this Delphi code:

library DelphiDll;

uses
  Dialogs,
  SysUtils,
  Classes;

  type
    TestEnum = (teOne, teTwo);

    TTestRecord = record
    end;

    TTestType = record
      MyTestRecords: array [1..255] of TTestRecord;
      MyTestEnum: TestEnum;
    end;

  {$R *.res}

  function DllFunction(var testType: TTestType): Boolean stdcall; export;

  begin
    testType.MyTestEnum := teTwo;

    Result := True;
  end;

  exports DllFunction;

  begin
  end.

And this C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace DelpiDllTester
{
    public enum TestEnum
    {
        One,
        Two
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TestType
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public TestRecord[] MyTestRecords;
        public TestEnum MyTestEnum;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TestRecord
    {
    }

    class Program
    {
        [DllImport("DelphiDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        public static extern bool DllFunction(ref TestType testType);

        static void Main(string[] args)
        {
            TestType testType = new TestType();
            bool dllFunctionResult = DllFunction(ref testType);

            Console.WriteLine(dllFunctionResult);
            Console.WriteLine(testType.MyTestEnum);

            Console.ReadLine();
        }
    }
}

When I run the C# code, the console output for testType.MyTestEnum is always the enum value One even though it is clearly set to Two in the Delphi code.

Now, if I simply change from using the array of TestRecord structures in the TestType structure to using a simple array of integers, all is well.

Why does the array of integers work yet the array of structures does not?

Upvotes: 2

Views: 1362

Answers (3)

David Heffernan
David Heffernan

Reputation: 613232

The main problem is the fact that TTestRecord has no contents defined in it. The C# code marshals that as a field of size 1. The Delphi compiler regards that as having size 0. And hence there is a mismatch between the two structures. The C# code returns 260 for Marshal.SizeOf(typeof(TestType)) whereas the Delphi compiler returns 8 for SizeOf(TTestType).

In the real code presumably there will be some actual content in that record and when you do that, everything will start to work.

Note that @JMarsch and @Ken White make valid points too. You will need to make sure that the enum is being marshalled correctly and that the struct layouts match. Because of the way structs are padded, you may get away without doing anything to your enum marshalling, but you may equally well be unlucky!

Upvotes: 3

Ken White
Ken White

Reputation: 125727

You need to set the enumeration size in your Delphi code. Delphi will make it as small as possible, but the .NET side expects an int. Add the following to your code before the enumeration declaration:

{$MINENUMSIZE 4}   // can also use {$Z4} but it's not clear later when
                   // you're trying to maintain the code.
                   // Or you can use {$Z-} and {$Z+}, for
                   // {$Z1} and {$Z4} respectively

// Your enum declaration

{$MINENUMSIZE 1}

Upvotes: 2

JMarsch
JMarsch

Reputation: 21751

It's been a whole other lifetime since I used Delphi (like '98), but, as I remember, enums in Delphi were 1 byte numbers. Enums in c# are ints (32-bit).

So, you might try defining your c# enum as an

enum TestEnum: byte {One, Two}

The thing this does not explain is how it worked with an array of int. About the only other thing I can think of is to make sure that the values of the c# enum exactly match those of the Delphi Enum (so use teOne, teTwo), but since we are really talking about an integer/byte under the covers, I don't see how that would matter.

Upvotes: 2

Related Questions