在 PowerShell 脚本中使用 Win32 GetStringTypeW()

Using Win32 GetStringTypeW() in a PowerShell script

我想在 PowerShell 脚本中使用 Win32 GetStringTypeW() 方法。

我在 C# 中找到了正确的签名,下面的代码在那里工作得很好:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern uint GetStringTypeW(uint dwInfoType, string lpSrcStr, int cchSrc, out ushort lpCharType);

private const uint CT_CTYPE1 = 0x0001;

public void MyMethod(string strIn) {
  ushort[] lpCharType = new ushort[strIn.Length];
  GetStringTypeW(CT_CTYPE1, strIn, strIn.Length, out lpCharType[0]);

  // Do stuff with lpCharType
}

lpCharType 数组填充了 16 位无符号整数;一个用于传入的字符串的每个字符。可以通过按位比较来检查整数,以找出字符串中存在哪些 types 个字符。

我在 PowerShell 中将该 C# 代码翻译成以下内容:

$MethodDefinition = @'
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern uint GetStringTypeW(uint dwInfoType, string lpSrcStr, int cchSrc, out ushort lpCharType);
'@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru

[System.UInt32] $CT_CTYPE1 = 0x0001

[System.UInt16[]] $lpCharType = [System.UInt16[]]::new($strIn.Length)

$Kernel32::GetStringTypeW($CT_CTYPE1, $strIn, $strIn.Length, [ref] $lpCharType[0])

# Do stuff with $lpCharType

这不会用任何东西填充 $lpCharType,并且根据我使用该代码的方式,我还可以使用 System.AccessViolationException: Attempted to read or write protected memory.

完全杀死 PowerShell

似乎内存中发生了一些我不完全理解的奇怪事情,所以有人对如何让它工作有什么建议吗?

注意:有趣的是,如果我尝试传入单个 UInt16 参数而不是它们的数组,它会填充一个适当的整数值,因此代码可以正常工作,但当然,它不能保存多个值,这并不能解决访问冲突问题。

如果必须,我可以向 $MethodDefinition 添加中间 C# 方法以接受来自 PowerShell 的字符串,调用 GetStringTypeW(),然后 return 输出,但我希望尽可能避免用 C# 代码填充我的 PowerShell 脚本。

zett42 points out, in PowerShell you cannot obtain a reference to an individual element of a value-type array, as discussed in .

但是,您可以在 P/Invoke 声明中简单地使用 array 参数,并从 PowerShell 中将数组作为一个整体传递:

$MethodDefinition = @'
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern uint GetStringTypeW(uint dwInfoType, 
                          string lpSrcStr, 
                          int cchSrc, 
                          ushort[] lpCharType); // Declared as array, w/o `out`
'@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru

[System.UInt32] $CT_CTYPE1 = 0x0001

$strIn = 'A!'

[uint16[]] $lpCharType = [uint16[]]::new($strIn.Length)

$ok = $Kernel32::GetStringTypeW($CT_CTYPE1, $strIn, -1, $lpCharType)

$lpCharType # -> 897, 528