导入非托管dll并将指针复制到c#中的字节数组

时间:2020-08-25 09:13:01

标签: marshalling ada unmanaged

我试图将一些非托管的ada代码导入C#并使用元帅复制将其复制到字节数组中,但是我遇到了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;
    }

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

结果证明,我只需要将字节数组传递给函数调用并将其封送为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);
        }
    }