我正在编写一个将从 PowerShell 脚本调用的本机 Win32 应用程序。它需要以字符串的形式将结果传递回 PowerShell 脚本(因此它不仅仅是一个整数。)
最有效的方法是什么?
以有用的评论为基础:
进程外解决方案是最简单,但效率不高:
将您的应用程序创建为console子系统应用程序,并使其通过其stdout流输出字符串。
$output = .\yourapp.exe ...
)Console.OutputEncoding
中的编码对此类字符串进行解码,默认为系统的旧版 OEM 代码页。
这限制了可以编码的字符,并且需要转码为本机 .NET 字符串(由 UTF-16 代码单元组成)。
但是,只要调用 PowerShell 脚本(临时)设置
[Console]::OutputEncoding = [System.Text.Encoding]::Unicode
,您就可以自由发出 UTF-16LE 字符串; UTF-8 也是如此 ([Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
)。
65001
,即 UTF-8),但请注意,这具有 深远的影响后果 - 请参阅此答案了解详细信息。流程内解决方案,效率更高:
最佳选择是通过C++/CLI创建.NET程序集,如IInspectable建议:
或者,您可以创建一个 本机 DLL,必须通过 P/Invoke 调用它:
Add-Type
cmdlet 通过临时编译 C# 代码间接进行 P/Invoke 调用。
它使用 PowerShell 自己的窗口句柄调用 Unicode 版本的
GetWindowText
请注意如何使用 System.Text.StringBuilder
LPWSTR
类型参数提供预分配的缓冲区,本机函数将 UTF-16 字符串复制到该缓冲区中。
.NET 负责固定和取消固定内存,然后在 .ToString()
StringBuilder
返回复制的字符串。请注意
sb.Capacity
+1
的传递方式,因为本机函数通常需要字符帐户 ,包括终止
NUL
字符,.NET 在幕后自动管理该帐户。
Add-Type -Namespace SampleNS -Name Sample -UsingNamespace System.Text -MemberDefinition @'
// P/Invoke declaration
// Note the use of `CharSet=CharSet.Unicode` to ensure
// that the *Unicode* version is called.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder stringBuf, int nMaxCount);
// Convenience function that wraps the StringBuilder logic
public static string GetWindowText(IntPtr hWnd) {
var sb = new StringBuilder(1024); // Preallocate the buffer.
GetWindowText(hWnd, sb, sb.Capacity+1);
return sb.ToString();
}
'@
# Invoke with the current PowerShell window's handle.
[SampleNS.Sample]::GetWindowText(
(Get-Process -Id $PID).MainWindowHandle
)