Powershell, PInvoke and GetLastError

5
我正在尝试通过PInvoke从PowerShell使用GetFileSecurity。调用本身有效,但是System.Runtime.InteropServices.Marshal.GetLastWin32Error和GetLastError(我导入进行测试)都返回错误的错误代码。似乎PInvoke签名中的“SetLastError = true”根本没有任何效果,因为在启动新的PowerShell实例后第一次运行脚本时,两者都返回203(ERROR_ENVVAR_NOT_FOUND),但是如果我再次调用相同的脚本,则两者都返回0。
作为一个快速测试,我编写了一个等价的C程序,在那里GetLastError返回预期值(122 == ERROR_INSUFFICIENT_BUFFER)。
请注意,我对PowerShell和.NET一般都很陌生,请原谅任何明显的失误。
$signature = @'
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetFileSecurity(
  string lpFileName,
  int RequestedInformation,
  byte [] pSecurityDescriptor,
  uint nLength,
  out int lpnLengthNeeded
);

[DllImport("Kernel32.dll")]
public static extern uint GetLastError();
'@

$enum = @'
namespace Win32 {
  public enum SECURITY_INFORMATION : uint
  {
    OWNER_SECURITY_INFORMATION        = 0x00000001,
    GROUP_SECURITY_INFORMATION        = 0x00000002,
    DACL_SECURITY_INFORMATION         = 0x00000004,
    SACL_SECURITY_INFORMATION         = 0x00000008,
    UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
    UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
    PROTECTED_SACL_SECURITY_INFORMATION   = 0x40000000,
    PROTECTED_DACL_SECURITY_INFORMATION   = 0x80000000
  }
}
'@


Add-Type -TypeDefinition $enum
Add-Type -MemberDefinition $signature -Name API -Namespace Win32

$len = 0
$sec = New-Object byte[] 0
$info = new-object Win32.SECURITY_INFORMATION
$info = [Win32.SECURITY_INFORMATION]::OWNER_SECURITY_INFORMATION -bor
        [Win32.SECURITY_INFORMATION]::GROUP_SECURITY_INFORMATION -bor
        [Win32.SECURITY_INFORMATION]::DACL_SECURITY_INFORMATION
[Win32.API]::GetFileSecurity("c:\temp", $info, $sec, $sec.Length, [ref] $len)
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
[Win32.API]::GetLastError()
2个回答

3

我建议您创建一个包装器C#类来调用GetFileSecurity函数并获取错误。这样做比直接调用要更好。因为在您调用GetFileSecurity()的pinvoke和GetLastWin32Error()的pinvoke之间,PowerShell可能会调用Win32 API,导致重置Win32错误号码。


谢谢,那个方法可行。我最终用C#写了整个程序,这样看起来更加清晰易读,也不那么疯狂了。 - leromarinvit

1

只要防止PowerShell将GetFileSecurity()的结果直接写入控制台,就可以在PowerShell中保留它。必须在调用GetFileSecurity()之后立即调用GetLastWin32Error(),在其间没有控制台写入或其他Win32 API调用,如Keith Hill所说。

您可以通过将其转换为void来消除该调用:

[void]([Win32.API]::GetFileSecurity("c:\temp", $info, $sec, $sec.Length, [ref] $len))
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error()

或者您可以将其结果保存到一个变量中,稍后再打印:

$result = [Win32.API]::GetFileSecurity("c:\temp", $info, $sec, $sec.Length, [ref] $len)
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Output $result

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接