PageRenderTime 96ms CodeModel.GetById 37ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/csharp/ch/cyberduck/core/local/RegistryApplicationFinder.cs

https://gitlab.com/vincent.hsu/cyberduck
C# | 362 lines | 306 code | 28 blank | 28 comment | 33 complexity | 650d816a4383df523da3bdf289c61a95 MD5 | raw file
  1. //
  2. // Copyright (c) 2010-2017 Yves Langisch. All rights reserved.
  3. // http://cyberduck.io/
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // Bug fixes, suggestions and comments should be sent to:
  16. // feedback@cyberduck.io
  17. //
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Runtime.InteropServices;
  24. using System.Text;
  25. using ch.cyberduck.core.local;
  26. using Ch.Cyberduck.Core.Collections;
  27. using java.util;
  28. using Microsoft.Win32;
  29. using org.apache.commons.io;
  30. using org.apache.log4j;
  31. namespace Ch.Cyberduck.Core.Local
  32. {
  33. public class RegistryApplicationFinder : ApplicationFinder
  34. {
  35. private static readonly Guid CLSID_QueryAssociations = new Guid("a07034fd-6caa-4954-ac3f-97a27216f98a");
  36. private static readonly Logger Log = Logger.getLogger(typeof(RegistryApplicationFinder).Name);
  37. private static readonly LRUCache<string, Application> applicationNameCache =
  38. new LRUCache<string, Application>(100);
  39. private static readonly LRUCache<string, Application> defaultApplicationCache =
  40. new LRUCache<string, Application>(100);
  41. private static readonly LRUCache<string, IList<Application>> defaultApplicationListCache =
  42. new LRUCache<string, IList<Application>>(100);
  43. private static Guid IID_IQueryAssociations = new Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57");
  44. //vormals GetApplicationNameForExe
  45. public Application getDescription(string application)
  46. {
  47. if (!applicationNameCache.ContainsKey(application))
  48. {
  49. string path = WindowsApplicationLauncher.GetExecutableCommand(application);
  50. if (File.Exists(path))
  51. {
  52. FileVersionInfo info = FileVersionInfo.GetVersionInfo(path);
  53. applicationNameCache.Add(new KeyValuePair<string, Application>(application,
  54. new Application(
  55. application.ToLower(),
  56. info.FileDescription)));
  57. }
  58. else
  59. {
  60. applicationNameCache.Add(new KeyValuePair<string, Application>(application,
  61. new Application(
  62. application.ToLower(),
  63. FilenameUtils.getName(application))));
  64. }
  65. }
  66. Application result;
  67. applicationNameCache.TryGetValue(application, out result);
  68. return result;
  69. }
  70. public Application find(String filename)
  71. {
  72. string extension = Utils.GetSafeExtension(filename);
  73. if (Utils.IsBlank(extension))
  74. {
  75. return Application.notfound;
  76. }
  77. Application app;
  78. Log.debug(string.Format("GetRegisteredDefaultApplication for filename {0}", filename));
  79. if (defaultApplicationCache.TryGetValue(extension, out app))
  80. {
  81. Log.debug(string.Format("Return cached default application {0} for extension {1}", app, extension));
  82. return app;
  83. }
  84. // Step 1 / Check if there is a registered edit command with File Explorer
  85. String exe = GetExplorerRegisteredApplication(extension, "edit");
  86. if (null != exe)
  87. {
  88. defaultApplicationCache.Add(extension, getDescription(exe));
  89. }
  90. // Step 2 / Check registry
  91. if (null == exe)
  92. {
  93. try
  94. {
  95. string strProgID;
  96. using (var extSubKey = Registry.ClassesRoot.OpenSubKey(extension))
  97. {
  98. if (null != extSubKey)
  99. {
  100. strProgID = (string) extSubKey.GetValue(null);
  101. if (null != strProgID)
  102. {
  103. // Get associated application and its edit command
  104. using (var oProgId = Registry.ClassesRoot.OpenSubKey(strProgID))
  105. {
  106. if (null != oProgId)
  107. {
  108. string strExe = GetEditCommand(oProgId.OpenSubKey(strProgID));
  109. if (!string.IsNullOrEmpty(strExe))
  110. {
  111. exe = Utils.ExtractExeFromCommand(strExe);
  112. }
  113. }
  114. }
  115. }
  116. }
  117. if (null != exe)
  118. {
  119. defaultApplicationCache.Add(extension, getDescription(exe));
  120. }
  121. }
  122. }
  123. catch (Exception)
  124. {
  125. Log.error(string.Format("Exception while finding application for {0}", filename));
  126. }
  127. }
  128. // Step 3 / Check if there is a registered open command with File Explorer
  129. if (null == exe)
  130. {
  131. exe = GetExplorerRegisteredApplication(extension, "open");
  132. if (null != exe)
  133. {
  134. defaultApplicationCache.Add(extension, getDescription(exe));
  135. }
  136. }
  137. defaultApplicationCache.TryGetValue(extension, out app);
  138. if (null == app)
  139. {
  140. return Application.notfound;
  141. }
  142. return app;
  143. }
  144. public List findAll(String filename)
  145. {
  146. IList<String> progs = new List<string>();
  147. List<Application> map = new List<Application>();
  148. string extension = Utils.GetSafeExtension(filename);
  149. if (Utils.IsBlank(extension))
  150. {
  151. return Utils.ConvertToJavaList(map);
  152. }
  153. if (!defaultApplicationListCache.ContainsKey(extension))
  154. {
  155. using (RegistryKey clsExt = Registry.ClassesRoot.OpenSubKey(extension))
  156. {
  157. IList<string> rootList = Utils.OpenWithListForExtension(extension, clsExt);
  158. foreach (string s in rootList)
  159. {
  160. progs.Add(s);
  161. }
  162. }
  163. using (
  164. RegistryKey clsExt =
  165. Registry.CurrentUser.OpenSubKey(
  166. "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + extension))
  167. {
  168. IList<string> explorerList = Utils.OpenWithListForExtension(extension, clsExt);
  169. foreach (string s in explorerList)
  170. {
  171. progs.Add(s);
  172. }
  173. }
  174. foreach (string exe in progs.Distinct())
  175. {
  176. Application application = find(exe);
  177. if (isInstalled(application))
  178. {
  179. map.Add(application);
  180. }
  181. else
  182. {
  183. map.Add(getDescription(exe));
  184. }
  185. }
  186. map.Sort(
  187. delegate(Application app1, Application app2)
  188. {
  189. return app1.getIdentifier().CompareTo(app2.getIdentifier());
  190. });
  191. defaultApplicationListCache.Add(extension, map);
  192. }
  193. return Utils.ConvertToJavaList(defaultApplicationListCache[extension]);
  194. }
  195. public bool isInstalled(Application application)
  196. {
  197. return Utils.IsNotBlank(application.getIdentifier()) && File.Exists(application.getIdentifier());
  198. }
  199. [DllImport("shlwapi.dll")]
  200. private static extern int AssocCreate(Guid clsid, ref Guid riid,
  201. [MarshalAs(UnmanagedType.Interface)] out object ppv);
  202. /// <summary>
  203. /// Return with Explorer registered application by file's extension (for editing)
  204. /// </summary>
  205. /// <param name="extension"></param>
  206. /// <param name="verb"></param>
  207. /// <returns></returns>
  208. /// <see cref="http://windevblog.blogspot.com/2008/09/get-default-application-in-windows-xp.html"/>
  209. /// <see cref="http://msdn.microsoft.com/en-us/library/cc144154%28VS.85%29.aspx"/>
  210. private string GetExplorerRegisteredApplication(string extension, string verb)
  211. {
  212. try
  213. {
  214. object obj;
  215. AssocCreate(CLSID_QueryAssociations, ref IID_IQueryAssociations, out obj);
  216. IQueryAssociations qa = (IQueryAssociations) obj;
  217. qa.Init(ASSOCF.INIT_DEFAULTTOSTAR, extension, UIntPtr.Zero, IntPtr.Zero);
  218. int size = 0;
  219. qa.GetString(ASSOCF.NOTRUNCATE, ASSOCSTR.COMMAND, verb, null, ref size);
  220. StringBuilder sb = new StringBuilder(size);
  221. qa.GetString(ASSOCF.NOTRUNCATE, ASSOCSTR.COMMAND, verb, sb, ref size);
  222. string cmd = sb.ToString();
  223. if (Utils.IsBlank(cmd))
  224. {
  225. return null;
  226. }
  227. if (cmd.Contains("\""))
  228. {
  229. return cmd.Substring(1, cmd.IndexOf("\"", 1) - 1);
  230. }
  231. return cmd.Substring(0, cmd.IndexOf(" "));
  232. }
  233. catch (Exception e)
  234. {
  235. return null;
  236. }
  237. }
  238. /// <summary>
  239. /// Extract edit command, fallback is the open command
  240. /// </summary>
  241. /// <param name="root">expected substructure shell/edit/command or shell/open/command</param>
  242. /// <returns>null if not found</returns>
  243. private string GetEditCommand(RegistryKey root)
  244. {
  245. if (null != root)
  246. {
  247. using (var editSk = root.OpenSubKey("shell\\edit\\command"))
  248. {
  249. if (null != editSk)
  250. {
  251. return (string) editSk.GetValue(String.Empty);
  252. }
  253. using (var openSk = root.OpenSubKey("shell\\open\\command"))
  254. {
  255. if (null != openSk)
  256. {
  257. return (string) openSk.GetValue(String.Empty);
  258. }
  259. }
  260. }
  261. }
  262. return null;
  263. }
  264. private enum ASSOCDATA
  265. {
  266. MSIDESCRIPTOR = 1,
  267. NOACTIVATEHANDLER,
  268. QUERYCLASSSTORE,
  269. HASPERUSERASSOC,
  270. EDITFLAGS,
  271. VALUE
  272. }
  273. [Flags]
  274. private enum ASSOCF
  275. {
  276. INIT_NOREMAPCLSID = 0x00000001,
  277. INIT_BYEXENAME = 0x00000002,
  278. OPEN_BYEXENAME = 0x00000002,
  279. INIT_DEFAULTTOSTAR = 0x00000004,
  280. INIT_DEFAULTTOFOLDER = 0x00000008,
  281. NOUSERSETTINGS = 0x00000010,
  282. NOTRUNCATE = 0x00000020,
  283. VERIFY = 0x00000040,
  284. REMAPRUNDLL = 0x00000080,
  285. NOFIXUPS = 0x00000100,
  286. IGNOREBASECLASS = 0x00000200,
  287. INIT_IGNOREUNKNOWN = 0x00000400
  288. }
  289. private enum ASSOCKEY
  290. {
  291. SHELLEXECCLASS = 1,
  292. APP,
  293. CLASS,
  294. BASECLASS
  295. }
  296. private enum ASSOCSTR
  297. {
  298. COMMAND = 1,
  299. EXECUTABLE,
  300. FRIENDLYDOCNAME,
  301. FRIENDLYAPPNAME,
  302. NOOPEN,
  303. SHELLNEWVALUE,
  304. DDECOMMAND,
  305. DDEIFEXEC,
  306. DDEAPPLICATION,
  307. DDETOPIC,
  308. INFOTIP,
  309. QUICKTIP,
  310. TILEINFO,
  311. CONTENTTYPE,
  312. DEFAULTICON,
  313. SHELLEXTENSION
  314. }
  315. [Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  316. private interface IQueryAssociations
  317. {
  318. void Init([In] ASSOCF flags, [In, MarshalAs(UnmanagedType.LPWStr)] string pszAssoc, [In] UIntPtr hkProgid,
  319. [In] IntPtr hwnd);
  320. void GetString([In] ASSOCF flags, [In] ASSOCSTR str, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
  321. [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszOut, [In, Out] ref int pcchOut);
  322. void GetKey([In] ASSOCF flags, [In] ASSOCKEY str, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
  323. [Out] out UIntPtr phkeyOut);
  324. void GetData([In] ASSOCF flags, [In] ASSOCDATA data, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
  325. [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] out byte[] pvOut,
  326. [In, Out] ref int pcbOut);
  327. void GetEnum(); // not used actually
  328. }
  329. }
  330. }