简体   繁体   English

将结构数组从c#传递给delphi

[英]Passing array of struct from c# to delphi

I am using Robert Giesecke Unmanaged Exports 1.2.6 in VS2010 and my goal is to pass an array of structs from c# (.NET 3.5) to delphi (D7) . 我在VS2010中使用Robert Giesecke Unmanaged Exports 1.2.6 ,我的目标是将一系列结构从c#(.NET 3.5)传递给delphi(D7) I have to admit, that I'm not that familiar with delphi. 我不得不承认,我对德尔福并不熟悉。

I've already read this post , but the suggested answer didn't work for me: When calling func in delphi the CPU-debugging-window opens and if I continue the app exits without exception and without the desired result. 我已经阅读过这篇文章 ,但建议的答案对我不起作用:在delphi中调用func时,CPU调试窗口打开,如果我继续应用程序退出,无异常且没有所需的结果。

Here is the code I tried: 这是我试过的代码:

C# platform x86 C#平台x86

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

namespace ArrayTest
{
    public class Class1
    {
        public struct Sample
        {
            [MarshalAs(UnmanagedType.BStr)]
            public string Name;
        }

        [DllExport]
        public static int func(
            [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
            Sample[] samples,
            ref int len
        )
        {
            // len holds the length of the array on input
            // len is assigned the number of items that have been assigned values 
            // use the return value to indicate success or failure
            for (int i = 0; i < len; i++)
                samples[i].Name = "foo: " + i.ToString();
            return 0;
        }
    }
}

Delphi7 Delphi7中

program DelphiApp;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX;

type
  TSample = record
    Name: WideString;
  end;
  PSample = ^TSample;

function func(samples: PSample; var len: Integer): Integer; stdcall;
  external 'ArrayTest.dll';

procedure Test2;
var
  samples: array of TSample;
  i, len: Integer;
begin
  len := 10;
  SetLength(samples, len);
  if func(PSample(samples), len)=0 then
    for i := 0 to len-1 do
      Writeln(samples[i].Name);
end;

begin
  Test2();
end.

As mentioned earlier, the debugger opens the CPU-Window and if I continue the app exits without exception or error message. 如前所述,调试器打开CPU窗口,如果我继续应用程序退出,无异常或错误消息。 If I run it without dbugger, Windows tells me the app isn't working any more and the app closes. 如果我在没有dbugger的情况下运行它,Windows告诉我应用程序不再工作,应用程序关闭。

What am I missing? 我错过了什么?

Update 更新

Modified code: 修改后的代码

[DllExport]
public static int func(
    [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    Sample[] samples,
    ref int len
)
{
    Console.WriteLine("return 0");
    return 0;
}

procedure Test2;
var
  samples: array of TSample;
  i, len: Integer;
begin
  len := 10;
  SetLength(samples, len);
  if func(PSample(samples), len)=0 then
    for i := 0 to len-1 do
      Writeln('D7: ', i);
end;

Even if I don't access the array on either side, the behaviour still is the same. 即使我不访问任何一侧的阵列,行为仍然是相同的。

Console-output: return 0 控制台输出: return 0

Seems like I found the issue: 好像我发现了这个问题:

The code runs fine if .NET 4.0 or higher is used. 如果使用.NET 4.0或更高版本,代码运行正常。 If you use .NET 3.5 or lower the len-parameter has to be passed by value. 如果使用.NET 3.5或更低版本,则len-parameter必须按值传递。

See MSDN-documentation SizeParamIndex v3.5 : 请参阅MSDN文档SizeParamIndex v3.5

The parameter containing the size must be an integer that is passed by value. 包含大小的参数必须是按值传递的整数。

See MSDN-documentation SizeParamIndex v4.0 : 请参阅MSDN文档SizeParamIndex v4.0

When arrays are passed as C-style arrays, the marshaler cannot determine the size of the array. 当数组作为C样式数组传递时,封送程序无法确定数组的大小。 Therefore, to pass an managed array to an unmanaged function or method, you must provide two arguments: 因此,要将托管数组传递给非托管函数或方法,必须提供两个参数:

  • The array, defined by reference or value. 数组,由引用或值定义。

  • The array size, defined by reference or value. 数组大小,由引用或值定义。

Code working with .NET 3.5: 代码使用.NET 3.5:

C# C#

[DllExport]
public static int func(
    [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    Sample[] samples,
    int len,
    ref int outLen
)
{
    // len holds the length of the array on input
    // outLen is assigned the number of items that have been assigned values 
    // use the return value to indicates success and the required array size (>=0) or failure (<0)
    int requiredSize = 20;
    if (requiredSize < len)
    {
        len = requiredSize;
    }
    for (outLen = 0; outLen < len; outLen++)
    {
        samples[outLen].Name = "foo: " + outLen.ToString();
    }
    return requiredSize;
}

Delphi7 Delphi7中

function func(samples: PSample; len: Integer; var outLen: Integer): Integer; stdcall;
  external 'ArrayTest.dll';

procedure Test2;
var
  samples: array of TSample;
  i, len: Integer;
begin
  len := 0;
  // query the required array size
  i := func(PSample(samples), len, len);
  if i>0 then
  begin
    len := i;
    SetLength(samples, len);
    if func(PSample(samples), len, len)>=0 then
      for i := 0 to len-1 do
        Writeln(samples[i].Name);
  end;
end;

Conclusion: 结论:

The code posted in my question and posted by David Heffernan here only works with .NET >= 4.0! 张贴在我的问题和大卫·赫弗南发布的代码在这里只适用于.NET> = 4.0! If you have to use .NET <= 3.5 you must pass the arraysize by value and not by reference! 如果必须使用.NET <= 3.5,则必须按值传递数组,而不是按引用传递!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM