From 6d5ef05faec83589c36eec6af1019c204e16afe3 Mon Sep 17 00:00:00 2001 From: Jan Scheperski Date: Wed, 23 Sep 2020 00:10:45 +0200 Subject: [PATCH 1/8] added .net core based MtApiService * based on https://github.com/dotnet/wcf * only client side is available in .net core * piping removed, only tcp connection possible * ported MtApi5 and MtApi5TestClient to .net core --- MetaTraderApi_2017.sln | 25 +- MtApi5/MtApi5.csproj | 110 +------ MtApi5/packages.config | 2 +- MtApiServiceNetCore/MtApiProxy.cs | 78 +++++ .../MtApiServiceNetCore.csproj | 45 +++ MtApiServiceNetCore/MtClient.cs | 291 ++++++++++++++++++ MtApiServiceNetCore/MtService.cs | 43 +++ .../Properties/AssemblyInfo.cs | 36 +++ .../MtApi5TestClient/MtApi5TestClient.csproj | 124 +------- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Settings.Designer.cs | 2 +- TestClients/MtApi5TestClient/app.config | 2 +- 12 files changed, 543 insertions(+), 217 deletions(-) create mode 100644 MtApiServiceNetCore/MtApiProxy.cs create mode 100644 MtApiServiceNetCore/MtApiServiceNetCore.csproj create mode 100644 MtApiServiceNetCore/MtClient.cs create mode 100644 MtApiServiceNetCore/MtService.cs create mode 100644 MtApiServiceNetCore/Properties/AssemblyInfo.cs diff --git a/MetaTraderApi_2017.sln b/MetaTraderApi_2017.sln index d8351e85..4010dd14 100644 --- a/MetaTraderApi_2017.sln +++ b/MetaTraderApi_2017.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30413.136 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApiClientUI", "TestClients\TestApiClientUI\TestApiClientUI.csproj", "{663CC515-EAAE-47D4-8933-5008C2DA1160}" EndProject @@ -47,6 +47,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "MtApiBootstrapper", "MtApiB {78B94552-DB17-40EC-B7C6-23D32DB85DC1} = {78B94552-DB17-40EC-B7C6-23D32DB85DC1} EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MtApiServiceNetCore", "MtApiServiceNetCore\MtApiServiceNetCore.csproj", "{7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,6 +234,22 @@ Global {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x64.ActiveCfg = Release|x86 {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x86.ActiveCfg = Release|x86 {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x86.Build.0 = Release|x86 + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Win32.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Win32.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x64.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x86.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Any CPU.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Win32.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Win32.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x64.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x64.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x86.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -241,4 +259,7 @@ Global {38B9C657-BC2F-44F0-8824-54B31F2A64F5} = {B91FF338-E05D-4EF1-948B-A2376DB37ECA} {EB7C228D-9494-4985-845E-B8312450DF3D} = {B91FF338-E05D-4EF1-948B-A2376DB37ECA} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {23C8878C-16A5-47DF-9A57-73CCF847780D} + EndGlobalSection EndGlobal diff --git a/MtApi5/MtApi5.csproj b/MtApi5/MtApi5.csproj index d11ca2de..45a3aae8 100755 --- a/MtApi5/MtApi5.csproj +++ b/MtApi5/MtApi5.csproj @@ -1,120 +1,24 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {AC8B5010-DA75-477E-9CA5-547C649E12D8} + netcoreapp3.0 Library - Properties - MtApi5 - MtApi5 - v4.0 - 512 + false - true - full - false ..\build\products\Debug\ - DEBUG;TRACE - prompt - 4 false - pdbonly - true ..\build\products\Release\ - TRACE - prompt - 4 - - ..\packages\Newtonsoft.Json.12.0.2\lib\net40\Newtonsoft.Json.dll - True - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - {DE76D5C7-B99C-4467-8408-78173BDD84E0} - MTApiService - + - - - - - \ No newline at end of file diff --git a/MtApi5/packages.config b/MtApi5/packages.config index 66b711be..32637c2e 100755 --- a/MtApi5/packages.config +++ b/MtApi5/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/MtApiServiceNetCore/MtApiProxy.cs b/MtApiServiceNetCore/MtApiProxy.cs new file mode 100644 index 00000000..7934fad9 --- /dev/null +++ b/MtApiServiceNetCore/MtApiProxy.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceModel; +using System.ServiceModel.Channels; + +namespace MTApiService +{ + internal class MtApiProxy : IMtApi, IDisposable + { + private IMtApi InnerChannel; + + public CommunicationState State => ((ICommunicationObject)InnerChannel).State; + + public MtApiProxy(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress) + { + var channel = new DuplexChannelFactory(callbackContext, binding, remoteAddress); + channel.Faulted += InnerDuplexChannel_Faulted; + + // configure endpoint programmatically instead via an attribute which will lead to a PlatformNotSupportedException + (channel.Endpoint.EndpointBehaviors.Single(b => b is CallbackBehaviorAttribute) as CallbackBehaviorAttribute).UseSynchronizationContext = false; + + InnerChannel = channel.CreateChannel(); + } + + #region IMtApi Members + + public bool Connect() + { + return InnerChannel.Connect(); + } + + public void Disconnect() + { + InnerChannel.Disconnect(); + } + + public MtResponse SendCommand(MtCommand command) + { + return InnerChannel.SendCommand(command); + } + + public List GetQuotes() + { + return InnerChannel.GetQuotes(); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + try + { + Disconnect(); + } + catch (Exception) + { + + } + } + + #endregion + + #region Private Methods + private void InnerDuplexChannel_Faulted(object sender, EventArgs e) + { + Faulted?.Invoke(this, e); + } + + #endregion + + #region Events + public event EventHandler Faulted; + #endregion + } +} diff --git a/MtApiServiceNetCore/MtApiServiceNetCore.csproj b/MtApiServiceNetCore/MtApiServiceNetCore.csproj new file mode 100644 index 00000000..9aeb655b --- /dev/null +++ b/MtApiServiceNetCore/MtApiServiceNetCore.csproj @@ -0,0 +1,45 @@ + + + netcoreapp3.0 + Library + false + + + ..\build\products\Debug\ + + + ..\build\products\Release\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MtApiServiceNetCore/MtClient.cs b/MtApiServiceNetCore/MtClient.cs new file mode 100644 index 00000000..f5403fec --- /dev/null +++ b/MtApiServiceNetCore/MtClient.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections; +using System.ServiceModel; +using System.Collections.Generic; +using log4net; +using System.Threading.Tasks; + +namespace MTApiService +{ + public sealed class MtClient : IMtApiCallback, IDisposable + { + private const string ServiceName = "MtApiService"; + + public delegate void MtQuoteHandler(MtQuote quote); + public delegate void MtEventHandler(MtEvent e); + + #region Fields + private static readonly ILog Log = LogManager.GetLogger(typeof(MtClient)); + + private readonly MtApiProxy _proxy; + private Task lastQuoteTask; + private Task lastEventTask; + #endregion + + #region ctor + public MtClient(string host, int port) + { + if (string.IsNullOrEmpty(host)) + throw new ArgumentNullException(nameof(host), "host is null or empty"); + + if (port < 0 || port > 65536) + throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid"); + + Host = host; + Port = port; + + var urlService = $"net.tcp://{host}:{port}/{ServiceName}"; + + var bind = new NetTcpBinding(SecurityMode.None) + { + MaxReceivedMessageSize = 2147483647, + MaxBufferSize = 2147483647, + MaxBufferPoolSize = 2147483647, + SendTimeout = new TimeSpan(12, 0, 0), + ReceiveTimeout = new TimeSpan(12, 0, 0), + ReaderQuotas = + { + MaxArrayLength = 2147483647, + MaxBytesPerRead = 2147483647, + MaxDepth = 2147483647, + MaxStringContentLength = 2147483647, + MaxNameTableCharCount = 2147483647 + } + }; + + var quoteScheduler = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent); + var eventScheduler = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent); + lastQuoteTask = quoteScheduler.StartNew(() => { }); + lastEventTask = eventScheduler.StartNew(() => { }); + + _proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService)); + _proxy.Faulted += ProxyFaulted; + } + + public MtClient(int port) : this("localhost", port) + { } + + #endregion + + #region Public Methods + /// Thrown when connection failed + public void Connect() + { + Log.Debug("Connect: begin."); + + if (_proxy.State != CommunicationState.Created) + { + Log.ErrorFormat("Connected: end. Client has invalid state {0}", _proxy.State); + return; + } + + bool coonected; + + try + { + coonected = _proxy.Connect(); + } + catch (Exception ex) + { + Log.ErrorFormat("Connect: Exception - {0}", ex.Message); + + throw new CommunicationException($"Connection failed to service. {ex.Message}"); + } + + if (coonected == false) + { + Log.Error("Connect: end. Connection failed."); + throw new CommunicationException("Connection failed"); + } + + Log.Debug("Connect: end."); + } + + public void Disconnect() + { + Log.Debug("Disconnect: begin."); + + try + { + _proxy.Disconnect(); + } + catch (Exception ex) + { + Log.ErrorFormat("Disconnect: Exception - {0}", ex.Message); + } + + Log.Debug("Disconnect: end."); + } + + /// Thrown when connection failed + public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary namedParams, int expertHandle) + { + Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count); + + if (IsConnected == false) + { + Log.Error("SendCommand: Client is not connected."); + throw new CommunicationException("Client is not connected."); + } + + MtResponse result; + + try + { + result = _proxy.SendCommand(new MtCommand { + CommandType = commandType, + Parameters = parameters, + NamedParams = namedParams, + ExpertHandle = expertHandle}); + } + catch (Exception ex) + { + Log.ErrorFormat("SendCommand: Exception - {0}", ex.Message); + + throw new CommunicationException("Service connection failed! " + ex.Message); + } + + Log.DebugFormat("SendCommand: end. result = {0}", result); + + return result; + } + + /// Thrown when connection failed + public List GetQuotes() + { + Log.Debug("GetQuotes: begin."); + + if (IsConnected == false) + { + Log.Warn("GetQuotes: end. Client is not connected."); + return null; + } + + List result; + + try + { + result = _proxy.GetQuotes(); + } + catch (Exception ex) + { + Log.ErrorFormat("GetQuotes: Exception - {0}", ex.Message); + + throw new CommunicationException($"Service connection failed! {ex.Message}"); + } + + Log.DebugFormat("GetQuotes: end. quotes count = {0}", result?.Count); + + return result; + } + + #endregion + + #region IMtApiCallback Members + + public void OnQuoteUpdate(MtQuote quote) + { + Log.DebugFormat("OnQuoteUpdate: begin. quote = {0}", quote); + + if (quote == null) return; + + if (QuoteUpdated != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteUpdated.Invoke(quote)); + } + + Log.Debug("OnQuoteUpdate: end."); + } + + public void OnQuoteAdded(MtQuote quote) + { + Log.DebugFormat("OnQuoteAdded: begin. quote = {0}", quote); + + if (QuoteAdded != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteAdded.Invoke(quote)); + } + + Log.Debug("OnQuoteAdded: end."); + } + + public void OnQuoteRemoved(MtQuote quote) + { + Log.DebugFormat("OnQuoteRemoved: begin. quote = {0}", quote); + + if (QuoteRemoved != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteRemoved.Invoke(quote)); + } + + Log.Debug("OnQuoteRemoved: end."); + } + + public void OnServerStopped() + { + Log.Debug("OnServerStopped: begin."); + + ServerDisconnected?.Invoke(this, EventArgs.Empty); + + Log.Debug("OnServerStopped: end."); + } + + + public void OnMtEvent(MtEvent e) + { + Log.DebugFormat("OnMtEvent: begin. event = {0}", e); + + if (MtEventReceived != null) + { + lastEventTask = lastEventTask.ContinueWith((t) => MtEventReceived.Invoke(e)); + } + + Log.Debug("OnMtEvent: end."); + } + + #endregion + + #region Properties + public string Host { get; private set; } + public int Port { get; private set; } + + private bool IsConnected => _proxy.State == CommunicationState.Opened; + + #endregion + + #region Private Methods + + private void ProxyFaulted(object sender, EventArgs e) + { + Log.Debug("ProxyFaulted: begin."); + + ServerFailed?.Invoke(this, EventArgs.Empty); + + Log.Debug("ProxyFaulted: end."); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Log.Debug("Dispose: begin."); + + _proxy.Dispose(); + + Log.Debug("Dispose: end."); + } + + #endregion + + #region Events + public event MtQuoteHandler QuoteAdded; + public event MtQuoteHandler QuoteRemoved; + public event MtQuoteHandler QuoteUpdated; + public event EventHandler ServerDisconnected; + public event EventHandler ServerFailed; + public event MtEventHandler MtEventReceived; + #endregion + } +} diff --git a/MtApiServiceNetCore/MtService.cs b/MtApiServiceNetCore/MtService.cs new file mode 100644 index 00000000..506fe9d4 --- /dev/null +++ b/MtApiServiceNetCore/MtService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ServiceModel; +using System.Threading; +using log4net; + +namespace MTApiService +{ + [ServiceContract(CallbackContract = typeof(IMtApiCallback), SessionMode = SessionMode.Required)] + public interface IMtApi + { + [OperationContract] + bool Connect(); + + [OperationContract(IsOneWay = true)] + void Disconnect(); + + [OperationContract] + MtResponse SendCommand(MtCommand command); + + [OperationContract] + List GetQuotes(); + } + + [ServiceContract] + public interface IMtApiCallback + { + [OperationContract(IsOneWay = true)] + void OnQuoteUpdate(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnServerStopped(); + + [OperationContract(IsOneWay = true)] + void OnQuoteAdded(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnQuoteRemoved(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnMtEvent(MtEvent ntEvent); + } +} diff --git a/MtApiServiceNetCore/Properties/AssemblyInfo.cs b/MtApiServiceNetCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..3dc1eafc --- /dev/null +++ b/MtApiServiceNetCore/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MTApiService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DW")] +[assembly: AssemblyProduct("MTApiService")] +[assembly: AssemblyCopyright("Copyright © DW 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f1cc1516-9352-4ddd-811a-c5fc842b12d4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.32.0")] +[assembly: AssemblyFileVersion("1.0.32.0")] \ No newline at end of file diff --git a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj index 377c8dfc..8acc46a5 100644 --- a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj +++ b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj @@ -1,154 +1,62 @@ - - + - Debug + netcoreapp3.0 x86 - 8.0.30703 - 2.0 - {38B9C657-BC2F-44F0-8824-54B31F2A64F5} WinExe - Properties - MtApi5TestClient - MtApi5TestClient - v4.5.2 - - - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 + false + true + false - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false + x64 - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU bin\Debug\MtApi5TestClient.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset + ManagedMinimumRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false - bin\Release\ - TRACE - true - pdbonly - AnyCPU bin\Release\MtApi5TestClient.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs - prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true false - false - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - - - MainWindow.xaml - Code - - - - - - - - - Code - - + True True Resources.resx - + True Settings.settings True - + ResXFileCodeGenerator Resources.Designer.cs - - + SettingsSingleFileGenerator Settings.Designer.cs - - {AC8B5010-DA75-477E-9CA5-547C649E12D8} - MtApi5 - + + + + + - - \ No newline at end of file diff --git a/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs b/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs index cd34a050..047362d8 100644 --- a/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs +++ b/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace MtApi5TestClient.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs b/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs index 9580053d..f0986a83 100644 --- a/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs +++ b/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace MtApi5TestClient.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/TestClients/MtApi5TestClient/app.config b/TestClients/MtApi5TestClient/app.config index 9c72f143..05d8b241 100755 --- a/TestClients/MtApi5TestClient/app.config +++ b/TestClients/MtApi5TestClient/app.config @@ -1,3 +1,3 @@ - + From 049042e2c7a45b21b90d803dcd414d7b1a0e6859 Mon Sep 17 00:00:00 2001 From: Jan Scheperski Date: Thu, 24 Dec 2020 23:32:34 +0100 Subject: [PATCH 2/8] updated dependencies and moved to .net5.0 --- MTApiService/MTApiService.csproj | 7 ++++--- MTApiService/packages.config | 2 +- MtApi5/MtApi5.csproj | 3 +-- MtApiServiceNetCore/MtApiServiceNetCore.csproj | 14 ++++---------- .../MtApi5TestClient/MtApi5TestClient.csproj | 2 +- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/MTApiService/MTApiService.csproj b/MTApiService/MTApiService.csproj index 64c83838..b99a1f50 100755 --- a/MTApiService/MTApiService.csproj +++ b/MTApiService/MTApiService.csproj @@ -44,14 +44,15 @@ MtApiKey.snk - - ..\packages\log4net.2.0.5\lib\net40-full\log4net.dll - True + + ..\packages\log4net.2.0.12\lib\net40\log4net.dll + + diff --git a/MTApiService/packages.config b/MTApiService/packages.config index a1a79164..0066004f 100755 --- a/MTApiService/packages.config +++ b/MTApiService/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MtApi5/MtApi5.csproj b/MtApi5/MtApi5.csproj index 45a3aae8..786f59c4 100644 --- a/MtApi5/MtApi5.csproj +++ b/MtApi5/MtApi5.csproj @@ -1,6 +1,6 @@  - netcoreapp3.0 + net5.0 Library false @@ -16,7 +16,6 @@ - diff --git a/MtApiServiceNetCore/MtApiServiceNetCore.csproj b/MtApiServiceNetCore/MtApiServiceNetCore.csproj index 9aeb655b..96f29b07 100644 --- a/MtApiServiceNetCore/MtApiServiceNetCore.csproj +++ b/MtApiServiceNetCore/MtApiServiceNetCore.csproj @@ -1,6 +1,6 @@  - netcoreapp3.0 + net5.0 Library false @@ -30,16 +30,10 @@ - - - - + - - - - - + + \ No newline at end of file diff --git a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj index 8acc46a5..0e51d1d9 100644 --- a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj +++ b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj @@ -1,6 +1,6 @@  - netcoreapp3.0 + net5.0-windows x86 WinExe false From 7a2dc6982023399b59655cda801f4f2f2b2b70f2 Mon Sep 17 00:00:00 2001 From: Jan Scheperski Date: Sun, 27 Dec 2020 23:40:36 +0100 Subject: [PATCH 3/8] removed build warning --- TestClients/MtApi5TestClient/MtApi5TestClient.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj index 0e51d1d9..05a14f75 100644 --- a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj +++ b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj @@ -1,4 +1,4 @@ - + net5.0-windows x86 From 9af26f7f56184151759c9506944182104763198b Mon Sep 17 00:00:00 2001 From: Jan Scheperski Date: Thu, 2 Dec 2021 00:14:16 +0100 Subject: [PATCH 4/8] updated .net core dependencies --- MtApi5/MtApi5.csproj | 2 +- MtApiServiceNetCore/MtApiServiceNetCore.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MtApi5/MtApi5.csproj b/MtApi5/MtApi5.csproj index 786f59c4..8119356d 100644 --- a/MtApi5/MtApi5.csproj +++ b/MtApi5/MtApi5.csproj @@ -18,6 +18,6 @@ - + \ No newline at end of file diff --git a/MtApiServiceNetCore/MtApiServiceNetCore.csproj b/MtApiServiceNetCore/MtApiServiceNetCore.csproj index 96f29b07..4a6daf9a 100644 --- a/MtApiServiceNetCore/MtApiServiceNetCore.csproj +++ b/MtApiServiceNetCore/MtApiServiceNetCore.csproj @@ -32,8 +32,8 @@ - - + + \ No newline at end of file From 91af1124c41a88b1f761086dc670cbeb584a7270 Mon Sep 17 00:00:00 2001 From: Jan Scheperski Date: Thu, 2 Dec 2021 21:18:41 +0100 Subject: [PATCH 5/8] removed workaround for wcf 4.8.0 --- MtApiServiceNetCore/MtApiProxy.cs | 37 +++++++++++++++---------------- MtApiServiceNetCore/MtClient.cs | 9 ++++---- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/MtApiServiceNetCore/MtApiProxy.cs b/MtApiServiceNetCore/MtApiProxy.cs index 7934fad9..71a85858 100644 --- a/MtApiServiceNetCore/MtApiProxy.cs +++ b/MtApiServiceNetCore/MtApiProxy.cs @@ -1,48 +1,39 @@ using System; using System.Collections.Generic; -using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; namespace MTApiService { - internal class MtApiProxy : IMtApi, IDisposable + internal class MtApiProxy : DuplexClientBase, IMtApi, IDisposable { - private IMtApi InnerChannel; - - public CommunicationState State => ((ICommunicationObject)InnerChannel).State; - public MtApiProxy(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress) + : base(callbackContext, binding, remoteAddress) { - var channel = new DuplexChannelFactory(callbackContext, binding, remoteAddress); - channel.Faulted += InnerDuplexChannel_Faulted; - - // configure endpoint programmatically instead via an attribute which will lead to a PlatformNotSupportedException - (channel.Endpoint.EndpointBehaviors.Single(b => b is CallbackBehaviorAttribute) as CallbackBehaviorAttribute).UseSynchronizationContext = false; - - InnerChannel = channel.CreateChannel(); + InnerChannel.Faulted += InnerDuplexChannel_Faulted; } #region IMtApi Members public bool Connect() { - return InnerChannel.Connect(); + InnerChannel.Open(); + return Channel.Connect(); } public void Disconnect() { - InnerChannel.Disconnect(); + Channel.Disconnect(); } public MtResponse SendCommand(MtCommand command) { - return InnerChannel.SendCommand(command); + return Channel.SendCommand(command); } public List GetQuotes() { - return InnerChannel.GetQuotes(); + return Channel.GetQuotes(); } #endregion @@ -53,11 +44,19 @@ public void Dispose() { try { - Disconnect(); + Close(); + } + catch (CommunicationException) + { + Abort(); + } + catch (TimeoutException) + { + Abort(); } catch (Exception) { - + Abort(); } } diff --git a/MtApiServiceNetCore/MtClient.cs b/MtApiServiceNetCore/MtClient.cs index f5403fec..38618d68 100644 --- a/MtApiServiceNetCore/MtClient.cs +++ b/MtApiServiceNetCore/MtClient.cs @@ -7,6 +7,7 @@ namespace MTApiService { + [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] public sealed class MtClient : IMtApiCallback, IDisposable { private const string ServiceName = "MtApiService"; @@ -63,7 +64,9 @@ public MtClient(string host, int port) } public MtClient(int port) : this("localhost", port) - { } + { + // piping is not supported yet: https://github.com/dotnet/wcf/issues/2535 + } #endregion @@ -193,7 +196,6 @@ public void OnQuoteUpdate(MtQuote quote) { lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteUpdated.Invoke(quote)); } - Log.Debug("OnQuoteUpdate: end."); } @@ -205,7 +207,6 @@ public void OnQuoteAdded(MtQuote quote) { lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteAdded.Invoke(quote)); } - Log.Debug("OnQuoteAdded: end."); } @@ -217,7 +218,6 @@ public void OnQuoteRemoved(MtQuote quote) { lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteRemoved.Invoke(quote)); } - Log.Debug("OnQuoteRemoved: end."); } @@ -239,7 +239,6 @@ public void OnMtEvent(MtEvent e) { lastEventTask = lastEventTask.ContinueWith((t) => MtEventReceived.Invoke(e)); } - Log.Debug("OnMtEvent: end."); } From 3d87492c29334ebd2b2af278c73f4a41ee52a9a3 Mon Sep 17 00:00:00 2001 From: lazou Date: Thu, 10 Nov 2022 00:59:52 +0100 Subject: [PATCH 6/8] Ported MtApi5 to .Net Core (#2) * added .net core based MtApiService * based on https://github.com/dotnet/wcf * only client side is available in .net core * piping removed, only tcp connection possible * ported MtApi5 and MtApi5TestClient to .net core * updated dependencies and moved to .net5.0 * removed build warning --- MTApiService/MTApiService.csproj | 7 +- MTApiService/packages.config | 2 +- MetaTraderApi_2017.sln | 25 +- MtApi5/MtApi5.csproj | 112 +------ MtApi5/packages.config | 2 +- MtApiServiceNetCore/MtApiProxy.cs | 78 +++++ .../MtApiServiceNetCore.csproj | 39 +++ MtApiServiceNetCore/MtClient.cs | 291 ++++++++++++++++++ MtApiServiceNetCore/MtService.cs | 43 +++ .../Properties/AssemblyInfo.cs | 36 +++ .../MtApi5TestClient/MtApi5TestClient.csproj | 124 +------- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Settings.Designer.cs | 2 +- TestClients/MtApi5TestClient/app.config | 2 +- 14 files changed, 541 insertions(+), 224 deletions(-) mode change 100755 => 100644 MtApi5/MtApi5.csproj create mode 100644 MtApiServiceNetCore/MtApiProxy.cs create mode 100644 MtApiServiceNetCore/MtApiServiceNetCore.csproj create mode 100644 MtApiServiceNetCore/MtClient.cs create mode 100644 MtApiServiceNetCore/MtService.cs create mode 100644 MtApiServiceNetCore/Properties/AssemblyInfo.cs diff --git a/MTApiService/MTApiService.csproj b/MTApiService/MTApiService.csproj index 64c83838..b99a1f50 100755 --- a/MTApiService/MTApiService.csproj +++ b/MTApiService/MTApiService.csproj @@ -44,14 +44,15 @@ MtApiKey.snk - - ..\packages\log4net.2.0.5\lib\net40-full\log4net.dll - True + + ..\packages\log4net.2.0.12\lib\net40\log4net.dll + + diff --git a/MTApiService/packages.config b/MTApiService/packages.config index d67e57f9..0066004f 100644 --- a/MTApiService/packages.config +++ b/MTApiService/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MetaTraderApi_2017.sln b/MetaTraderApi_2017.sln index d8351e85..4010dd14 100644 --- a/MetaTraderApi_2017.sln +++ b/MetaTraderApi_2017.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30413.136 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApiClientUI", "TestClients\TestApiClientUI\TestApiClientUI.csproj", "{663CC515-EAAE-47D4-8933-5008C2DA1160}" EndProject @@ -47,6 +47,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "MtApiBootstrapper", "MtApiB {78B94552-DB17-40EC-B7C6-23D32DB85DC1} = {78B94552-DB17-40EC-B7C6-23D32DB85DC1} EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MtApiServiceNetCore", "MtApiServiceNetCore\MtApiServiceNetCore.csproj", "{7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,6 +234,22 @@ Global {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x64.ActiveCfg = Release|x86 {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x86.ActiveCfg = Release|x86 {8E63046B-56E5-4623-8808-558AD72A8F2B}.Release|x86.Build.0 = Release|x86 + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Win32.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|Win32.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x64.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Debug|x86.Build.0 = Debug|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Any CPU.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Win32.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|Win32.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x64.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x64.Build.0 = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x86.ActiveCfg = Release|Any CPU + {7CAFAAE2-0C15-479A-B16D-C2FCE0A48E11}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -241,4 +259,7 @@ Global {38B9C657-BC2F-44F0-8824-54B31F2A64F5} = {B91FF338-E05D-4EF1-948B-A2376DB37ECA} {EB7C228D-9494-4985-845E-B8312450DF3D} = {B91FF338-E05D-4EF1-948B-A2376DB37ECA} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {23C8878C-16A5-47DF-9A57-73CCF847780D} + EndGlobalSection EndGlobal diff --git a/MtApi5/MtApi5.csproj b/MtApi5/MtApi5.csproj old mode 100755 new mode 100644 index 8596a9c9..786f59c4 --- a/MtApi5/MtApi5.csproj +++ b/MtApi5/MtApi5.csproj @@ -1,123 +1,23 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {AC8B5010-DA75-477E-9CA5-547C649E12D8} + net5.0 Library - Properties - MtApi5 - MtApi5 - v4.0 - 512 + false - true - full - false ..\build\products\Debug\ - DEBUG;TRACE - prompt - 4 false - pdbonly - true ..\build\products\Release\ - TRACE - prompt - 4 - - ..\packages\Newtonsoft.Json.12.0.2\lib\net40\Newtonsoft.Json.dll - True - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - {DE76D5C7-B99C-4467-8408-78173BDD84E0} - MTApiService - + - - - - - \ No newline at end of file diff --git a/MtApi5/packages.config b/MtApi5/packages.config index 66b711be..32637c2e 100755 --- a/MtApi5/packages.config +++ b/MtApi5/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/MtApiServiceNetCore/MtApiProxy.cs b/MtApiServiceNetCore/MtApiProxy.cs new file mode 100644 index 00000000..7934fad9 --- /dev/null +++ b/MtApiServiceNetCore/MtApiProxy.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceModel; +using System.ServiceModel.Channels; + +namespace MTApiService +{ + internal class MtApiProxy : IMtApi, IDisposable + { + private IMtApi InnerChannel; + + public CommunicationState State => ((ICommunicationObject)InnerChannel).State; + + public MtApiProxy(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress) + { + var channel = new DuplexChannelFactory(callbackContext, binding, remoteAddress); + channel.Faulted += InnerDuplexChannel_Faulted; + + // configure endpoint programmatically instead via an attribute which will lead to a PlatformNotSupportedException + (channel.Endpoint.EndpointBehaviors.Single(b => b is CallbackBehaviorAttribute) as CallbackBehaviorAttribute).UseSynchronizationContext = false; + + InnerChannel = channel.CreateChannel(); + } + + #region IMtApi Members + + public bool Connect() + { + return InnerChannel.Connect(); + } + + public void Disconnect() + { + InnerChannel.Disconnect(); + } + + public MtResponse SendCommand(MtCommand command) + { + return InnerChannel.SendCommand(command); + } + + public List GetQuotes() + { + return InnerChannel.GetQuotes(); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + try + { + Disconnect(); + } + catch (Exception) + { + + } + } + + #endregion + + #region Private Methods + private void InnerDuplexChannel_Faulted(object sender, EventArgs e) + { + Faulted?.Invoke(this, e); + } + + #endregion + + #region Events + public event EventHandler Faulted; + #endregion + } +} diff --git a/MtApiServiceNetCore/MtApiServiceNetCore.csproj b/MtApiServiceNetCore/MtApiServiceNetCore.csproj new file mode 100644 index 00000000..96f29b07 --- /dev/null +++ b/MtApiServiceNetCore/MtApiServiceNetCore.csproj @@ -0,0 +1,39 @@ + + + net5.0 + Library + false + + + ..\build\products\Debug\ + + + ..\build\products\Release\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MtApiServiceNetCore/MtClient.cs b/MtApiServiceNetCore/MtClient.cs new file mode 100644 index 00000000..f5403fec --- /dev/null +++ b/MtApiServiceNetCore/MtClient.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections; +using System.ServiceModel; +using System.Collections.Generic; +using log4net; +using System.Threading.Tasks; + +namespace MTApiService +{ + public sealed class MtClient : IMtApiCallback, IDisposable + { + private const string ServiceName = "MtApiService"; + + public delegate void MtQuoteHandler(MtQuote quote); + public delegate void MtEventHandler(MtEvent e); + + #region Fields + private static readonly ILog Log = LogManager.GetLogger(typeof(MtClient)); + + private readonly MtApiProxy _proxy; + private Task lastQuoteTask; + private Task lastEventTask; + #endregion + + #region ctor + public MtClient(string host, int port) + { + if (string.IsNullOrEmpty(host)) + throw new ArgumentNullException(nameof(host), "host is null or empty"); + + if (port < 0 || port > 65536) + throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid"); + + Host = host; + Port = port; + + var urlService = $"net.tcp://{host}:{port}/{ServiceName}"; + + var bind = new NetTcpBinding(SecurityMode.None) + { + MaxReceivedMessageSize = 2147483647, + MaxBufferSize = 2147483647, + MaxBufferPoolSize = 2147483647, + SendTimeout = new TimeSpan(12, 0, 0), + ReceiveTimeout = new TimeSpan(12, 0, 0), + ReaderQuotas = + { + MaxArrayLength = 2147483647, + MaxBytesPerRead = 2147483647, + MaxDepth = 2147483647, + MaxStringContentLength = 2147483647, + MaxNameTableCharCount = 2147483647 + } + }; + + var quoteScheduler = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent); + var eventScheduler = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent); + lastQuoteTask = quoteScheduler.StartNew(() => { }); + lastEventTask = eventScheduler.StartNew(() => { }); + + _proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService)); + _proxy.Faulted += ProxyFaulted; + } + + public MtClient(int port) : this("localhost", port) + { } + + #endregion + + #region Public Methods + /// Thrown when connection failed + public void Connect() + { + Log.Debug("Connect: begin."); + + if (_proxy.State != CommunicationState.Created) + { + Log.ErrorFormat("Connected: end. Client has invalid state {0}", _proxy.State); + return; + } + + bool coonected; + + try + { + coonected = _proxy.Connect(); + } + catch (Exception ex) + { + Log.ErrorFormat("Connect: Exception - {0}", ex.Message); + + throw new CommunicationException($"Connection failed to service. {ex.Message}"); + } + + if (coonected == false) + { + Log.Error("Connect: end. Connection failed."); + throw new CommunicationException("Connection failed"); + } + + Log.Debug("Connect: end."); + } + + public void Disconnect() + { + Log.Debug("Disconnect: begin."); + + try + { + _proxy.Disconnect(); + } + catch (Exception ex) + { + Log.ErrorFormat("Disconnect: Exception - {0}", ex.Message); + } + + Log.Debug("Disconnect: end."); + } + + /// Thrown when connection failed + public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary namedParams, int expertHandle) + { + Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count); + + if (IsConnected == false) + { + Log.Error("SendCommand: Client is not connected."); + throw new CommunicationException("Client is not connected."); + } + + MtResponse result; + + try + { + result = _proxy.SendCommand(new MtCommand { + CommandType = commandType, + Parameters = parameters, + NamedParams = namedParams, + ExpertHandle = expertHandle}); + } + catch (Exception ex) + { + Log.ErrorFormat("SendCommand: Exception - {0}", ex.Message); + + throw new CommunicationException("Service connection failed! " + ex.Message); + } + + Log.DebugFormat("SendCommand: end. result = {0}", result); + + return result; + } + + /// Thrown when connection failed + public List GetQuotes() + { + Log.Debug("GetQuotes: begin."); + + if (IsConnected == false) + { + Log.Warn("GetQuotes: end. Client is not connected."); + return null; + } + + List result; + + try + { + result = _proxy.GetQuotes(); + } + catch (Exception ex) + { + Log.ErrorFormat("GetQuotes: Exception - {0}", ex.Message); + + throw new CommunicationException($"Service connection failed! {ex.Message}"); + } + + Log.DebugFormat("GetQuotes: end. quotes count = {0}", result?.Count); + + return result; + } + + #endregion + + #region IMtApiCallback Members + + public void OnQuoteUpdate(MtQuote quote) + { + Log.DebugFormat("OnQuoteUpdate: begin. quote = {0}", quote); + + if (quote == null) return; + + if (QuoteUpdated != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteUpdated.Invoke(quote)); + } + + Log.Debug("OnQuoteUpdate: end."); + } + + public void OnQuoteAdded(MtQuote quote) + { + Log.DebugFormat("OnQuoteAdded: begin. quote = {0}", quote); + + if (QuoteAdded != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteAdded.Invoke(quote)); + } + + Log.Debug("OnQuoteAdded: end."); + } + + public void OnQuoteRemoved(MtQuote quote) + { + Log.DebugFormat("OnQuoteRemoved: begin. quote = {0}", quote); + + if (QuoteRemoved != null) + { + lastQuoteTask = lastQuoteTask.ContinueWith((t) => QuoteRemoved.Invoke(quote)); + } + + Log.Debug("OnQuoteRemoved: end."); + } + + public void OnServerStopped() + { + Log.Debug("OnServerStopped: begin."); + + ServerDisconnected?.Invoke(this, EventArgs.Empty); + + Log.Debug("OnServerStopped: end."); + } + + + public void OnMtEvent(MtEvent e) + { + Log.DebugFormat("OnMtEvent: begin. event = {0}", e); + + if (MtEventReceived != null) + { + lastEventTask = lastEventTask.ContinueWith((t) => MtEventReceived.Invoke(e)); + } + + Log.Debug("OnMtEvent: end."); + } + + #endregion + + #region Properties + public string Host { get; private set; } + public int Port { get; private set; } + + private bool IsConnected => _proxy.State == CommunicationState.Opened; + + #endregion + + #region Private Methods + + private void ProxyFaulted(object sender, EventArgs e) + { + Log.Debug("ProxyFaulted: begin."); + + ServerFailed?.Invoke(this, EventArgs.Empty); + + Log.Debug("ProxyFaulted: end."); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Log.Debug("Dispose: begin."); + + _proxy.Dispose(); + + Log.Debug("Dispose: end."); + } + + #endregion + + #region Events + public event MtQuoteHandler QuoteAdded; + public event MtQuoteHandler QuoteRemoved; + public event MtQuoteHandler QuoteUpdated; + public event EventHandler ServerDisconnected; + public event EventHandler ServerFailed; + public event MtEventHandler MtEventReceived; + #endregion + } +} diff --git a/MtApiServiceNetCore/MtService.cs b/MtApiServiceNetCore/MtService.cs new file mode 100644 index 00000000..506fe9d4 --- /dev/null +++ b/MtApiServiceNetCore/MtService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ServiceModel; +using System.Threading; +using log4net; + +namespace MTApiService +{ + [ServiceContract(CallbackContract = typeof(IMtApiCallback), SessionMode = SessionMode.Required)] + public interface IMtApi + { + [OperationContract] + bool Connect(); + + [OperationContract(IsOneWay = true)] + void Disconnect(); + + [OperationContract] + MtResponse SendCommand(MtCommand command); + + [OperationContract] + List GetQuotes(); + } + + [ServiceContract] + public interface IMtApiCallback + { + [OperationContract(IsOneWay = true)] + void OnQuoteUpdate(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnServerStopped(); + + [OperationContract(IsOneWay = true)] + void OnQuoteAdded(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnQuoteRemoved(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnMtEvent(MtEvent ntEvent); + } +} diff --git a/MtApiServiceNetCore/Properties/AssemblyInfo.cs b/MtApiServiceNetCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..3dc1eafc --- /dev/null +++ b/MtApiServiceNetCore/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MTApiService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DW")] +[assembly: AssemblyProduct("MTApiService")] +[assembly: AssemblyCopyright("Copyright © DW 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f1cc1516-9352-4ddd-811a-c5fc842b12d4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.32.0")] +[assembly: AssemblyFileVersion("1.0.32.0")] \ No newline at end of file diff --git a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj index 377c8dfc..05a14f75 100644 --- a/TestClients/MtApi5TestClient/MtApi5TestClient.csproj +++ b/TestClients/MtApi5TestClient/MtApi5TestClient.csproj @@ -1,154 +1,62 @@ - - + - Debug + net5.0-windows x86 - 8.0.30703 - 2.0 - {38B9C657-BC2F-44F0-8824-54B31F2A64F5} WinExe - Properties - MtApi5TestClient - MtApi5TestClient - v4.5.2 - - - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 + false + true + false - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false + x64 - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU bin\Debug\MtApi5TestClient.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset + ManagedMinimumRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false - bin\Release\ - TRACE - true - pdbonly - AnyCPU bin\Release\MtApi5TestClient.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs - prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true false - false - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - - - MainWindow.xaml - Code - - - - - - - - - Code - - + True True Resources.resx - + True Settings.settings True - + ResXFileCodeGenerator Resources.Designer.cs - - + SettingsSingleFileGenerator Settings.Designer.cs - - {AC8B5010-DA75-477E-9CA5-547C649E12D8} - MtApi5 - + + + + + - - \ No newline at end of file diff --git a/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs b/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs index cd34a050..047362d8 100644 --- a/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs +++ b/TestClients/MtApi5TestClient/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace MtApi5TestClient.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs b/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs index 9580053d..f0986a83 100644 --- a/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs +++ b/TestClients/MtApi5TestClient/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace MtApi5TestClient.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/TestClients/MtApi5TestClient/app.config b/TestClients/MtApi5TestClient/app.config index 9c72f143..05d8b241 100755 --- a/TestClients/MtApi5TestClient/app.config +++ b/TestClients/MtApi5TestClient/app.config @@ -1,3 +1,3 @@ - + From 3176dfdde4c5b41538c9a7e2de89b0b7b370ebda Mon Sep 17 00:00:00 2001 From: lazou Date: Thu, 10 Nov 2022 01:03:32 +0100 Subject: [PATCH 7/8] Added TesterDeposit and TesterWithdrawal methods (#3) --- MtApi5/Mt5CommandType.cs | 4 +- MtApi5/MtApi5Client.cs | 22 ++++++ TestClients/MtApi5TestClient/MainWindow.xaml | 2 + TestClients/MtApi5TestClient/ViewModel.cs | 16 ++++ mq5/MtApi5.ex5 | Bin 786740 -> 315774 bytes mq5/MtApi5.mq5 | 74 +++++++++++++++++-- 6 files changed, 112 insertions(+), 6 deletions(-) diff --git a/MtApi5/Mt5CommandType.cs b/MtApi5/Mt5CommandType.cs index c50d5521..0a5691a6 100755 --- a/MtApi5/Mt5CommandType.cs +++ b/MtApi5/Mt5CommandType.cs @@ -255,6 +255,8 @@ internal enum Mt5CommandType UnlockTicks = 159, PositionCloseAll = 160, - TesterStop = 161 + TesterStop = 161, + TesterDeposit = 162, + TesterWithdrawal = 163 } } diff --git a/MtApi5/MtApi5Client.cs b/MtApi5/MtApi5Client.cs index a28348c0..ea6742a1 100755 --- a/MtApi5/MtApi5Client.cs +++ b/MtApi5/MtApi5Client.cs @@ -2241,6 +2241,28 @@ public void TesterStop() SendCommand(Mt5CommandType.TesterStop, null); } + /// + ///The special function that emulates depositing funds during a test. It can be used in some money management systems. + /// + ///Money to be deposited to an account in the deposit currency. + ///If successful, returns true, otherwise - false. + public bool TesterDeposit(double money) + { + var commandParameters = new ArrayList { money }; + return SendCommand(Mt5CommandType.TesterDeposit, commandParameters); + } + + /// + ///The special function to emulate the operation of money withdrawal in the process of testing. Can be used in some asset management systems. + /// + /// The sum of money that we need to withdraw (in the deposit currency). + ///If successful, returns true, otherwise - false. + public bool TesterWithdrawal(double money) + { + var commandParameters = new ArrayList { money }; + return SendCommand(Mt5CommandType.TesterWithdrawal, commandParameters); + } + #endregion // Common Functions #region Object Functions diff --git a/TestClients/MtApi5TestClient/MainWindow.xaml b/TestClients/MtApi5TestClient/MainWindow.xaml index 0a7cdbb4..78729d34 100755 --- a/TestClients/MtApi5TestClient/MainWindow.xaml +++ b/TestClients/MtApi5TestClient/MainWindow.xaml @@ -594,6 +594,8 @@