我正在使用 PowerShell Core 编写 P/Invoke 代码,但失败了。
using System;
using System.Runtime.InteropServices;
public class Bindings
{
[DllImport("MyEncoder.dll")]
public static extern bool EncodeStream(
byte[] pbIn,
int cbIn,
ref byte pbOut,
out int cbOut);
}
我的C#代码如下:
var pbOut = new byte[pbIn.Length];
int cbOut = 0;
Bindings.EncodeStream(pbIn, pbIn.Length, ref pbOut[0], out cbOut);
它有效。
我的PowerShell代码如下:
$Bindings = Add-Type -TypeDefinition $cs_code -PassThru
[byte[]]$pbIn = [IO.File]::ReadAllBytes("src.txt")
$cbIn = $pbIn.Length
$pbOut = [byte[]]::new($cbIn)
$cbOut = [int]0
# PowerShell 7.3.9
# Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
$Bindings::EncodeStream($pbIn, $cbIn, [ref]$pbOut[0], [ref]$cbOut)
我尝试调试,发现
GCHandle.AddrOfPinnedObject
为$pbOut
$pbOut[0]
返回了不同的地址。所以我想知道是否是 $pbOut[0]
创建了新值而 PoewrShell 引用了临时值。
欢迎任何帮助和测试!
[ref]
type 和 C# 的 ref
keyword 功能非常不同,并且您从根本上无法获取对 PowerShell 中单个数组元素的引用。
要解决此限制,请围绕 P/Invoke 方法创建一个 C# wrapper 方法,其中:
byte[]
数组cbOut
值大致如下(未经测试):
$Bindings = Add-Type -PassThru @'
using System;
using System.Runtime.InteropServices;
public class Bindings
{
public static int EncodeStream(byte[] pbIn, byte[] pbOut)
{
int cbOut;
if (!EncodeStream_Impl(pbIn, pbIn.Length, ref pbOut[0], out cbOut)) {
throw new Exception("Encoding failed.");
}
return cbOut;
}
[DllImport("MyEncoder.dll", EntryPoint="EncodeStream")]
private static extern bool EncodeStream_Impl(
byte[] pbIn,
int cbIn,
ref byte pbOut,
out int cbOut);
}
'@
[byte[]]$pbIn = [IO.File]::ReadAllBytes("src.txt")
$pbOut = [byte[]]::new($pbIn.Length)
$cbOut = $Bindings::EncodeStream($pbIn, $pbOut)