2010-04-18 109 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