gpt4 book ai didi

powershell - 从 Powershell 调用 AppDomain.DoCallback

转载 作者:行者123 更新时间:2023-12-03 18:18:38 25 4
gpt4 key购买 nike

这是基于 Stack Overflow 问题:How to load an assembly as reflection-only in a new AppDomain?

我正在尝试确定程序集的运行时版本,但是当我遍历嵌套文件夹时,该程序集可能会被加载多次。直接使用

加载程序集
[Reflection.Assembly]::ReflectionOnlyLoadFrom($assembly)

因此将不起作用,因为程序集只能在应用程序域中加载一次。

给定以下函数在单独的 AppDomain 中加载程序集:

function Load-AssemblyInNewAppDomain($assembly)
{
Write-Host $assembly.FullName
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid())
$domain.DoCallback
({
$loaded = [Reflection.Assembly]::Load($assembly)
$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime
})
}

这会将委托(delegate)的内容输出到控制台,而不是执行它:

OverloadDefinitions
-------------------
void DoCallBack(System.CrossAppDomainDelegate callBackDelegate)
void _AppDomain.DoCallBack(System.CrossAppDomainDelegate theDelegate)


$loaded = [Reflection.Assembly]::Load($assembly)

$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime

请注意,无论我使用 PowerShell 4 还是 5,结果都是一样的

感谢任何帮助/指导

最佳答案

第一个想法:根本不要乱用 AppDomains,使用完全独立的进程。至少,这些(相对)很容易从 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 无法以这种方式远程委托(delegate)(因为在幕后,它会生成动态方法)。

但是,托管 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 中是非常重要的,因为您不能在 PowerShell 中 Hook AppDomain.AssemblyResolve。如果此命令打包在模块中,您将提前编译包含 ScriptInvoker 的程序集,因此我认为解决此问题不是优先事项。

关于powershell - 从 Powershell 调用 AppDomain.DoCallback,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45562003/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com