I'm working on coding a P/Invoke using PowerShell Core, but it fails.
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);
}
My C# code is as:
var pbOut = new byte[pbIn.Length];
int cbOut = 0;
Bindings.EncodeStream(pbIn, pbIn.Length, ref pbOut[0], out cbOut);
It works.
My PowerShell code is as:
$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)
I tried debugging and found GCHandle.AddrOfPinnedObject
returned different address for $pbOut
$pbOut[0]
. So I wonder if it's the point that $pbOut[0]
created new value and PoewrShell referenced the temp value instead.
Any help and test is welcome!
PowerShell's [ref]
type and C#'s ref
keyword function very differently, and you fundamentally cannot obtain a reference to an individual array element in PowerShell.
To work around this limitation, create a C# wrapper method around your P/Invoke method, which:
byte[]
array from PowerShell and uses ref pbOut[0]
internally in the P/Invoke call.cbOut
valueSomething along the following lines (untested):
$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)