ãã®èšäºã§ã¯ãAppDomainã䜿çšããŠãã©ã°ã€ã³ãäœæããæ¹æ³ãšãã¯ãã¹ãã¡ã€ã³æäœã説æããããšæããŸãã æ¢åã®TCPChatã¢ããªã±ãŒã·ã§ã³çšã®ãã©ã°ã€ã³ãäœæããŸãã
誰ãèªè»¢è»ãããã-ç«ã®äžã§åé²ã
ãã£ããã¯ãã¡ãã§ãã
ãŸãã ããã§ã¢ããªã±ãŒã·ã§ã³ã¢ãŒããã¯ãã£ã«ã€ããŠèªãããšãã§ããŸã ã ãã®å Žåãã¢ãã«ã«ã®ã¿é¢å¿ããããŸãã æ ¹æ¬çãªå€æŽã¯ãªããäž»èŠãªãšã³ãã£ãã£ïŒ Model Root / A PI / Team ïŒã«ã€ããŠç¥ãã ãã§ååã§ãã
ã¢ããªã±ãŒã·ã§ã³ã«å®è£ ããå¿ èŠããããã®ã«ã€ããŠïŒ
ãã©ã°ã€ã³ã®ã³ãŒãã¯å¥ã®ãã¡ã€ã³ã§å®è¡ããå¿ èŠããããŸããããã©ã°ã€ã³ã®å©ããåããŠã³ãã³ãã®ã»ãããæ¡åŒµã§ããå¿ èŠããããŸãã
æããã«ãã³ãã³ãã¯ããèªäœã§ã¯åŒã³åºãããªããããUIãå€æŽããæ©èœãè¿œå ããå¿ èŠããããŸãã ãããè¡ãããã«ãã¡ãã¥ãŒé ç®ãè¿œå ããããç¬èªã®ãŠã£ã³ããŠãäœæããæ©äŒãæäŸããŸãã
æåŸã«ããªã¢ãŒãã§ä»»æã®ãŠãŒã¶ãŒã®ã¹ã¯ãªãŒã³ã·ã§ãããæ®ãããšãã§ãããã©ã°ã€ã³ãäœæããŸãã
AppDomainã®ç®çã¯äœã§ããïŒ
ã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ã¯ãå¶éãããæš©éã§ã³ãŒããå®è¡ããã¢ããªã±ãŒã·ã§ã³ã®å®è¡äžã«ã©ã€ãã©ãªãã¢ã³ããŒãããããã«å¿ èŠã§ãã ãåç¥ã®ããã«ãã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ããã¢ã»ã³ããªãã¢ã³ããŒãããããšã¯ã§ããŸãããããã¡ã€ã³ãã¢ã³ããŒãããŠãã ããã
ãã¡ã€ã³ãã¢ã³ããŒãããããã«ããããã®éã®çžäºäœçšã¯æå°éã«æããããŸãã
å®éã次ã®ããšãã§ããŸãã
- å¥ã®ãã¡ã€ã³ã§ã³ãŒããå®è¡ããŸãã
- ãªããžã§ã¯ããäœæããå€ã§ææ ŒãããŸãã
- ãªããžã§ã¯ããäœæããåç §ã«ããææ ŒãããŸãã
ããã¢ãŒã·ã§ã³ã«ã€ããŠå°ãïŒ
ææ Œã¯ãåç §ãŸãã¯å€ã«ãã£ãŠçºçããŸãã
å€ãããã°ããã¹ãŠãæ¯èŒçåçŽã§ãã ã¯ã©ã¹ã¯1ã€ã®ãã¡ã€ã³ã§ã·ãªã¢ã«åããããã€ãã®é åã«ãã£ãŠå¥ã®ãã¡ã€ã³ã«è»¢éãããéã·ãªã¢ã«åããããªããžã§ã¯ãã®ã³ããŒãååŸãããŸãã åæã«ãã¢ã»ã³ããªãäž¡æ¹ã®ãã¡ã€ã³ã«ããŒãããå¿ èŠããããŸãã ã¢ã»ã³ããªãã¡ã€ã³ãã¡ã€ã³ã«èªã¿èŸŒãŸãªãããã«ããå¿ èŠãããå Žåã¯ãã¢ããªã±ãŒã·ã§ã³ãæ¢å®ã®ã¢ã»ã³ããªïŒAppDomain.BaseDirectory / AppDomainSetup.PrivateBinPathïŒãæ¢ããã©ã«ããŒã®ãªã¹ãã«ããã©ã°ã€ã³ã®ãããã©ã«ããŒãè¿œå ããªãæ¹ãè¯ãã§ãããã ãã®å ŽåãåãèŠã€ãããªãã£ããšããäŸå€ãçºçãããµã€ã¬ã³ãããŒããããã¢ã»ã³ããªãåãåããŸããã
ãªã³ã¯ããã¢ãŒã·ã§ã³ãå®è¡ããã«ã¯ãã¯ã©ã¹ã§MarshalByRefObjectãå®è£ ããå¿ èŠããããŸã ã ãã®ãããªãªããžã§ã¯ãããšã«ãCreateInstanceAndUnwrapã¡ãœãããåŒã³åºããåŸãåŒã³åºãåŽãã¡ã€ã³ã«ä»£è¡šãäœæãããŸãã ããã¯ããã®ãªããžã§ã¯ãã®ãã¹ãŠã®ã¡ãœãããå«ããªããžã§ã¯ãã§ãïŒãã£ãŒã«ãã¯ãããŸããïŒã ãããã®ã¡ãœããã§ã¯ãç¹å¥ãªã¡ã«ããºã ã䜿çšããŠãå¥ã®ãã¡ã€ã³ã«ããå®éã®ãªããžã§ã¯ãã®ã¡ãœãããåŒã³åºããŸãããããã£ãŠãã¡ãœããã¯ãªããžã§ã¯ããäœæããããã¡ã€ã³ã§ãå®è¡ãããŸãã ãŸãã代衚è ã®å¯¿åœã¯éãããŠãããšèšã䟡å€ããããŸãã äœæåŸã圌ãã¯5åéçåããã¡ãœãããåŒã³åºããã³ã«ã圌ãã®å¯¿åœã¯2åéã«ãªããŸãã ã¬ã³ã¿ã«æéãå€æŽã§ããŸãããã®ãããMarshalByRefObjectã®InitializeLifetimeServiceã¡ãœããããªãŒããŒã©ã€ãã§ããŸãã
ãªã³ã¯ããã¢ãŒã·ã§ã³ã§ã¯ããã©ã°ã€ã³ã䜿çšããŠã¡ã€ã³ã¢ã»ã³ããªãã¡ã€ã³ã«èªã¿èŸŒãå¿ èŠã¯ãããŸããã
ãã£ãŒã«ãã«é¢ããäœè«ïŒ
ããã¯ããªãŒãã³ãã£ãŒã«ãã§ã¯ãªãããããã£ã䜿çšããçç±ã®1ã€ã§ãã æ åœè ãä»ããŠãã£ãŒã«ãã«ã¢ã¯ã»ã¹ã§ããŸããããã¹ãŠåäœãé ããªããŸãã ããã«ããããããé ãåäœããããã«ã¯ãã¯ãã¹ãã¡ã€ã³æäœã䜿çšããå¿ èŠã¯ãããŸãããMarshalByRefObjectããç¶æ¿ããã ãã§ååã§ãã
ã³ãŒãå®è¡ã®è©³çŽ°ïŒ
ã³ãŒãã®å®è¡ã¯ã AppDomain.DoCallBackïŒïŒã¡ãœããã䜿çšããŠå®è¡ãããŸãã
ãã®å Žåãããªã²ãŒãã¯å¥ã®ãã¡ã€ã³ã«ææ Œãããããããããå¯èœã§ããããšã確èªããå¿ èŠããããŸãã
ãããã¯ç§ãåºããããå°ããªåé¡ã§ãïŒ
- ããã¯ã€ã³ã¹ã¿ã³ã¹ã¡ãœããã§ããããã¹ãã¯ã©ã¹ã¯ææ Œã§ããŸããã ãåç¥ã®ããã«ãå眲åä»ãã¡ãœããã®ããªã²ãŒãã«ã¯ã2ã€ã®ã¡ã€ã³ãã£ãŒã«ããã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãžã®ãªã³ã¯ãããã³ã¡ãœãããžã®ãã€ã³ã¿ãŒãæ ŒçŽãããŸãã
- ã¯ããŒãžã£ãŒã䜿çšããŸããã ããã©ã«ãã§ã¯ãã³ã³ãã€ã©ãäœæããã¯ã©ã¹ã¯ã·ãªã¢ã©ã€ãºå¯èœãšããŠããŒã¯ãããŠãããã MarshalByRefObjectãå®è£ ããŠããŸããã ïŒæ®µèœ1ãåç §ïŒ
- MarshalByRefObjectããã¯ã©ã¹ãç¶æ¿ãããã¡ã€ã³1ã§ã¯ã©ã¹ãäœæãããã®ã€ã³ã¹ã¿ã³ã¹ã¡ãœãããå¥ã®ãã¡ã€ã³2ã§å®è¡ããããšãããšããã¡ã€ã³ã®å¢çã2å亀差ããã³ãŒãããã¡ã€ã³1ã§å®è¡ãããŸãã
ããå§ããŸããã
ãŸããã¢ããªã±ãŒã·ã§ã³ãããŠã³ããŒãã§ãããã©ã°ã€ã³ãèŠã€ããå¿ èŠããããŸãã 1ã€ã®ã¢ã»ã³ããªã«è€æ°ã®ãã©ã°ã€ã³ãããå Žåããããåãã©ã°ã€ã³ã«åå¥ã®ãã¡ã€ã³ãæäŸããå¿ èŠããããŸãã ãããã£ãŠãå¥ã®ãã¡ã€ã³ã§ãåäœããæ å ±ããŒããŒãäœæããå¿ èŠããããŸããããŒããŒã®æåŸã«ããã®ãã¡ã€ã³ãã¢ã³ããŒããããŸãã
ãã©ã°ã€ã³ã«é¢ããããŒãæ å ±ãä¿åããããã®æ§é ã¯ãSerializableå±æ§ã§ããŒã¯ãããŠããŸãã ãã¡ã€ã³éã§é²ã¿ãŸãã
[Serializable] struct PluginInfo { private string assemblyPath; private string typeName; public PluginInfo(string assemblyPath, string typeName) { this.assemblyPath = assemblyPath; this.typeName = typeName; } public string AssemblyPath { get { return assemblyPath; } } public string TypeName { get { return typeName; } } }
æ å ±èªäœã®ããŒããŒã Proxyã¯ã©ã¹ãMarshalByRefObjectãç¶æ¿ããŠããããšã«æ°ä»ããããããŸããã ãã®ãã£ãŒã«ãã¯ãå ¥åããã³åºåãã©ã¡ãŒã¿ãŒã«äœ¿çšãããŸãã ãããŠã圌ã¯ããŒãããŒããŒãã¡ã€ã³ã«äœæãããŸãã
class PluginInfoLoader { private class Proxy : MarshalByRefObject { public string[] PluginLibs { get; set; } public string FullTypeName { get; set; } public List<PluginInfo> PluginInfos { get; set; } public void LoadInfos() { foreach (var assemblyPath in PluginLibs) { var assembly = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(assemblyPath).FullName); foreach (var type in assembly.GetExportedTypes()) { if (type.IsAbstract) continue; var currentBaseType = type.BaseType; while (currentBaseType != typeof(object)) { if (string.Compare(currentBaseType.FullName, FullTypeName, StringComparison.OrdinalIgnoreCase) == 0) { PluginInfos.Add(new PluginInfo(assemblyPath, type.FullName)); break; } currentBaseType = currentBaseType.BaseType; } } } } } public List<PluginInfo> LoadFrom(string typeName, string[] inputPluginLibs) { var domainSetup = new AppDomainSetup(); domainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; domainSetup.PrivateBinPath = "plugins;bin"; var permmisions = new PermissionSet(PermissionState.None); permmisions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)); permmisions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); permmisions.AddPermission(new UIPermission(UIPermissionWindow.AllWindows)); permmisions.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, inputPluginLibs)); List<PluginInfo> result; var pluginLoader = AppDomain.CreateDomain("Plugin loader", null, domainSetup, permmisions); try { var engineAssemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\Engine.dll"); var proxy = (Proxy)pluginLoader.CreateInstanceAndUnwrap(AssemblyName.GetAssemblyName(engineAssemblyPath).FullName, typeof(Proxy).FullName); proxy.PluginInfos = new List<PluginInfo>(); proxy.PluginLibs = inputPluginLibs; proxy.FullTypeName = typeName; proxy.LoadInfos(); result = proxy.PluginInfos; } finally { AppDomain.Unload(pluginLoader); } return result; } }
ããŒãããŒããŒã®æ©èœãå¶éããããã«ããã¡ã€ã³ãžã®äžé£ã®ã¢ã¯ã»ã¹èš±å¯ãããŒãããŒããŒã«è»¢éããŸãã ãªã¹ããèŠããšãããããã«ã3ã€ã®æš©éãèšå®ãããŠããŸãã
- ReflectionPermissionãªãã¬ã¯ã·ã§ã³ã䜿çšããèš±å¯ã
- ãããŒãžã³ãŒããå®è¡ããSecurityPermissionã¢ã¯ã»ã¹èš±å¯ã
- 2çªç®ã®ãã©ã¡ãŒã¿ãŒã§æž¡ããããã¡ã€ã«ãèªã¿åãããã®FileIOPermissionèš±å¯ã
äžéšã®ã¢ã¯ã»ã¹èš±å¯ïŒã»ãšãã©ãã¹ãŠïŒã¯ãéšåã¢ã¯ã»ã¹ãŸãã¯å®å šã¢ã¯ã»ã¹ãšããŠæå®ã§ããŸãã ããŒã·ã£ã«ã¯ã解å床ããšã«ç¹å®ã®ãªã¹ãã䜿çšããŠäžããããŸãã ãã«ã¢ã¯ã»ã¹ããŸãã¯éã«çŠæ¢ã®å Žåãç¶æ ãåå¥ã«è»¢éã§ããŸãã
PermissionState.None-çŠæ¢ããŸãã
PermissionState.Unrestricted-å®å šãªèš±å¯ã
ãã®ä»ã®èš±å¯ã«ã€ããŠã®è©³çŽ°ã¯ããã¡ããåç §ããŠãã ããã ããã§ã¯ãããã©ã«ããã¡ã€ã³ã®ãã©ã¡ãŒã¿ã確èªããããšãã§ããŸã ã
ãã¡ã€ã³ãäœæããã¡ãœããã§ã¯ãAppDomainSetupã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãæž¡ããŸãã 圌ã«ã¯2ã€ã®ãã£ãŒã«ãã®ã¿ãèšå®ãããŠããŸããããã«ãããããã©ã«ãã§ã¢ã»ã³ããªãæ¢ãå¿ èŠãããå ŽæãããããŸãã
ããã«ããã¡ã€ã³ãç®ç«ããªãããã«äœæããåŸãCreateInstanceAndUnwrapã¡ãœãããåŒã³åºããŠãã¢ã»ã³ããªã®å®å šãªååãšåããã©ã¡ãŒã¿ãŒã«æž¡ããŸãã ãã®ã¡ãœããã¯ãããŒããŒãã¡ã€ã³ã«ãªããžã§ã¯ããäœæãããã®å Žåã¯åç §ã«ãã£ãŠããã¢ãŒã·ã§ã³ãå®è¡ããŸãã
ãã©ã°ã€ã³
ç§ã®å®è£ ã®ãã©ã°ã€ã³ã¯ãã¯ã©ã€ã¢ã³ããšãµãŒããŒã«åãããŠããŸãã ãµãŒããŒåŽã¯ã³ãã³ãã®ã¿ãæäŸããŸãã ã¯ã©ã€ã¢ã³ããã©ã°ã€ã³ããšã«åå¥ã®ã¡ãã¥ãŒé ç®ãäœæããããµãŒããŒãã©ã°ã€ã³ãšåæ§ã«ããã£ããã³ãã³ãã®ã»ãããçºè¡ã§ããŸãã
äž¡æ¹ã®ãã©ã°ã€ã³ã«ã¯åæåã¡ãœããããããã¢ãã«äžã§ã©ãããŒãããã·ã¥ããŠéçãã£ãŒã«ãã«ä¿åããŸãã ã³ã³ã¹ãã©ã¯ã¿ãŒã§ãããè¡ãããªãã®ã¯ãªãã§ããïŒ
ããŒãããããã©ã°ã€ã³ã®ååã¯äžæã§ããããªããžã§ã¯ããäœæãããåŸã«ã®ã¿æ€åºãããŸãã ãã®ååã®ãã©ã°ã€ã³ãçªç¶è¿œå ãããŸãããïŒ ãã®åŸãã¢ã³ããŒãããå¿ èŠããããŸãã ååã®ãã©ã°ã€ã³ããŸã ãªãå Žåã¯ãåæåãå®è¡ãããŸãã ããã«ãããããŠã³ããŒããæåããå Žåã«ã®ã¿åæåãä¿èšŒãããŸãã
ãã©ã°ã€ã³èªäœã®åºæ¬ã¯ã©ã¹ã¯æ¬¡ã®ãšããã§ãã
public abstract class Plugin<TModel> : CrossDomainObject where TModel : CrossDomainObject { public static TModel Model { get; private set; } private Thread processThread; public void Initialize(TModel model) { Model = model; processThread = new Thread(ProcessThreadHandler); processThread.IsBackground = true; processThread.Start(); Initialize(); } private void ProcessThreadHandler() { while (true) { Thread.Sleep(TimeSpan.FromMinutes(1)); Model.Process(); OnProcess(); } } public abstract string Name { get; } protected abstract void Initialize(); protected virtual void OnProcess() { } }
CrossDomainObjectã¯ã1ã€ã®ã¡ãœããïŒProcessïŒã®ã¿ãå«ããªããžã§ã¯ãã§ããããã«ããããµããŒãæè¡ã¹ã¿ããã®åç¶æéã®å»¶é·ãä¿èšŒãããŸãã ãã£ããåŽã§ã¯ããã©ã°ã€ã³ãããŒãžã£ãŒã¯ãã¹ãŠã®ãã©ã°ã€ã³ã«å¯ŸããŠ1åã«1ååŒã³åºããŸãã ãã©ã°ã€ã³ã®äžéšã§ã¯ããã©ã°ã€ã³èªäœãã¢ãã«ã©ãããŒã®Processã¡ãœãããžã®åŒã³åºããæäŸããŸãã
public abstract class CrossDomainObject : MarshalByRefObject { public void Process() { } }
ãµãŒããŒããã³ã¯ã©ã€ã¢ã³ããã©ã°ã€ã³ã®åºæ¬ã¯ã©ã¹ïŒ
public abstract class ServerPlugin : Plugin<ServerModelWrapper> { public abstract List<ServerPluginCommand> Commands { get; } } public abstract class ClientPlugin : Plugin<ClientModelWrapper> { public abstract List<ClientPluginCommand> Commands { get; } public abstract string MenuCaption { get; } public abstract void InvokeMenuHandler(); }
ãã©ã°ã€ã³ãããŒãžã£ãŒã¯ããã©ã°ã€ã³ã®ã¢ããããŒããããŠã³ããŒããææãæ åœããŸãã
ããŠã³ããŒããæ€èšããŠãã ããã
private void LoadPlugin(PluginInfo info) { var domainSetup = new AppDomainSetup(); domainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; domainSetup.PrivateBinPath = "plugins;bin"; var permmisions = new PermissionSet(PermissionState.None); permmisions.AddPermission(new UIPermission(PermissionState.Unrestricted)); permmisions.AddPermission(new SecurityPermission( SecurityPermissionFlag.Execution | SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.Assertion)); permmisions.AddPermission(new FileIOPermission( FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, AppDomain.CurrentDomain.BaseDirectory)); var domain = AppDomain.CreateDomain( string.Format("Plugin Domain [{0}]", Path.GetFileNameWithoutExtension(info.AssemblyPath)), null, domainSetup, permmisions); var pluginName = string.Empty; try { var plugin = (TPlugin)domain.CreateInstanceFromAndUnwrap(info.AssemblyPath, info.TypeName); pluginName = plugin.Name; if (plugins.ContainsKey(pluginName)) { AppDomain.Unload(domain); return; } plugin.Initialize(model); var container = new PluginContainer(domain, plugin); plugins.Add(pluginName, container); OnPluginLoaded(container); } catch (Exception e) { OnError(string.Format("plugin failed: {0}", pluginName), e); AppDomain.Unload(domain); return; } }
ããŒãããŒããŒãšåæ§ã«ãæåã«ãã¡ã€ã³ãåæåããŠäœæããŸãã 次ã«ãAppDomain.CreateInstanceFromAndUnwrapã¡ãœããã䜿çšããŠããªããžã§ã¯ããäœæããŸãã äœæåŸããã©ã°ã€ã³ã®ååãåæããããã©ã°ã€ã³ãæ¢ã«è¿œå ãããŠããå Žåã¯ããã©ã°ã€ã³ãæã€ãã¡ã€ã³ãã¢ã³ããŒããããŸãã ãã®ãããªãã©ã°ã€ã³ããªãå ŽåãåæåãããŸãã
ãããŒãžã£ãŒã³ãŒãã®è©³çŽ°ã«ã€ããŠã¯ã ãã¡ããã芧ãã ãã ã
éåžžã«ç°¡åã«è§£æ±ºãããåé¡ã®1ã€ã¯ãã¢ãã«ãžã®ãã©ã°ã€ã³ã¢ã¯ã»ã¹ãæäŸããããšã§ããã ç§ã®ã¢ãã«ã«ãŒãã¯éçã§ãããå¥ã®ãã¡ã€ã³ã§ã¯åæåãããŸããã åãã¡ã€ã³ã®ã¿ã€ããšéçãã£ãŒã«ãã¯ç°ãªããŸãã
ãã®åé¡ã¯ãã¢ãã«ãªããžã§ã¯ããä¿åãããŠããã©ãããŒãäœæããããšã§è§£æ±ºããããã®ã©ãããŒã®ã€ã³ã¹ã¿ã³ã¹ã¯æ¢ã«é²è¡ããŠããŸãã ã¢ãã«ãªããžã§ã¯ãã¯ãåºæ¬ã¯ã©ã¹MarshalByRefObjectã«è¿œå ããã ãã§æžã¿ãŸããã äŸå€ã¯ãã©ããããå¿ èŠããã£ãã¯ã©ã€ã¢ã³ããšãµãŒããŒïŒå¯Ÿç§°ããã®ãµãŒããŒïŒAPIã§ãã ã¯ã©ã€ã¢ã³ãAPIã¯ãã©ã°ã€ã³ãããŒãžã£ãŒã®åŸã«äœæãããã¢ããªã³ãããŠã³ããŒãããæç¹ã§ã¯ãŸã ååšããŠããŸããã ã¯ã©ã€ã¢ã³ãã©ãããŒã®äŸ ã
ã¯ã©ã€ã¢ã³ããšãµãŒããŒã®ãã©ã°ã€ã³çšã«ãåºæ¬çãªPluginManagerãå®è£ ãã2ã€ã®ç°ãªããããŒãžã£ãŒãäœæããŸããã ã©ã¡ãã«ãTryGetCommandã¡ãœãããããããã®ãããªIDãæã€ãã€ãã£ãã³ãã³ããèŠã€ãããªãå Žåã察å¿ããAPIã§åŒã³åºãããŸãã 以äžã¯ãGetCommand APIã¡ãœããã®å®è£ ã§ãã
ã³ãŒã
public IClientCommand GetCommand(byte[] message) { if (message == null) throw new ArgumentNullException("message"); if (message.Length < 2) throw new ArgumentException("message.Length < 2"); ushort id = BitConverter.ToUInt16(message, 0); IClientCommand command; if (commandDictionary.TryGetValue(id, out command)) return command; if (ClientModel.Plugins.TryGetCommand(id, out command)) return command; return ClientEmptyCommand.Empty; }
ãã©ã°ã€ã³ã®äœæïŒ
èšè¿°ãããã³ãŒãã«åºã¥ããŠããã©ã°ã€ã³ã®å®è£ ãè©Šã¿ãããšãã§ããŸãã
ã¡ãã¥ãŒé ç®ãã¯ãªãã¯ããŠããã¿ã³ãšããã¹ããã£ãŒã«ãã®ãããŠã£ã³ããŠãéããã©ã°ã€ã³ãäœæããŸãã ãã¿ã³ãã³ãã©ã§ã¯ããã£ãŒã«ãã«ããã¯ããŒã ãå ¥åãããŠãŒã¶ãŒã«ã³ãã³ããéä¿¡ãããŸãã ããŒã ã¯åçãæ®ããããããã©ã«ãã«ä¿åããŸãã ãã®åŸããããã¡ã€ã³ã«ãŒã ã«å ¥ããŠãçããéã£ãŠãã ããã
ããã¯P2Pçžäºäœçšã§ããããããµãŒããŒãã©ã°ã€ã³ãèšè¿°ããå¿ èŠã¯ãããŸããã
ãŸãããããžã§ã¯ããäœæããã¯ã©ã¹ã©ã€ãã©ãªãéžæããŸãã ããã«ããªã³ã¯3ã€ã®äž»èŠãªã¢ã»ã³ããªïŒEngine.dllãLidgren.Network.dllãOpenAL.dllãè¿œå ããŸãã æ£ããããŒãžã§ã³ã®.NET Frameworkãé 眮ããããšãå¿ããªãã§ãã ããã3.5ã®ãã£ãããåéããŸãããããã£ãŠããã©ã°ã€ã³ãåãããŒãžã§ã³ä»¥äžã§ãªããã°ãªããŸããã
次ã«ã2ã€ã®ã³ãã³ããæäŸãããã©ã°ã€ã³ã®ã¡ã€ã³ã¯ã©ã¹ãå®è£ ããŸãã ãŸããã¡ãã¥ãŒé ç®ãã³ãã©ãŒã¯ãã€ã¢ãã°ããã¯ã¹ãéããŸãã
ãã©ã°ã€ã³ãããŒãžã£ãã³ãã³ãããµã€ãã«ãã£ãã·ã¥ããããããã©ã°ã€ã³ãããããžã®ãªã³ã¯ãä¿æããå¿ èŠãããããšã«æ³šæããŠãã ããã ãããŠãCommandsããããã£ã¯åãã³ãã³ãã€ã³ã¹ã¿ã³ã¹ãè¿ããŸããã
public class ScreenClientPlugin : ClientPlugin { private List<ClientPluginCommand> commands; public override List<ClientPluginCommand> Commands { get { return commands; } } protected override void Initialize() { commands = new List<ClientPluginCommand> { new ClientMakeScreenCommand(), new ClientScreenDoneCommand() }; } public override void InvokeMenuHandler() { var dialog = new PluginDialog(); dialog.ShowDialog(); } public override string Name { get { return "ScreenClientPlugin"; } } public override string MenuCaption { get { return " "; } } }
ãã€ã¢ãã°ããã¯ã¹ã¯æ¬¡ã®ããã«ãªããŸãã

ã³ãŒã
<Window x:Class="ScreenshotPlugin.PluginDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Screen" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBox Grid.Row="0" Margin="10, 10, 10, 5" MinWidth="200" Name="UserNameTextBox"/> <Button Grid.Row="1" Margin="10, 5, 10, 10" Padding="5, 2, 5, 2" Content=" " Click="Button_Click"/> </Grid> </Window>
public partial class PluginDialog : Window { public PluginDialog() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { ScreenClientPlugin.Model.Peer.SendMessage(UserNameTextBox.Text, ClientMakeScreenCommand.CommandId, null); } }
ãã¡ã€ã«ã転éãããšãã¯ãAPIã䜿çšããŠæ¢ã«äœæããããã£ããæ©èœã䜿çšããŸããã
public class ClientMakeScreenCommand : ClientPluginCommand { public static ushort CommandId { get { return 50000; } } public override ushort Id { get { return ClientMakeScreenCommand.CommandId; } } public override void Run(ClientCommandArgs args) { if (args.PeerConnectionId == null) return; string screenDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "screens"); if (!Directory.Exists(screenDirectory)) Directory.CreateDirectory(screenDirectory); string fileName = Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".bmp"; string fullPath = Path.Combine(screenDirectory, fileName); using (Bitmap bmpScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)) using (Graphics graphic = Graphics.FromImage(bmpScreenCapture)) { graphic.CopyFromScreen( Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, bmpScreenCapture.Size, CopyPixelOperation.SourceCopy); bmpScreenCapture.Save(fullPath); } ScreenClientPlugin.Model.API.AddFileToRoom(ServerModel.MainRoomName, fullPath); var messageContent = Serializer.Serialize(new ClientScreenDoneCommand.MessageContent { FileName = fullPath }); ScreenClientPlugin.Model.Peer.SendMessage(args.PeerConnectionId, ClientScreenDoneCommand.CommandId, messageContent); } }
æãèå³æ·±ãã®ã¯ãæåŸã®3è¡ã§ãã ããã§ã¯ãã«ãŒã ã«ãã¡ã€ã«ãè¿œå ããAPIã䜿çšããŸãã ãã®åŸãå¿çã³ãã³ããéä¿¡ããŸãã ãã¢ã¯ã¡ãœãããªãŒããŒããŒããšåŒã°ãããã€ãã®ã»ãããåãåããŸãã ãã£ããã®ã¡ã€ã³ã¢ã»ã³ããªã§ãªããžã§ã¯ããã·ãªã¢ã«åããããšã¯ã§ããŸããã
以äžã¯ãå¿çãåä¿¡ããã³ãã³ãã®å®è£ ã§ãã 圌女ã¯è²§ãããŠãŒã¶ãŒã®ã¹ã¯ãªãŒã³ã·ã§ãããæ®ã£ãããšãã¡ã€ã³ã«ãŒã å šäœã«çºè¡šããŸãã
public class ClientScreenDoneCommand : ClientPluginCommand { public static ushort CommandId { get { return 50001; } } public override ushort Id { get { return ClientScreenDoneCommand.CommandId; } } public override void Run(ClientCommandArgs args) { if (args.PeerConnectionId == null) return; var receivedContent = Serializer.Deserialize<MessageContent>(args.Message); ScreenClientPlugin.Model.API.SendMessage( string.Format(" {0}.", args.PeerConnectionId), ServerModel.MainRoomName); } [Serializable] public class MessageContent { private string fileName; public string FileName { get { return fileName; } set { fileName = value; } } } }
ãã©ã°ã€ã³ã䜿çšããŠãããžã§ã¯ãå šäœãæçš¿ã§ããŸãããã©ãã«ãããããããŸããã ïŒgithubã®å¥ã®ãªããžããªã®å Žåãéåžžã«å°ãããããç§ã«ã¯æããŸãïŒã
UPD ïŒãã©ã°ã€ã³ãgithubã«æçš¿ããŸããã
UPD 2 ïŒãã®èšäºã§ã¯ããã«ã䜿çšããŠãã©ã°ã€ã³ã®æå¹æéããµããŒãããŠããŸãããããã¯å¥åŠã§ãã githubã®å®è£ ã§ã¯ã åŸã§éåžžã®ã¢ãã«ãå®è£ ããŸããã