导入非托管 dll 并将指针复制到 c# 中的字节数组
Importing an unmanaged dll and copying the pointer to a byte array in c#
我正在尝试将一些非托管 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;
}
鉴于问题中有关被调用的 Ada 子程序的信息有限,因此假设调用者必须分配字符数组(字符串缓冲区),下面的最小示例有效(基于编组文档 here and the SO answer on building DLL libraries with GNAT ):
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();
}
}
}
原来我只需要将一个字节数组传递给函数调用并将其封送为 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);
}
}
我正在尝试将一些非托管 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;
}
鉴于问题中有关被调用的 Ada 子程序的信息有限,因此假设调用者必须分配字符数组(字符串缓冲区),下面的最小示例有效(基于编组文档 here and the SO answer on building DLL libraries with GNAT
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();
}
}
}
原来我只需要将一个字节数组传递给函数调用并将其封送为 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);
}
}