随笔 - 55, 评论 - 298, 引用 - 17

导航

关于

我是新人,: )

每月存档

最新留言

  • re:Last day at Microsoft
    <p><a href="http://www.moretiffany.com/">tiffany jewelry</a> Choose, buy...
    by sibat0705(注册) on 2010/3/12 20:48:58
  • bVSVKxjrysxCAx
    zKTeMu &lt;a href=&quot;http://jmvdsaxpwywz.com/&quot;&gt;jmvdsaxpwywz&lt;/a&am...
    by algkkzvif(匿名) on 2010/2/21 22:53:15
  • DLXXSrOqat
    F3OQLk &lt;a href=&quot;http://pfpvdtnczscw.com/&quot;&gt;pfpvdtnczscw&lt;/a&am...
    by sfxnoylvca(匿名) on 2010/2/21 21:28:18
  • iNOutySOJKTsFyl
    kSPmy1 &lt;a href=&quot;http://kdajmdtvcxfu.com/&quot;&gt;kdajmdtvcxfu&lt;/a&am...
    by lydyggun(匿名) on 2010/2/21 20:21:46
  • mDUfBYTmJjTiGrv
    IXumnI &lt;a href=&quot;http://lamuwgvmprtw.com/&quot;&gt;lamuwgvmprtw&lt;/a&am...
    by gacyafcvtas(匿名) on 2010/2/14 4:47:26
  • oiUMbbvrAlLbcr
    ot9Ga3 &lt;a href=&quot;http://nwawfslgnomg.com/&quot;&gt;nwawfslgnomg&lt;/a&am...
    by cvalpp(匿名) on 2010/2/14 4:46:45
  • re:Last day at Microsoft
    写的真好,看了觉得很受到启发,谢谢,
    by Zheying Zheng(匿名) on 2010/2/4 10:35:20
  • re:REST API的身份验证(Authentication)
    <p>顶</p>
    by kekesoft(注册) on 2010/1/27 20:27:43
  • re:Last day at Microsoft
    祝你好运啊
    by Eric v(匿名) on 2010/1/24 1:53:34
  • re:Last day at Microsoft
    鄙人正在打算内部调动,跨过欢德福卡海峡去西雅图呢, 以后互通有无,常联系.
    by Charlie 木匠(匿名) on 2010/1/11 4:23:23
  • re:Last day at Microsoft
    Good luck, buddy.
    by Huimiao Liu(匿名) on 2010/1/10 20:18:24
  • re:Last day at Microsoft
    &lt;p&gt;Oops,you are right. 改正了。&lt;/p&gt;
    by demonfox(匿名) on 2010/1/10 18:22:13
  • re:Last day at Microsoft
    2006年8月14日起至2009年1月8日止 是2010年吧
    by q(匿名) on 2010/1/10 14:59:53
  • re:Last day at Microsoft
    &quot;永远选择你最感兴趣的项目而不是升职空间等所谓的职业发展前景&quot; -- 说的很好!Good luck!
    by CoderZh(匿名) on 2010/1/8 20:49:26
  • re:Chrome OS和Android的背后
    Google当然不是在传统的操作系统上去跟微软计算,手机操作系统,Windows CE是公认的烂,Google在这里竞争没有什么不可以。Chrome OS更多的是Google云计算战略的一部分,人家根...
    by 啊(匿名) on 2009/12/22 13:14:12

广告

 

上周为测试组写了一个用来对测试环境进行配置的小工具,在编写的时候要不停地调试看看功能是否运行正确。我们平时写项目是有unit test framework的,但写个小工具我就难得去加什么正式的unit test了。

不过没unit test,写程序的时候又浑身不舒服,所以就临时想在Powershell里写几个小scrip来做些简单的测试。因为这个工具是用C#写的,所以用Powershell直接调用assembly里的那些函数其实很方便。

在Powershell里要调用一个assembly里的类以及其方法,首先要做的是把这个assembly加载到当前的AppDomain里来。Powershell启动的时候是有一个默认的AppDomain的,其中也已经默认加载了一些.NET的基础assembly,可以用如下命令查看:

image

然后我写了一个简单的脚本来加载需要测试的assembly:

//load.ps1
 
if ($args.Length -ne 1 -or $args[0] -eq "/?" -or $args[0] -eq "-?")
{
  Write-Host "Usage:"
  Write-Host "  load.ps1 <Path to .NET assembly>"
  return
}
 
if (!(Test-Path $args[0]))
{
  Write-Host "ERROR: Specified .NET assembly does not exist"
  return
}
 
$assemblyPath = resolve-path $args[0]
Write-Host "$assemblyPath"
[System.Reflection.Assembly]::LoadFile($assemblyPath)

这样调用:

PS C:\me\Projects\Powershell> .\load.ps1 MathLib.dll

之后你可以再运行一下:[AppDomain]::CurrentDomain.GetAssemblies()来看看结果如何。

Assembly加载进来后就可以调用其中的类型以及函数了,比如你有如下的C#类:

namespace MathLib
{
    public class Math
    {
        public static double Add(double n1, double n2)
        {
            return n1 + n2;
        }
        public static double Subtract(double  n1, double n2)
        {
            return n1 - n2;
        }
        public double Multiply(double n1, double n2)
        {
            return n1 * n2;
        }
        public double Divide(double n1, double n2)
        {
            return n1 / n2;
        }
    }
}

你就可以在Powershell下直接这样调用:

PS C:\me\Projects\Powershell> [MathLib.Math]::Add(1, 2)

如果需要调用的不是static的成员函数也很简单,只要用“New-Object”先生成一个对象就可以调用了:

PS C:\me\Projects\Powershell> $obj = New-Object MathLib.Math
PS C:\me\Projects\Powershell> $obj.Multiply(3, 8)

下面这个小脚本可以快速看一下某个类所提供的成员函数:

//list-methods.ps1
 
$obj = New-Object $args[0]
$methods = $obj.GetType().GetMethods()
foreach ($m in $methods)
{
  Write-Host " - " $m.ReturnParameter.Member
}

 

这之后我其实发现这个方法有一个很大的问题,那就是加载到Powershell默认AppDomain中的assembly是无法动态卸载的,唯一的方法是关闭当前的Powershell窗口。这样的话,如果我们对代码进行了一些改动,但重新编译后却无法覆盖先前的assembly(因为它还是处于被加载的状态,想copy的话系统会报Access Denied)!这确实是很不方便(每次重新编译后,都要关掉Powershell窗口,copy新的assembly,然后再打开一个Powershell)。为了解决这个问题我看了很多资料,试用了N多的方法,不过最终没有找到一个好的解答。

首先要明确的是.NET中没有提供动态卸载单个Assembly的函数(动态加载是可以的,如上文所示)。所以只有通过卸载Assembly所在的那整个AppDomain才可以。不过我们显然不可能卸载Powershell自己所在的当前默认AppDomain,那么自然的答案就是先建立一个新的AppDomain,然后在新的AppDomain里加载所需的Assembly,使用完毕后再卸载整个AppDomain。

这个思路似乎是可行的,但在实际试验中我没有发现任何一种行得通的做法,我试过AppDomain::Load, AppDomain::CreateInstance, AppDomain::CreateInstanceAndUnwrap, AppDomain::CreateInstanceFrom, AppDomain::CreateInstanceFromAndUnwrap等各种方法,都没有成功。其中CreateInstance这一系列函数确实可以生成运行时的一个对象,但因为这些方法返回的结果是一个Object,而在没有将Assembly加载进来前,我又没有相关的类型定义可以用来做typecast,所以空有一个对象却无法调用所需的函数(如果你看完这段还能理解的话,我很佩服;不懂的话也没关系,自己动手试一下很快就明白我的意思了)。

我也查到有些资料说关键是要将你的Aseembly编译为强命名的(strong-named)并且将其加入GAC(过后再从GAC里清理出去即可),比如这篇文章:

http://www.eggheadcafe.com/software/aspnet/30766269/how-to-load--unload-dll.aspx

但我过后发现实际情况并非如此。事实上,你不用把你的assembly加入GAC(strong-named只是允许将assembly加入GAC前的先决条件)也有办法将它加载到一个新的AppDomain中来,下面是我写的代码:

// load-in-new-appdomain.ps1
 
$appDomain = [System.AppDomain]::CreateDomain("TempDomain")
Write-Host "Assemblies in the temp AppDomain"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain"
[AppDomain]::CurrentDomain.GetAssemblies()
 
$appDomain.GetAssemblies()[0].GetType()::LoadFile("C:\me\Projects\Powershell\MathLib.dll")
Write-Host "Assemblies in the temp AppDomain after loading"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain after loading"
[AppDomain]::CurrentDomain.GetAssemblies()
 
[AppDomain]::Unload($appDomain)
 
Write-Host "Assemblies in the temp AppDomain after unloading"
$appDomain.GetAssemblies()
 
Write-Host "Assemblies in the default AppDomain after unloading"
[AppDomain]::CurrentDomain.GetAssemblies()

看上去,我确实是对新建的$appDomain在调用LoadFile,但结果呢:

PS C:\me\Projects\Powershell> .\load-in-new-appdomain.ps1

Assemblies in the temp AppDomain

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

...

Assemblies in the temp AppDomain after loading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll

Assemblies in the default AppDomain after loading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHost\1.0.0.0__31bf3856ad364e35\Micros...
...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll
False  v2.0.50727     C:\me\Projects\Powershell\MathLib.dll

Assemblies in the temp AppDomain after unloading
Exception calling "GetAssemblies" with "0" argument(s): "Attempted to access an unloaded AppDomain."
At C:\me\Projects\Powershell\load-in-new-appdomain.ps1:18 char:25
+ $appDomain.GetAssemblies( <<<< )

Assemblies in the default AppDomain after unloading
True   v2.0.50727     C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHost\1.0.0.0__31bf3856ad364e35\Micros...
...
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll
False  v2.0.50727     C:\me\Projects\Powershell\MathLib.dll

我们的Assembly还是被加载在了默认的AppDomain里!所以我们又回到了square 1,就是我们是无法卸载默认的这个AppDomain的,除非我们关掉Powershell的窗口。

所以最终我没能很好地解决每次重新编译都要关掉Powershell才能copy新的assembly的问题。如果你找到了一个很好的解决方法的话,请不吝赐教。

=========================================================================================================================

后记:写完这篇文章后,我才偶尔看到以下的一篇blog,原来说的是和我同样的意思,呵呵:

Why AppDomains are not a Magic Bullet

打印 | 张贴于 2009-10-09 17:40:21 | Tag:暂无标签

留言反馈

#re:利用Powershell做简单的单元测试 编辑

@ grapef: 调用所测试的类的函数的时候当然是通过reflection的,不过这里问题的关键是我无法将assembly加载到一个新的AppDomain中(以便之后可以卸载),如果能加载成功,后面用Reflection调用什么的都很简单。

@ geniusleft: 这是当然可以的。但所谓的“测试代码执行程序”是不是就是指你平常所用的unit test runner呢?如果是的话,那就违背我的初衷了。我的初衷就是不适用任何unit test framework,只利用Powershell写一点脚本就可以做一些简单的ad hoc的测试。换句话说,Powershell在这里就像我的一个简易的单元测试工具。

@ Colin Han: 多谢分享你的思路,虽然我没有完全理解你所说的中的一些细节,不过你的叙述确实给我提供了一条很好的思路,多谢,我会尽快尝试一下。另外有时间的话也会看一下你的项目的源代码。

再次感谢!

2009-10-12 04:55:22 | [博主:demonfox]
#re:利用Powershell做简单的单元测试 编辑
我不确定这个是不是PowerShell的问题。但是,你的跨域操作的实现似乎有点问题。 一般情况下,如果需要将Assembly加载到另一个AppDomain中,并调用其中的代码。我都需要有一个两个域都会加载的一个Driver Assembly(我自己起的名字)。然后,在新AppDomain中构造Driver(从MashilByRefObject继承)并利用它加载被测Assembly,并进行必要的启动工作。并将Driver的Proxy传递回当前AppDomain (其实就是调用CreateInstanceAddUnwrap)。从而在当前AppDomain中启动远端执行,并进行通信。 这样,当前AppDomain中仅仅加载了Driver Assembly,没有加载被测Assembly,因此不会导致被测Assembly被占用。 具体可以参考我在CodePlex中的开源项目“AssemblyTraveller" http://assemblytraveller.codeplex.com/ 哈哈,对PowerShell不熟,随便发表议论了。
2009-10-10 23:23:34 | [匿名:Colin Han]
#re:利用Powershell做简单的单元测试 编辑
用powershell每次创建测试代码执行程序的进程,通过解析进程的output来验证成功或失败否。 这样可以避免程序及加载到powershell本身程序域的问题。
2009-10-10 12:28:14 | [匿名:geniusleft]
#re:利用Powershell做简单的单元测试 编辑
用reflection调用可以么?
2009-10-10 11:16:34 | [匿名:grapef]

发表留言

标题
姓名
邮件
主页
留言 

Powered by: Joycode.MVC引擎 0.5.2.0