【问题标题】:Importing an unmanaged dll and copying the pointer to a byte array in c#导入非托管 dll 并将指针复制到 c# 中的字节数组
【发布时间】:2020-12-13 22:56:42
【问题描述】:

我正在尝试将一些非托管 ada 代码导入 C# 并使用 Marshal Copy 将其复制到字节数组中,但是我收到了 System.AccessViolationException。你有什么想法为什么会这样。

    [DllImport(@"Interpreter.dll", EntryPoint = "ReadErrors", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.AsAny)]
    public static extern void ReadErrors(out IntPtr errors);
    
    static IntPtr _errors;

    static unsafe void Main(string[] args)                                                                                      
    {
        ReadErrors(out _errors);
        var managedArray = CopyToByteArrayWithMarshalCopy(_errors);
    }
    static byte[] CopyToByteArrayWithMarshalCopy(IntPtr errors)
    {
        byte[] managedArray = new byte[Marshal.SizeOf(errors)];      
        try
        {
            Marshal.Copy(errors, managedArray, 0, managedArray.Length);
        }
        catch
        {
            Marshal.FreeHGlobal(errors);
        }
        finally
        {
            Marshal.FreeHGlobal(errors);
        }
        return managedArray;
    }

【问题讨论】:

  • 那么ReadErrors在Ada库中的签名是什么?您实际上要复制什么样的数据?新数组managedArray 的初始化长度等于指针errors 本身的大小(4 或8 个字节)。这不太可能是正确的。
  • 程序读取_Errors_S(错误:Error_T);这是签名,Error_T 是 8 的无符号数组字符
  • 但是数组的大小确实不是问题,因为 Marshal 副本只会在指定的长度之前执行此操作。

标签: marshalling ada unmanaged


【解决方案1】:

鉴于问题中有关被调用的 Ada 子程序的信息有限,因此假设调用者必须分配字符数组(字符串缓冲区),下面的最小示例有效(基于编组文档 here 和 SO关于使用 GNAT 构建 DLL 库的答案here):

src/foo.ads

with Interfaces.C;

package Foo is

   package C renames Interfaces.C;   
   
   procedure Initialize
     with Export, Convention => C;
  
   procedure Finalize
     with Export, Convention => C;

   
   subtype Error_T is C.char_array (1 .. 8);
   
   procedure Read_Errors_S (Error : in out Error_T)
     with Export, Convention => C;

end Foo;

src/foo.adb

package body Foo is
   
   ----------------
   -- Initialize --
   ----------------
   
   procedure Initialize is
      procedure fooinit with Import;       --  Generated by binder.
   begin
      fooinit;
   end Initialize;

   --------------
   -- Finalize --
   --------------
   
   procedure Finalize is
      procedure foofinal with Import;      --  Generated by binder.
   begin
      foofinal;
   end Finalize;
   
   -------------------
   -- Read_Errors_S --
   -------------------
   
   procedure Read_Errors_S (Error : in out Error_T) is      
   begin      
      Error := C.To_C ("Error 1");      
   end Read_Errors_S;

end Foo;

foo.gpr

library project Foo is

   for Library_Kind use "dynamic";
   for Library_Name use "foo";
   for Library_Interface use ("foo");
   for Library_Auto_Init use "False";

   for Library_Dir use "lib";
   for Object_Dir use "obj";
   for Source_Dirs use ("src");

end Foo;

lib/libfoo.def

LIBRARY   LIBFOO
EXPORTS
    initialize
    finalize
    read_errors_s

Program.cs

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApp
{
    internal static class LibFoo
    {
        [DllImport(@"libfoo.dll",
            EntryPoint = "initialize",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern void Init();

        [DllImport(@"libfoo.dll",
            EntryPoint = "finalize",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern void Final();

        [DllImport(@"libfoo.dll",
            EntryPoint = "read_errors_s",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern void ReadErrors(StringBuilder error);
    }

    public static class Program
    {
        public static void Main()
        {
            LibFoo.Init();
            
            // Using StringBuilder to allocate a string buffer.
            var sb = new StringBuilder(8);
            LibFoo.ReadErrors(sb);
            Console.WriteLine(sb.ToString());

            LibFoo.Final();
        }
    }
}

【讨论】:

    【解决方案2】:

    原来我只需要将一个字节数组传递给函数调用并将其作为 LPArray 编组。

        [DllImport("Interpreter.dll", EntryPoint = "ReadErrors", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        public static extern void ReadErrors([Out, MarshalAs(UnmanagedType.LPArray)] byte [] errors);
    
        class Program
        {
            static void Main(string[] args)
            {
                byte[] errors = new byte[8];
                ReadErrors(errors);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-23
      • 2011-11-11
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多