现在的位置: 首页 > 综合 > 正文

关于由ClickOnce部署的应用程序的两种模式和卸载问题

2013年10月11日 ⁄ 综合 ⁄ 共 4456字 ⁄ 字号 评论关闭

在项目的发布选项卡中可以为应用程序选择两种ClickOnce部署模式:

  • 该应用程序只能联机使用
  • 该应用程序也可以脱机使用(可以从“开始”菜单启动)

在这里,我们姑且分别称它们为联机模式和脱机模式。联机模式要求运行应用程序的机器必须与部署应用程序的服务器保持联机才能使用,因为每次运行应用程序都要从服务器的应用程序发布页面(通常是publish.htm)或者由自己创建的快捷方式启动(链向发布页面的setup.exe)。它会联机到服务器验证应用程序并自动下载最新版本到本机的应用程序缓存区(以后简称缓存区),然后启动缓存区中的应用程序。在这个过程中它不会创建卸载程序,也不会自动创建启动应用程序的任何快捷方式。感觉就是在应用程序发布页面上点击“运行”启动了应用程序,关闭之后就不存在了,但是它仍然存放在缓存区。所以在发布联机应用程序时是不需要设置更新的(每次运行的一定是最新的版本),“更新”选项是不可用的。

那么我们自然而然的意识到,部署为联机模式的应用程序,是不存在卸载这一说的。我们能做的只是清空缓冲区的文件。这个缓冲区在类似C:/Documents and Settings/Administrator/Local Settings/Apps/2.0/(2000,XP,2003系统是"%USERPROFILE%/Local Settings/apps/2.0";Vista系统是"%USERPROFILE%/AppData/Local/Apps/2.0" 和"%USERPROFILE%/AppData/Roaming")的目录中创建随机名称的文件夹,用于存放应用程序缓存文件;还存在一个Data文件夹,其中存放的也是随机名称的文件夹,用于存放应用程序的数据缓存文件。我们可以通过Windows SDK6中的mage.exe -cc命令将这里的联机缓存文件清空(参见:《清单生成和编辑工具 (Mage.exe)》
)。

mage.exe所在目录:
C:/Program Files/Microsoft SDKs/Windows/v6.0A/bin/mage.exe

Setting environment for using Microsoft Visual Studio 2008 x86 tools.

e:/Program Files/Microsoft Visual Studio 9.0/VC>mage -?
命令
-New <文件类型> -n
-Update <文件名> -u
-Sign <文件名> -s
-ClearApplicationCache -cc
-Help [verbose] -h -?

Options
-AppCodeBase <路径> -appc
-AppManifest <路径> -appm
-CertFile <文件名> -cf
-CertHash <哈希> -ch
-FromDirectory <路径> -fd
-IconFile <文件路径> -if
-IncludeProviderURL -ip
-Install -i
-Name <名称> -n
-Password <密码> -pwd
-Processor <处理器> -p
-ProviderURL -pu
-Publisher <发行者名称> -pub
-MinVersion <版本号|none> -mv
-SupportURL <支持 url> -s
-TimeStampUri -ti
-ToFile <文件名> -t
-TrustLevel <级别> -tr
-UseManifestForTrust -um
-Version <版本> -v
-WPFBrowserApp -wpf

使用 "mage -help verbose" 了解更详细的帮助

e:/Program Files/Microsoft Visual Studio 9.0/VC>mage -cc
已清除应用程序缓存。

脱机模式将应用程序通过发布页面安装,或者其他存储介质安装,安装过程中可能需要联机到发布服务器,而应用程序会被“安装”到前面说过的本地应用程序缓存区(注意它不会被mage.exe -cc命令清除)。它还会在开始菜单和桌面创建快捷方式,在注册表中当前用户的卸载键中留下应用程序的相关信息(方便从控制面板的“添加删除程序”删除它)。我们还可以设置详细的更新选项,甚至可以通过ApplicationDeployment类编写自己风格的更新代码(见《ClickOnce部署——手动检查更新》
)。

其实我们更关心的是部署为脱机模式的应用程序的卸载问题。通过上图可以看到,注册表中留下了关于应用程序许多有趣的卸载信息。先是由讨厌的16位16进制数值序列组成的键(图中左侧红线的68f29660d055f6eb),然后是图标、名称、发布版本、发行商、快捷方式标识、快捷方式文件名、快捷方式文件夹、联机支持快捷方式名称、卸载命令(图中右侧红线的UninstallString)以及更新地址。这些信息会显示在开始菜单和桌面的快捷方式上,以及“添加删除程序”的卸载信息中。

于是我们可以直接在命令行中粘贴卸载命令(UninstallString项的值),调出应用程序的卸载界面。

rundll32.exe dfshim.dll,ShArpMaintain ClickOnce.application, Culture=neutral, PublicKeyToken=0000000000000000, processorArchitecture=msil

一切并不是那么美好,作为软件的开发者或者应用程序的布置者,我们往往会选择一种“安静地”方式卸载掉过时的应用程序,遗憾的是目前版本的ClickOnce(.NET Framework 3.5 SP1)并不提供安装器的安静模式——这与流行的应用程序安装器相悖。或者说dfshim.dll并没有提供安静模式的入口函数给我们调用(参考《How to uninstall a ClickOnce application silently?》
)。

我更喜欢《How to uninstall a ClickOnce application silently?》
提问中spencery
的答复,他主张由程序调用并找到卸载窗口后发送模拟键盘命令实现自动应答的方法。那么我可以发送SHIFT+TAB和ENTER或者ALT+O(%{O})来确认卸载。

  1. using Microsoft.Win32;  
  2. ///
  3. /// The sole purpose of this class is so we an send SHIFT-TAB and ENTER to the ClickOnce removal dialog.
  4. ///
  5. /// If the click once removal dialog is not yet open, this class will do nothing.
  6. public partial class AutomateKeystrokes : Form  
  7. {  
  8.      [DllImport("user32.dll")]  
  9. private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  
  10.      [DllImport("user32.dll")]  
  11. private static extern bool SetForegroundWindow(IntPtr hWnd);  
  12. public AutomateKeystrokes()  
  13.      {  
  14. this.Load += new System.EventHandler(this.AutomateKeystrokes_OnLoad);  
  15.      }  
  16. private void AutomateKeystrokes_OnLoad(object sender, EventArgs e)  
  17.      {  
  18.          RespondToClickOnceRemovalDialog();  
  19.          Application.Exit();  
  20.      }  
  21. ///
  22. /// Use automated keystrokes to answer "OK" to the ClickOnce removal dialog.
  23. ///
  24. /// Timing is very critical here. Even Debug.Writeline lines can cause this to fail.
  25. private void RespondToClickOnceRemovalDialog()  
  26.      {  
  27.          IntPtr myWindowHandle = IntPtr.Zero;  
  28. for (int i = 0; i < 60 && myWindowHandle == IntPtr.Zero; i++)  
  29.          {  
  30.              Thread.Sleep(500);  
  31.              myWindowHandle = FindWindow(null, _MAGIC_LENS_APPLICATION + " Maintenance");  
  32.          }  
  33. if (myWindowHandle != IntPtr.Zero)  
  34.          {  
  35.              SetForegroundWindow(myWindowHandle);  
  36.              SendKeys.Send("+{TAB}"); // SHIFT-TAB
  37.              SendKeys.Send("{ENTER}");  
  38.              SendKeys.Flush();  
  39.          }  
  40.      }  

事实上在某种情况下,mage.exe -cc命令同样对脱机模式的应用程序有效。假设我们在应用程序运行期间,执行了应用程序的卸载程序。按照常规安装器的管理方式,要么要求关闭应用程序后执行卸载操作,要么依旧执行卸载操作,而将当时无法删除的文件注册给Windows,让系统重新启动时删除这些文件,完成软件的卸载。ClickOnce采取的方式与它们都不同,如果处于这种情况,卸载程序会依旧被执行,但是并不把当时无法删除的文件注册给Windows,即使重新启动操作系统,那些上次没法删除的文件却仍然保留在缓存区里,此时就是执行mage.exe -cc命令最好的时机,它会把这些成为“垃圾”的文件清除掉。

顺便说一下,上面代码第33行FindWindow方法。它的第一个参数指类名(是指API的窗口类名,而不是OOP中的类,每个窗口都应该有自己的类名),第二个参数是指窗口标题名称。对于本例,我们要找到标题为“ClickOnce 维护”的窗口,所以修改此行代码的" Maintenance"为" 维护",并在窗体类代码中声明这样的字段:

  1. readonly string _MAGIC_LENS_APPLICATION = "ClickOnce"; 

 

原帖:http://hi.baidu.com/wingingbob/blog/item/2091f951728d53868c5430ad.html

抱歉!评论已关闭.