.NET P/Invoke 使用跨度而不是托管数组进行编组

问题描述 投票:0回答:1

我需要从 C# 调用以下非托管函数:

    TLINError __stdcall LIN_GetAvailableHardware(
    HLINHW *pBuff,
    WORD wBuffSize,
    int *pCount
    );

该库提供了此 C# P/Invoke 定义:

[DllImport("plinapi.dll", EntryPoint = "LIN_GetAvailableHardware")]
public static extern TLINError GetAvailableHardware(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    HLINHW[] pBuff,
    ushort wBuffSize,
    out ushort pCount);

此函数接收一个类似 C 的数组,其大小通过

wBuffSize
定义。
pCount
是写入数组的实际项目数。
HLINHW
ushort
的别名。我想知道是否可以以某种方式避免分配托管数组。我的第一直觉是修改 C# 函数定义以接受
Span<ushort>
并传递堆栈分配的跨度作为参数。

[DllImport("plinapi.dll", EntryPoint = "LIN_GetAvailableHardware")]
public static extern TLINError GetAvailableHardware(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    Span<HLINHW> pBuff,
    ushort wBuffSize,
    out ushort pCount);

此操作失败,并出现以下错误:无法封送“参数#1”:无法封送通用类型

我可以通过将定义更改为来解决这个问题:

[DllImport("plinapi.dll", EntryPoint = "LIN_GetAvailableHardware")]
public static extern TLINError GetAvailableHardware(
    ref readonly HLINHW pBuff,
    ushort wBuffSize,
    out ushort pCount);

并这样称呼它:

ReadOnlySpan<ushort> buffer = stackalloc ushort[availableHardwareCount];
status = PLinApi.GetAvailableHardware(ref MemoryMarshal.GetReference(buffer), (ushort)(buffer.Length*sizeof(ushort)), out availableHardwareCount);

是否可以以直接接受跨度的方式定义 C# P/Invoke 定义?如果是这样我该怎么办?还应该仍然有可能将 null 传递到非托管函数中。

如果我们忽略调用

MemoryMarshal.GetReference(buffer)
带来的轻微不便,这种直接跨度 P/Invoke 会对生成的程序产生任何好处。性能、内存?

c# .net pinvoke marshalling
1个回答
0
投票

我已经想通了。自 .NET 7 以来,似乎有一个新的源生成的 P/Invoke 选项。它取代了 DllImport。如果我们现在定义 P/Invoke 函数如下:

[LibraryImport("plinapi.dll", EntryPoint = "LIN_GetAvailableHardware")]
public static partial TLINError GetAvailableHardware(
    ReadOnlySpan<HLINHW> pBuff,
    ushort wBuffSize,
    out ushort pCount);

我们可以简单地调用它:

ReadOnlySpan<ushort> buffer = stackalloc ushort[availableHardwareCount];
PLinApi.GetAvailableHardware(buffer, (ushort)(buffer.Length*sizeof(ushort)), 
out availableHardwareCount);

或者空版本:

PLinApi.GetAvailableHardware(ReadOnlySpan<ushort>.Empty, 0, 
out var availableHardwareCount);
© www.soinside.com 2019 - 2024. All rights reserved.