2010-04-18 105 views
4

我正在開發一個PoSh項目,該項目生成CSharp代碼,然後Add-Type將其存儲到內存中。如何獲取PowerShell添加類型使用添加類型

新類型使用通過Add-Type加載的磁盤DLL中的現有類型。

一切都很好,直到我真的嘗試調用新類型的方法。下面是我在做什麼的例子:運行上面的腳本

$PWD = "." 
rm -Force $PWD\TestClassOne* 
$code = " 
namespace TEST{ 
public class TestClassOne 
{ 
    public int DoNothing() 
    { 
     return 1; 
    } 
} 
}" 
$code | Out-File tcone.cs 
Add-Type -OutputAssembly $PWD\TestClassOne.dll -OutputType Library -Path $PWD\tcone.cs 
Add-Type -Path $PWD\TestClassOne.dll 
$a = New-Object TEST.TestClassOne 
"Using TestClassOne" 
$a.DoNothing() 


"Compiling TestClassTwo" 
Add-Type -Language CSharpVersion3 -TypeDefinition " 
namespace TEST{ 
public class TestClassTwo 
{ 
    public int CallTestClassOne() 
    { 
     var a = new TEST.TestClassOne(); 
     return a.DoNothing(); 
    } 
} 
}" -ReferencedAssemblies $PWD\TestClassOne.dll 
"OK" 
$b = New-Object TEST.TestClassTwo 
"Using TestClassTwo" 
$b.CallTestClassOne() 

給出的最後一行出現以下錯誤:

異常調用「CallTestClassOne」和「0」的說法(S): 「無法加載文件或程序集'TestClassOne,...' 或它的某個依賴項,系統找不到指定的文件。「 在AddTypeTest.ps1:39字符:20 + $ b.CallTestClassOne < < < <() + CategoryInfo:NotSpecified:(:) [],MethodInvocationException + FullyQualifiedErrorId:DotNetMethodException

我在做什麼錯?

回答

4

當你將TestClassTwo輸出到dll(與TestClassOne相同的目錄)和Add-Type它時,它可以工作。或者至少在我的機器上;)所以這是一個醜陋的解決方法。

當調用​​PowerShell的嘗試(從某種原因,我不知道),在這些地點,查詢組件TestClassOne.dll:

LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne.DLL 
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne/TestClassOne.DLL 
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne.EXE 
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne/TestClassOne.EXE 

這是fuslogvw工具輸出。它可能對你有用。使用ProcessMonitor可以看到相同的路徑列表。

您也可以致電CallTestClassOne()

[appdomain]::CurrentDomain.add_assemblyResolve({ 
    $global:x = $args 
}) 
$b.CallTestClassOne() 
$x | fl 

這將告訴你什麼是彙編失敗,更多的一些信息之前,試試這個(

我同意,你期望它應該工作。所以這就是爲什麼這個看起來有些錯誤

+0

謝謝你。我認爲Add-Type沒有獲得它所需的API可用性測試。 – 2010-04-19 12:08:24

+2

它在這些位置查找,因爲這是應用程序的(PowerShell的)基本目錄 - [appdomain] :: CurrentDomain.BaseDirectory。除非你在PowerShell.exe的配置文件(不推薦)中添加專用探測路徑,否則CLR加載器將在那裏或在該位置的某些子分區中查找。 – 2010-04-19 17:20:49

+0

@凱蒂,你說得對。然而,問題是爲什麼PowerShell試圖加載程序集,即使程序集已經加載。奇怪的是:| – stej 2010-04-19 18:18:16

6

發生這種情況是因爲CLR加載器在應用程序(PowerShell的)基目錄中查找了任何程序集當然,它沒有在那找到你的程序集,解決這個問題的最好方法是將h以Stej提到的方式提供AssemblyResolve事件,但用它來告訴CLR程序集所在的位置。您無法使用PowerShell 2.0的Register-ObjectEvent完成此操作,因爲它不適用於需要返回值的事件(即程序集)。在這種情況下,讓我們通過Add-Type使用更多的C#來爲我們完成這項工作。這段代碼的作品:

ri .\TestClassOne.dll -for -ea 0 

$resolver = @' 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Reflection; 
namespace Utils 
{ 
    public static class AssemblyResolver 
    { 
     private static Dictionary<string, string> _assemblies; 

     static AssemblyResolver() 
     { 
      var comparer = StringComparer.CurrentCultureIgnoreCase; 
      _assemblies = new Dictionary<string,string>(comparer); 
      AppDomain.CurrentDomain.AssemblyResolve += ResolveHandler; 
     } 

     public static void AddAssemblyLocation(string path) 
     { 
      // This should be made threadsafe for production use 
      string name = Path.GetFileNameWithoutExtension(path); 
      _assemblies.Add(name, path); 
     } 

     private static Assembly ResolveHandler(object sender, 
               ResolveEventArgs args) 
     { 
      var assemblyName = new AssemblyName(args.Name); 
      if (_assemblies.ContainsKey(assemblyName.Name)) 
      { 
       return Assembly.LoadFrom(_assemblies[assemblyName.Name]); 
      } 
      return null; 
     } 
    } 
} 
'@ 

Add-Type -TypeDefinition $resolver -Language CSharpVersion3 

$code = @' 
namespace TEST { 
    public class TestClassOne { 
     public int DoNothing() { 
      return 1; 
     } 
    } 
} 
'@ 
$code | Out-File tcone.cs 
Add-Type -OutputAssembly TestClassOne.dll -OutputType Library -Path tcone.cs 

# This is the key, register this assembly's location with our resolver utility 
[Utils.AssemblyResolver]::AddAssemblyLocation("$pwd\TestClassOne.dll") 

Add-Type -Language CSharpVersion3 ` 
     -ReferencedAssemblies "$pwd\TestClassOne.dll" ` 
     -TypeDefinition @' 
namespace TEST { 
    public class TestClassTwo { 
     public int CallTestClassOne() { 
      var a = new TEST.TestClassOne(); 
      return a.DoNothing(); 
     } 
    } 
} 
'@ 

$b = new-object Test.TestClassTwo 
$b.CallTestClassOne() 
+1

好的! ;)然而,Posh的行爲是棘手的,因爲人們會認爲第一個程序集已經被加載。 – stej 2010-04-19 18:21:25