00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 using System;
00013 using System.Collections.Generic;
00014 using System.Globalization;
00015 using System.IO;
00016 using System.Reflection;
00017 using System.Windows.Forms;
00018 using Mcs.Epm.MicrosoftProject.mpFx.Client.Shared.Forms;
00019 using Mcs.Epm.MicrosoftProject.mpFx.Client.Shared.Properties;
00020
00021 namespace Mcs.Epm.MicrosoftProject.mpFx.Client.Shared
00022 {
00023 public class PluginHost : IDisposable
00024 {
00025 #region Instance Data
00026
00027 private readonly string _StartupPath;
00028 private bool _Disposed;
00029 private readonly List<string> _ExclusionList = new List<string>(Settings.Default.ExclusionList.Split(','));
00030
00031 #endregion
00032
00033 #region Public Events
00034
00035 public event OnStatusChangedEventHandler OnStatusChanged;
00036
00037 #endregion
00038
00039 #region Public Properties
00040
00041 public ProjectServer ProjectServer { get; private set; }
00042 public Form Parent { get; private set; }
00043 public ToolStrip ToolStrip { get; private set; }
00044 public Dictionary<Guid, IMpFxClientPlugin> Plugins { get; private set; }
00045 public PluginGallery Gallery { get; private set; }
00046 #endregion
00047
00048 #region Constructors
00049
00050 public PluginHost(ProjectServer projectServer, Form parent, ToolStrip toolStrip, string startupPath)
00051 {
00052 Tools.ValidateObjectParam(projectServer,
00053 string.Format(CultureInfo.CurrentCulture,
00054 Resources.InvalidArgument,
00055 "projectServer"));
00056
00057 Tools.ValidateObjectParam(parent,
00058 string.Format(CultureInfo.CurrentCulture,
00059 Resources.InvalidArgument,
00060 "parent"));
00061
00062 Tools.ValidateObjectParam(toolStrip,
00063 string.Format(CultureInfo.CurrentCulture,
00064 Resources.InvalidArgument,
00065 "toolStrip"));
00066
00067
00068 Tools.ValidateDirectoryParam(startupPath,
00069 string.Format(CultureInfo.CurrentCulture,
00070 Resources.DirectoryNotFound,
00071 "startupPath"));
00072 ProjectServer = projectServer;
00073 Parent = parent;
00074 ToolStrip = toolStrip;
00075
00076 _StartupPath = startupPath;
00077 _StartupPath = Path.Combine(_StartupPath, Settings.Default.PluginSubDirectory);
00078
00079 if (!Directory.Exists(_StartupPath))
00080 {
00081 Directory.CreateDirectory(_StartupPath);
00082 }
00083
00084 Plugins = new Dictionary<Guid, IMpFxClientPlugin>();
00085
00086 Gallery = new PluginGallery(this);
00087
00088 _Disposed = false;
00089 }
00090
00091 #endregion
00092
00093 #region Public Methods
00094
00095 public void Load()
00096 {
00097 Tools.ValidateDirectoryParam(_StartupPath,
00098 string.Format(CultureInfo.CurrentCulture,
00099 Resources.DirectoryNotFound,
00100 _StartupPath));
00101
00102 string[] directories = Directory.GetDirectories(_StartupPath);
00103
00104 if (directories.Length == 0)
00105 {
00106 throw new MpFxException(Resources.PluginsNotPresent);
00107 }
00108
00109 CreateUserInterFace();
00110
00111 AppDomain testDomain = AppDomain.CreateDomain(Resources.AppTitle);
00112
00113 IInterfaceInspector interfaceInspector = InterfaceInspector.Get(testDomain, Resources.Inspector);
00114
00115 foreach (string directory in directories)
00116 {
00117 if (File.Exists(Path.Combine(directory, "delete")))
00118 {
00119 Tools.DeleteDirectory(directory, true);
00120 continue;
00121 }
00122
00123 string[] files = Directory.GetFiles(directory, "*.dll");
00124
00125 foreach (string file in files)
00126 {
00127 IMpFxClientPlugin plugin = LoadPlugin(interfaceInspector, file, true);
00128 if (plugin != null)
00129 {
00130 break;
00131 }
00132 }
00133 }
00134 }
00135
00136 public void LoadPlugin(Guid guid)
00137 {
00138 if (!Plugins.ContainsKey(guid))
00139 {
00140 throw new ArgumentException(Tools.Format(Resources.InvalidPlugin, guid));
00141 }
00142
00143 IMpFxClientPlugin plugin = Plugins[guid];
00144
00145 if (plugin != null)
00146 {
00147 plugin.OnLoad(ProjectServer, Parent, ToolStrip);
00148 }
00149 }
00150
00151 public void UnloadPlugin(Guid guid)
00152 {
00153 if (!Plugins.ContainsKey(guid))
00154 {
00155 throw new ArgumentException(Tools.Format(Resources.InvalidPlugin, guid));
00156 }
00157
00158 IMpFxClientPlugin plugin = Plugins[guid];
00159
00160 if (plugin != null && plugin.IsLoaded)
00161 {
00162 plugin.OnUnload();
00163 }
00164 }
00165
00166 public IMpFxClientPlugin LoadPlugin(IInterfaceInspector interfaceInspector, string file, bool addToCollection)
00167 {
00168 IMpFxClientPlugin plugin = null;
00169
00170 try
00171 {
00172 string typeName;
00173
00174 if (interfaceInspector.TryGetTypeNameByFileAndInterface(file, Resources.PluginInterface, out typeName))
00175 {
00176 Assembly pluginAssembly = Assembly.LoadFile(file);
00177
00178 Type pluginType = pluginAssembly.GetType(typeName);
00179
00180 if (pluginType != null)
00181 {
00182 plugin = Activator.CreateInstance(pluginType) as IMpFxClientPlugin;
00183
00184 if (plugin != null)
00185 {
00186 plugin.Implementation = file;
00187
00188 if (addToCollection)
00189 {
00190 Plugins.Add(plugin.Guid, plugin);
00191
00192 plugin.OnStatusChanged += plugin_OnStatusChanged;
00193
00194 DoOnStatusChanged(new OnStatusChangedArgs(Tools.Format(Resources.LoadingPlugin,
00195 plugin.Name,
00196 plugin.Version,
00197 plugin.Author)));
00198
00199 plugin.OnLoad(ProjectServer, Parent, ToolStrip);
00200 }
00201
00202 return plugin;
00203 }
00204 }
00205 }
00206 }
00207 catch (Exception)
00208 {
00209 if (plugin != null)
00210 {
00211 plugin.Dispose();
00212 }
00213
00214 throw;
00215 }
00216
00217 return null;
00218 }
00219
00220 public void RemovePlugin(Guid guid)
00221 {
00222 if (!Plugins.ContainsKey(guid))
00223 {
00224 throw new KeyNotFoundException("guid");
00225 }
00226
00227 string implementation = Plugins[guid].Implementation;
00228
00229 implementation = Path.GetDirectoryName(implementation);
00230
00231 if (!Directory.Exists(implementation))
00232 {
00233 throw new DirectoryNotFoundException(implementation);
00234 }
00235
00236 implementation = Path.Combine(implementation, "delete");
00237
00238 File.Create(implementation).Dispose();
00239 }
00240
00241 public string AddPluginToLocalGallery(PluginDescriptor pluginDescriptor, string file)
00242 {
00243 Assembly sourceAssembly = Assembly.LoadFile(file);
00244 List<string> referencedAssembliesPaths = GetDependencies(sourceAssembly);
00245 string targetPath = pluginDescriptor.Name.Replace(" ", "");
00246 targetPath = Path.Combine(_StartupPath, targetPath);
00247
00248 if (Directory.Exists(targetPath))
00249 {
00250 throw new IOException(string.Format("Target directory exists: ' {0}'. Please remove the directory and try again", targetPath));
00251 }
00252
00253 Directory.CreateDirectory(targetPath);
00254
00255 foreach (string referencedAssemblyPath in referencedAssembliesPaths)
00256 {
00257 File.Copy(referencedAssemblyPath, Path.Combine(targetPath, Path.GetFileName(referencedAssemblyPath)));
00258 }
00259
00260 string[] files = Directory.GetFiles(Path.GetDirectoryName(file));
00261
00262 string pluginFilePath = Path.Combine(targetPath, Path.GetFileName(file));
00263
00264 foreach (string otherfile in files)
00265 {
00266 if (!referencedAssembliesPaths.Contains(otherfile) && !otherfile.Equals(file, StringComparison.CurrentCultureIgnoreCase))
00267 {
00268 string otherTargetPath = Path.Combine(targetPath, Path.GetFileName(otherfile));
00269 File.Copy(otherfile, otherTargetPath);
00270 }
00271 }
00272
00273 File.Copy(file, pluginFilePath);
00274
00275 Tools.CopyFiles(Path.GetDirectoryName(file), targetPath, "*.config", false);
00276 Tools.CopyFiles(Path.GetDirectoryName(file), targetPath, "*.pdb", false);
00277
00278 return pluginFilePath;
00279 }
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297 public List<string> GetDependencies(Assembly sourceAssembly)
00298 {
00299 AssemblyName[] referencedAssemblies = sourceAssembly.GetReferencedAssemblies();
00300
00301 string[] files = Directory.GetFiles(Path.GetDirectoryName(sourceAssembly.Location), "*.dll");
00302
00303 Dictionary<AssemblyName, Assembly> assemblies = new Dictionary<AssemblyName, Assembly>();
00304
00305 List<string> referencedAssembliesPaths = new List<string>();
00306 foreach (string file in files)
00307 {
00308 try
00309 {
00310 AssemblyName name = AssemblyName.GetAssemblyName(file);
00311
00312 assemblies.Add(name, Assembly.LoadFile(file));
00313 }
00314 catch (BadImageFormatException) { }
00315 }
00316
00317 foreach (AssemblyName assemblyName in referencedAssemblies)
00318 {
00319 if (!IsInExclusionList(assemblyName, sourceAssembly))
00320 {
00321 foreach (KeyValuePair<AssemblyName, Assembly> pair in assemblies)
00322 {
00323 if (pair.Value.FullName == assemblyName.FullName)
00324 {
00325 Assembly assembly = assemblies[pair.Key];
00326
00327 if (assembly.Location != null)
00328 {
00329 referencedAssembliesPaths.Add(assembly.Location);
00330 }
00331 break;
00332 }
00333 }
00334 }
00335 }
00336
00337 return referencedAssembliesPaths;
00338 }
00339
00340 #endregion
00341
00342 #region Private Methods
00343
00344 private void CreateUserInterFace()
00345 {
00346 DoOnStatusChanged(new OnStatusChangedArgs("Creating user interface..."));
00347
00348 ToolStripDropDownButton toolDropDownButton = Tools.FindToolStripDropDownButtonByText(ToolStrip, "Tools");
00349
00350 if (toolDropDownButton == null)
00351 {
00352 toolDropDownButton = new ToolStripDropDownButton("&Tools");
00353 }
00354
00355 ToolStrip.Items.Insert(ToolStrip.Items.Count - 1, toolDropDownButton);
00356
00357 ToolStripMenuItem pluginsMenuItem = new ToolStripMenuItem("&Plugins");
00358
00359 pluginsMenuItem.Click += pluginsMenuItem_Click;
00360
00361 toolDropDownButton.DropDownItems.Add(pluginsMenuItem);
00362
00363 DoOnStatusChanged(new OnStatusChangedArgs(string.Empty));
00364
00365 }
00366
00367 public void DoOnStatusChanged(OnStatusChangedArgs args)
00368 {
00369 if (OnStatusChanged != null)
00370 {
00371 OnStatusChanged(this, args);
00372 }
00373 }
00374
00375
00376
00377
00378
00379
00380
00381
00382 private bool IsInExclusionList(AssemblyName assemblyName, Assembly sourceAssembly)
00383 {
00384 string assemblyFullName = assemblyName.FullName;
00385
00386 if (assemblyFullName.Equals(sourceAssembly.FullName))
00387 {
00388 return true;
00389 }
00390
00391 foreach (string excludedItem in _ExclusionList)
00392 {
00393 if (assemblyFullName.StartsWith(excludedItem, StringComparison.CurrentCultureIgnoreCase))
00394 {
00395 return true;
00396 }
00397 }
00398
00399 return false;
00400 }
00401
00402 #endregion
00403
00404 #region Event Handlers
00405
00406 void pluginsMenuItem_Click(object sender, EventArgs e)
00407 {
00408 using (PluginManagerForm managerForm = new PluginManagerForm(this))
00409 {
00410 managerForm.ShowDialog(Parent);
00411 }
00412 }
00413
00414 void plugin_OnStatusChanged(object sender, OnStatusChangedArgs args)
00415 {
00416 DoOnStatusChanged(args);
00417 }
00418
00419 #endregion
00420
00421 #region IDisposable members
00422
00423 ~PluginHost()
00424 {
00425 Dispose(false);
00426 }
00427
00428 public void Dispose()
00429 {
00430 Dispose(true);
00431 GC.SuppressFinalize(this);
00432 }
00433
00434 private void Dispose(bool disposing)
00435 {
00436 if (!_Disposed)
00437 {
00438 if (disposing)
00439 {
00440 CleanUp();
00441 }
00442
00443 _Disposed = true;
00444 }
00445 }
00446
00447 private void CleanUp()
00448 {
00449 try
00450 {
00451 foreach (KeyValuePair<Guid, IMpFxClientPlugin> plugin in Plugins)
00452 {
00453 plugin.Value.OnUnload();
00454 plugin.Value.Dispose();
00455 }
00456
00457 Gallery.Dispose();
00458
00459 }
00460 catch (Exception)
00461 {}
00462 }
00463
00464 #endregion
00465
00466 }
00467 }