C# 从非托管代码传递指针

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/835072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 03:32:04  来源:igfitidea点击:

Passing pointers from unmanaged code

c#interopdllimport

提问by Richard

I have a C# project that imports a C dll, the dll has this function:

我有一个导入 C dll 的 C# 项目,该 dll 具有以下功能:

int primary_read_serial(int handle, int *return_code, int *serial, int length);

I want to get access to the serial parameter. I've actually got it to return one character of the serial parameter, but I'm not really sure what I'm doing and would like to understand what is going, and of course get it working.

我想访问串行参数。我实际上已经让它返回串行参数的一个字符,但我不确定我在做什么,并想了解发生了什么,当然让它工作。

So, I'm very sure that the dll is being accessed, other functions without pointers work fine. How do I handle pointers? Do I have to marshal it? Maybe I have to have a fixed place to put the data it?

所以,我非常确定正在访问 dll,其他没有指针的函数工作正常。我如何处理指针?我必须编组它吗?也许我得有一个固定的地方来放数据呢?

An explanation would be great.

一个解释会很棒。

Thanks! Richard

谢谢!理查德

采纳答案by Coincoin

You will have to use an IntPtr and Marshal that IntPtr into whatever C# structure you want to put it in. In your case you will have to marshal it to an int[].

您必须使用 IntPtr 并将 IntPtr 编组到您想要放入的任何 C# 结构中。在您的情况下,您必须将其编组为 int[]。

This is done in several steps:

这是分几个步骤完成的:

  • Allocate an unmanaged handle
  • Call the function with the unamanged array
  • Convert the array to managed byte array
  • Convert byte array to int array
  • Release unmanaged handle
  • 分配一个非托管句柄
  • 使用非托管数组调用函数
  • 将数组转换为托管字节数组
  • 将字节数组转换为 int 数组
  • 释放非托管句柄

That code should give you an idea:

该代码应该给你一个想法:

// The import declaration
[DllImport("Library.dll")]
public static extern int primary_read_serial(int, ref int, IntPtr, int) ;


// Allocate unmanaged buffer
IntPtr serial_ptr = Marshal.AllocHGlobal(length * sizeof(int));

try
{
    // Call unmanaged function
    int return_code;
    int result = primary_read_serial(handle, ref return_code, serial_ptr, length);

    // Safely marshal unmanaged buffer to byte[]
    byte[] bytes = new byte[length * sizeof(int)];
    Marshal.Copy(serial_ptr, bytes, 0, length);

    // Converter to int[]
    int[] ints = new int[length];
    for (int i = 0; i < length; i++)
    {
        ints[i] = BitConverter.ToInt32(bytes, i * sizeof(int));
    }

}
finally
{
    Marshal.FreeHGlobal(serial_ptr);
}

Don't forget the try-finally, or you will risk leaking the unmanaged handle.

不要忘记 try-finally,否则您将面临泄露非托管句柄的风险。

回答by Anzurio

When writing your P/invoke declaration of that function, try using keyword ref for the pointer parameters like this:

在编写该函数的 P/invoke 声明时,尝试使用关键字 ref 作为指针参数,如下所示:

[DllImport("YouDll.dll", EntryPoint = "primary_read_serial")]
public static extern int primary_read_serial(int, ref int, ref int, int) ;

I'm not sure if you need to specify the parameters' name in C#. And remember, when calling that method, you will also have to use ref in the arguments you're passing by reference.

我不确定您是否需要在 C# 中指定参数的名称。请记住,在调用该方法时,您还必须在通过引用传递的参数中使用 ref。

回答by Noldorin

If I understand what you're trying to do, this should work for you.

如果我理解你想要做什么,这应该对你有用。

unsafe
{
    int length = 1024; // Length of data to read.

    fixed (byte* data = new byte[length]) // Pins array to stack so a pointer can be retrieved.
    {
        int returnCode;
        int retValue = primary_read_serial(0, &returnCode, data, length);

        // At this point, `data` should contain all the read data.
    }
}

JaredPar gives you the easy way to do it however, which is just to change your external function declaration so that C# does the marshalling for you in the background.

然而,JaredPar 为您提供了一种简单的方法,只需更改您的外部函数声明,以便 C# 在后台为您进行编组。

Hopefully this gives you an idea of what's happening at a slightly lower level anyway.

希望这能让您了解在稍低的级别上发生的事情。