今天遇到了一个奇怪的问题,有2个程序,一个是ClickOnce应用,一个是普通的C#可执行程序,都用到了一个系统环境变量,System environment variable,Machine级别的,程序中都是用的 Environment.GetEnvironmentVariable(env)。当这个环境变量被修改后,发现ClickOnce应用不能运行了,debug看到得到的环境变量不对,还是旧的,重启ClickOnce应用还是不行。但是C#的程序重启之后运行很正常。
然后就看了看MSDN关于Environment.GetEnvironmentVariable的
描述。并且自己做了一些测试,基本搞清楚了原因。
Environment.GetEnvironmentVariable(env)等同于Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.Process),取得时Process的环境变量。另外还有2个函数,分别是Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.Machine),Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.User),分别是取System的环境变量和User的环境变量。
Process的环境变量是指当Process起来的时候,把Machne(System)和User的环境变量加起来,存了个拷贝,放在了Process的信息里面。这个可以用
Process Explorer很清楚地看出来,双击Process,在Environment的tab里面可以看到所有的环境变量。
好的,搞清楚了背景知识,现在我们来看看为什么会出现本文一开始说的那个问题。
对于一个普通的C#程序。
如果我们要取的变量是System Variables,首先调用Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.Machine)和Environment.GetEnvironmentVariable(env)可以得到一样的值。如果这个时候修改了System Variable的值,再调用可以发现Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.Machine)已经是新的值了,但是Environment.GetEnvironmentVariable(env)还是修改前的值。因为Process中保存的是一份拷贝,还是原来的值。重启这个程序就能变得一样。
但是对于ClickOnce应用,变得有点复杂:)
因为ClickOnce是通过dfsvc.exe运行的,这个是ClickOnce的引擎,它负责检查更新,下载等。每当有ClickOnce的应用启动时,都需要通过它。它会自动关掉进程如果15分钟没有使用。参考
What is the dfsvc.exe process that starts when I use "ClickOnce"? - Windows Forms FAQs
所以如果第一次运行ClickOnce程序,dfsvc.exe会拿到一份环境变量的拷贝,然后ClickOnce应用是这个process启动的,所以用的就是它的环境变量拷贝。等关掉ClickOnce应用,这个dfsvc.exe还不会退出(要等15分钟)。再重启ClickOnce还是同样的dfsvc.exe启动的,所以用的还是之前的环境变量拷贝。这就是为什么ClickOnce程序运行不正确。如果kill掉dfsvc.exe,再重启ClickOnce应用就一切正常了。
所以在写代码的时候要注意,如果有程序在运行过程中,System Variables被修改了,使用Environment.GetEnvironmentVariable(env)有可能得不到正确的值。