【问题标题】:C# calling Delphi DLL that returns structure within structureC#调用Delphi DLL,返回结构内的结构
【发布时间】:2011-11-23 21:19:08
【问题描述】:

又一个 Delphi 互操作问题......

我有这个 Delphi 代码:

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.

还有这个 C# 代码:

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();
        }
    }
}

当我运行 C# 代码时,testType.MyTestEnum 的控制台输出始终是枚举值 One,即使它在 Delphi 代码中明确设置为 Two。

现在,如果我简单地从使用 TestType 结构中的 TestRecord 结构数组更改为使用简单的整数数组,一切都很好。

为什么整数数组有效,而结构数组却无效?

【问题讨论】:

    标签: c# delphi pinvoke


    【解决方案1】:

    主要问题是TTestRecord 中没有定义任何内容。 C# 代码将其编组为大小为 1 的字段。Delphi 编译器将其视为大小为 0。因此两个结构之间存在不匹配。 C# 代码为 Marshal.SizeOf(typeof(TestType)) 返回 260,而 Delphi 编译器为 SizeOf(TTestType) 返回 8。

    在实际代码中,该记录中可能会有一些实际内容,当你这样做时,一切都会开始工作。

    请注意,@JMarsch 和@Ken White 也提出了有效的观点。您需要确保正确编组枚举并且 struct 布局匹配。由于填充结构的方式,您可能没有对枚举编组做任何事情就逃脱了,但您也可能同样不走运!

    【讨论】:

    • 噢!你曾经有过这样的日子,你只是陷入了思维定势,似乎无法突破。我提供的代码是真实代码的精简示例,但我没有注意到我的精简代码带有空记录会导致这样一个问题。再次感谢所有 cmets。下次我会注意的。
    【解决方案2】:

    自从我使用 Delphi(比如 '98)以来,已经过了一整辈子,但是,我记得,Delphi 中的枚举是 1 字节数字。 c# 中的枚举是整数(32 位)。

    因此,您可以尝试将您的 c# 枚举定义为

    enum TestEnum: byte {One, Two}
    

    这里没有解释的是它是如何与 int 数组一起工作的。关于我能想到的唯一另一件事是确保 c# 枚举的值与 Delphi 枚举的值完全匹配(所以使用 teOne,teTwo),但由于我们实际上是在谈论一个整数/字节,我不明白这有什么关系。

    【讨论】:

    • 我还认为在 c# 中处理枚举类型的方式一定存在问题,而不是在 Delphi 中。它实际上最终成为了一个我忽略的更简单的问题。空唱片一直是罪魁祸首。不过,感谢您的帮助。
    【解决方案3】:

    您需要在 Delphi 代码中设置枚举大小。 Delphi 将使其尽可能小,但 .NET 方面期望int。在枚举声明之前将以下内容添加到您的代码中:

    {$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}
    

    【讨论】:

    • 看我的回答。虽然这个问题是相关的,但房间里还有一头更大的大象。
    • 我的工作是假设发布者知道没有内容的记录是行不通的,并且这是用于解释目的的模拟代码。确实,如果不是,并且发布者试图在真实代码中使用无内容记录,那将是一头更大的大象。 (实际上,重新阅读帖子下方的文字,我想你已经明白了 - 我认为它可能是真正的代码,如“更改...使用简单的整数数组”所示“。很好的收获,对你的回答 +1。)
    • 我实际上确实知道这一点,但我对枚举的视野如此狭隘,以至于我看不到我面前的东西。我和你的想法一样,但是,由于空记录问题,这并没有解决我的问题。我只是无法摆脱我的一个想法。不过,感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 2012-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多