2017-08-08 27 views
3

調用AppDomain.DoCallback這是基於堆棧溢出的問題:How to load an assembly as reflection-only in a new AppDomain?從Powershell的

我試圖確定組件的運行時版本,但我穿越的嵌套的文件夾是組件可以加載多次。直接使用加載程序集

[Reflection.Assembly]::ReflectionOnlyLoadFrom($assembly) 

因此無法工作,因爲程序集只能在應用程序域中加載一次。

給出的以下功能在一個單獨的應用程序域加載程序集:

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 
    }) 
} 

此委託的內容輸出到控制檯,而不是執行它:

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

任何幫助/指導讚賞

+0

刪除DoCallback現在去除斷行左括號 –

+0

之間斷行允許執行塊,但失敗:使用「1」參數調用「DoCallBack」的異常:「無法通過委託創建者程序集外的非託管函數指針,動態方法或方法序列化委託。」 –

回答

0

首先想到的是:不要在AppDomain中使用完全獨立的進程。至少,這些(相對)從PowerShell輕鬆啓動。缺點是如果你爲大量文件做這個,它可能會慢得多。

$myAssemblyPath = "C:\..." 
$getImageRuntimeVersion = { 
    [Reflection.Assembly]::ReflectionOnlyLoadFrom($input).ImageRuntimeVersion 
} 
$encodedCommand = [Convert]::ToBase64String(
    [Text.Encoding]::Unicode.GetBytes($getImageRuntimeVersion) 
) 
$imageRuntimeVersion = $myAssemblyPath | powershell -EncodedCommand $encodedCommand 

那麼,有沒有辦法在PowerShell中使用AppDomain來做到這一點?嗯,有,但並不漂亮。你不能使用AppDomain.DoCallBack,因爲正如你發現的那樣,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產生一個內存中的程序集,並且即使你最終得到了在另一個AppDomain中加載的東西,也不是什麼微不足道的事情,因爲你不能在PowerShell中掛接AppDomain.AssemblyResolve。如果這個命令被封裝在一個模塊中,你會提前編譯包含ScriptInvoker的程序集,所以我沒有把這看作是一個優先事項。

0

您無法單獨通過powershell運行DoCallback。但DoCallBack確實可以處理一些內嵌的C#。作爲吉榮說,這是醜陋的,但這個工程:

$assm = "C:\temp\so\bin\dynamic-assembly.dll" 

Add-Type -TypeDefinition @" 

using System.Reflection; 
using System; 

namespace Example 
{ 


    public class AppDomainUtil 
    { 


     public void LoadInAppDomain(AppDomain childDomain, string assemblyName) 
     { 
      childDomain.SetData("assemblyName", assemblyName); 

      childDomain.DoCallBack(new CrossAppDomainDelegate(LoadAssembly)) ; 
     } 

     public static void LoadAssembly() 
     { 

      string assemblyName = (string)AppDomain.CurrentDomain.GetData("assemblyName"); 

      // console not available from another domain 
      string log = "c:\\temp\\hello.txt"; 

      System.IO.File.WriteAllText(log, string.Format("Hello from {0}\r\n",AppDomain.CurrentDomain.FriendlyName)); 

      System.IO.File.AppendAllText(log, string.Format("Assembly to load is {0}\r\n",assemblyName)); 

      Assembly loaded = Assembly.Load(assemblyName); 

      System.IO.File.AppendAllText(log, string.Format("Assemblyloaded: {0}\r\n",loaded.FullName)); 
     } 

    } 



} 
"@ -OutputAssembly $assm -OutputType Library # must set output assembly otherwise assembly generated in-memory and it will break with Type errors. 

Add-Type -Path $assm 

function Load-AssemblyInNewAppDomain([string]$assembly) { 

    Write-Host "Parent domain: $([AppDomain]::CurrentDomain.FriendlyName)" 

    $util = New-Object Example.AppDomainUtil 

    $ads = New-Object System.AppDomainSetup 

    $cd = [AppDomain]::CurrentDomain 

    # set application base 
    $ads.ApplicationBase = [IO.path]::GetDirectoryName($assm) 

    [System.AppDomain]$newDomain = [System.AppDomain]::CreateDomain([System.Guid]::NewGuid().ToString(), $null, $ads); 
    Write-Host "Created child domain: $($newDomain.FriendlyName)" 

    $util.LoadInAppDomain($newDomain, $assembly) 
} 

測試出來:

PS C:\WINDOWS\system32> Load-AssemblyInNewAppDomain "".GetType().Assembly.FullName 

Parent domain: PowerShell_ISE.exe 
Created child domain: 61ab2dbb-8b33-4e7e-84db-5fabfded53aa 

PS C:\WINDOWS\system32> cat C:\temp\hello.txt 

Hello from 61ab2dbb-8b33-4e7e-84db-5fabfded53aa 
Assembly to load is mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 
Assemblyloaded: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089