首先,我将指出错误的原因实际上与PowerShell无关,它可能位于.NET的更深层,但这正是我遇到的地方。
PowerShell版本:7.1.0
我定义了以下类型,以解决注册表中使用的资源请求(例如,签名与在PInvoke.net上完全相同):
Add-Type -TypeDefinition @"
using System;
using System.Text;
using System.Runtime.InteropServices;
public static class ShellHelper {
[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
}
"@
现在,对于一个测试用例,请考虑以下几点:
$result = [System.Text.StringBuilder]::New();
$tmp = [System.IntPtr]::New(0);
[ShellHelper]::SHLoadIndirectString("@firewallapi.dll,-50323", $result, 256, $tmp) > $null;
$result.ToString()
输出SNMP Trap
已产生,一切看起来都很好。其他一些值也可以正常工作。但是,如果我更换号码-50323
与-50326
例如然后我仍然得到正确的值Block any other traffic to and from SNMPTRAP service
作为输出,但后来当我把几乎所有的动作(即执行命令,甚至只是按Tab键)无休止的一系列Fatal error. Internal CLR error. (0x80131506)
异常被抛出,直到我收到Stack overflow.
消息,PowerShell崩溃到桌面。(请参阅https://imgur.com/a/MYZwhYa。)
错误取决于所处理的字符串,而不是到目前为止的方法调用次数:如果在新会话中首先调用了最后一个命令,则立即发生相同的错误。
除了输出字符串的某些特定属性(其长度,某些字符的存在等)之外,我无法确定甚至无法合理地猜测导致此行为的确切原因。玩弄某些参数(更改第三个参数,对第四个参数使用out int
和[ref]
,等等)似乎不会影响结果。
错误的怪异时机使我大失所望。似乎某些内部过程可能会导致内存中的其他数据被破坏,从而在以后导致错误,但这只是我的一个疯狂猜测。我可能也只是简单地错过了一些显而易见的东西。
造成这种现象的原因可能是什么?如果它是.NET中的真正错误,我应该如何解决它,甚至只是确定确切的发生条件?
StringBuilder
像你一样使用构造函数,不传递0或不传递任何值,则可以有效地为其分配16个字符的容量(默认容量)。你可以通过$result.Capacity
在后面添加来验证这一点New
。
当你调用时,SHLoadIndirectString
你256
将按大小传递,因此该函数假定它可以将那么多字节写入result
缓冲区。这样做是因为现在有了方法可以知道$result
实际上只能占用16个字节,因此会覆盖PowerShell进程中的其他任意内存。
内存损坏错误通常在你实际损坏内存时绝不会导致崩溃,但是以后任何时候都不会导致崩溃。
你需要传递实际可用的大小,如下所示:
$result = [System.Text.StringBuilder]::New(256);
$tmp = [System.IntPtr]::New(0);
[ShellHelper]::SHLoadIndirectString("@firewallapi.dll,-50323", $result, $result.Capacity, $tmp) > $null;
$result.ToString()
谢谢!我以某种方式没有想到StringBuilder无法处理被喂入更大的字符串的问题,因为我只是理所当然地认为它将在内部根据需要增加其容量(我想这仅适用于该API的高级API)然后我们将其直接写入其内容)。但是,现在我肯定在努力地抚摸自己。:)
这可能很有趣。的
StringBuilder
实例未(通过调用例如作为这样Append()
,这将增加容量)由本机代码(或编组),但只是作为一个“缓冲器”来接收数据。因此必须知道该缓冲区的大小。