第一个想法:根本不要乱搞 AppDomains 并使用完全独立的process。至少,这些可以(相对)轻松地从 PowerShell 启动。缺点是,如果您对大量文件执行此操作,速度可能会慢得多。
$myAssemblyPath = "C:\..."
$getImageRuntimeVersion = {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($input).ImageRuntimeVersion
}
$encodedCommand = [Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes($getImageRuntimeVersion)
)
$imageRuntimeVersion = $myAssemblyPath | powershell -EncodedCommand $encodedCommand
那么,有没有办法在 PowerShell 中使用 AppDomains 来做到这一点呢?嗯,有,但不太漂亮。你不能使用AppDomain.DoCallBack
因为,正如您所发现的,PowerShell 无法以这种方式进行远程委托(因为,在幕后,它会生成动态方法)。
然而,托管 PowerShell 运行时很容易,并且所有 PowerShell 对象都知道如何序列化(跨域远程处理的要求),因此在另一个 AppDomain 中调用 PowerShell 脚本相当简单(但仍然很难看):
$scriptInvokerAssembly = [System.IO.Path]::GetTempFileName() + ".dll"
Add-Type -OutputAssembly $tempAssembly -TypeDefinition @"
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Management.Automation;
public class ScriptInvoker : MarshalByRefObject {
public IEnumerable<PSObject> Invoke(ScriptBlock scriptBlock, PSObject[] parameters) {
using (var powerShell = PowerShell.Create()) {
powerShell.Commands.AddScript(scriptBlock.ToString());
if (parameters != null) {
powerShell.AddParameters(parameters);
}
return powerShell.Invoke();
}
}
}
"@
[Reflection.Assembly]::LoadFile($scriptInvokerAssembly) | Out-Null
Function Invoke-CommandInTemporaryAppDomain([ScriptBlock] $s, [object[]] $arguments) {
$setup = New-Object System.AppDomainSetup
$setup.ApplicationBase = Split-Path ([ScriptInvoker].Assembly.Location) -Parent
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid(), $null, $setup)
$scriptInvoker = $domain.CreateInstanceAndUnwrap(
[ScriptInvoker].Assembly.FullName, [ScriptInvoker]
);
$scriptInvoker.Invoke($s, $arguments)
[AppDomain]::Unload($domain)
}
现在你可以做
Invoke-CommandInTemporaryAppDomain {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($args[0]).ImageRuntimeVersion
} $myAssemblyPath
请注意,我们必须在磁盘上生成一个临时程序集并具有AppDomain
从那里加载它。这很丑,但你不能拥有Add-Type
产生一个内存中的程序集,即使你最终得到了一个byte[]
将其加载到另一个 AppDomain 中绝非易事,因为您无法挂钩AppDomain.AssemblyResolve
在 PowerShell 中。如果此命令打包在模块中,您将编译包含以下内容的程序集ScriptInvoker
提前,所以我不认为解决这个问题是一个优先事项。