从C#传递结构数组德尔福 [英] Passing array of struct from c# to delphi

查看:218
本文介绍了从C#传递结构数组德尔福的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的罗伯特·捷托管输出1.2.6 VS2010 我的目标是从 C#(.NET 3.5)传递结构的数组德尔福(D7)
我必须承认,我不是那么熟悉德尔福



我已经阅读过的这个帖子,但建议的答案没'对我来说将不起作用:
当在Delphi中CPU调试窗口调用 FUNC 打开,如果我继续在应用程序退出无一例外,没有期望的结果<。 / p>

下面是我试过的代码:



C#平台86

 使用系统; 
使用System.Collections.Generic;
使用System.Linq的;
使用System.Text;使用System.Runtime.InteropServices
;
使用RGiesecke.DllExport;

命名空间ArrayTest中
{
公共类的Class1
{
公共结构示例
{
[的MarshalAs(UnmanagedType.BStr) ]
公共字符串名称;
}

[DLLEXPORT]
公共静态INT FUNC(
[手续,的MarshalAs(UnmanagedType.LPArray,SizeParamIndex = 1)]
样品[]样本,
裁判INT的len $ b $二)
{
// len个持有// len个被分配一个已项的数目上的输入
中的阵列的长度赋值
//使用返回值来指示成功或失败
的for(int i = 0; I< LEN,我++)
样本[I] .Name点=富 + i.ToString();
返回0;
}
}
}



Delphi7中

 程序DelphiApp; 

{$ APPTYPE CONSOLE}

使用
SysUtils单元,
的ActiveX;


TSAMPLE =记录
名称:WideString的;
端;
PSample = ^ TSAMPLE;

函数func(样本:PSample; VAR LEN:整数):整数; STDCALL;
外部ArrayTest.dll';

程序的Test2;
变量
瓶尿样:TSAMPLE的阵列;
我,LEN:整数;
开始
LEN:= 10;
SetLength函数(样本,LEN);
如果FUNC(PSample(样本),LEN)= 0,那么
为I:= 0到LEN-1做
Writeln(样本[I] .Name点);
端;

开始
的Test2();
端。



如前所述,调试器打开CPU-窗口,如果我继续应用程序退出无一例外或错误信息。
。如果我运行它没有dbugger时,Windows告诉我的应用无法正常运行任何更多的应用程序关闭。



我缺少什么?



更新



修改后的代码:

  [DLLEXPORT] 
公共静态INT FUNC(
[手续,的MarshalAs(UnmanagedType.LPArray,SizeParamIndex = 1)]
样品[]样本,
裁判INT LEN

{
Console.WriteLine(返回0);
返回0;
}

程序的Test2;
变量
瓶尿样:TSAMPLE的阵列;
我,LEN:整数;
开始
LEN:= 10;
SetLength函数(样本,LEN);
如果FUNC(PSample(样本),LEN)= 0,那么
为I:= 0到LEN-1做
Writeln('D7:'我);
端;



就算我不访问两侧的阵列,其行为仍然是相同的。



控制台输出:返回0


解决方案

好像我发现了问题:



如果使用.NET 4.0或更高版本的代码运行正常。如果你使用.NET 3.5或降低len个参数必须按值传递。



请参阅MSDN的文档的 SizeParamIndex V3.5



< BLOCKQUOTE>

包含大小必须是按值传递的整数参数。




请参阅MSDN-文档 SizeParamIndex V4.0




在阵列为C风格的数组通过,封送不能
确定数组的大小。因此,要传递一个托管数组
到非托管函数或方法时,必须提供两个参数:




  • 的阵列,通过引用或值来定义。


  • 数组大小,引用或值。


  • <定义/ UL>


    代码与.NET 3.5的工作:



    C#

      [DLLEXPORT] 
    公共静态INT FUNC(
    [手续,的MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    样品[]样本,
    INT LEN,
    裁判INT outLen

    {
    // LEN持有的长度上输入
    //数组outLen被分配已经分配值
    //使用返回值来指示成功和所需的数组大小项的数目(大于= 0)或失败(小于0)
    INT requiredSize = 20;
    如果(requiredSize< LEN)
    {
    LEN = requiredSize;
    }
    为(outLen = 0; outLen< LEN; outLen ++)
    {
    样本[outLen] .Name点=富+ outLen.ToString();
    }
    返回requiredSize;
    }



    Delphi7中



     函数func(样本:PSample; LEN:整数; VAR outLen:整数):整数; STDCALL; 
    外部ArrayTest.dll';

    程序的Test2;
    变量
    瓶尿样:TSAMPLE的阵列;
    我,LEN:整数;
    开始
    LEN:= 0;
    //查询所需要的数组大小
    I:= FUNC(PSample(样本),LEN,LEN);
    如果我大于0,然后
    开始
    LEN:= I;
    SetLength函数(样本,LEN);
    如果FUNC(PSample(样本),LEN,LEN)> = 0,那么
    为I:= 0到LEN-1做
    Writeln(样本[I] .Name点);
    端;
    端;



    结论:



    中的代码张贴在我的问题和大卫·赫弗南张贴这里只适用于.NET> = 4.0!
    如果你必须使用.NET<!= 3.5,你必须通过引用通过值传递ARRAYSIZE而不是


    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). 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.

    Here is the code I tried:

    C# platform 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

    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. If I run it without dbugger, Windows tells me the app isn't working any more and the app closes.

    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

    解决方案

    Seems like I found the issue:

    The code runs fine if .NET 4.0 or higher is used. If you use .NET 3.5 or lower the len-parameter has to be passed by value.

    See MSDN-documentation SizeParamIndex v3.5:

    The parameter containing the size must be an integer that is passed by value.

    See MSDN-documentation SizeParamIndex v4.0:

    When arrays are passed as C-style arrays, the marshaler cannot determine the size of the array. 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:

    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

    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! If you have to use .NET <= 3.5 you must pass the arraysize by value and not by reference!

    这篇关于从C#传递结构数组德尔福的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆