
使用 Skype 发送来自 Expert Advisor 的消息
简介
Skype 是一种通讯程序,允许人们通过互联网打电话和进行正常的聊天。与其他同类程序相比,Skype 最重要的优势之一是,它是通往真正移动网络运营商的一个途径。人们可以拨打真正的移动电话,发送短信等等。还有一个移动电话的 Skype 版本。人们可以节省短信费用,因为用此程序发送常规消息是完全免费的。基本上,移动电话必须在操作系统下运行。不过,如今必要时也可以实现全移动式运行。这正是现在很多人在利用的机会。
哪些人需要它?为什么?
交易者不可能一直坐在交易终端前看着交易执行情况,也不必这么做。但是,能够不时获取一些信号,让交易者了解有关 Expert Advisor 的操作或特定时刻的市场状态的信息,这自然是极好的。为什么不在移动电话里获取此类信息呢?
哪种信息是有用的?
可通过移动电话中的消息获取的信息可分为两类:
- 不影响任何东西且实际上与日志文件中的数据并无差别的当前数据;
- 收到时正好对交易者有帮助的有用信息。
让我们看看当前数据的示例:
- 订单状态。订单是何时开放的,哪个方向,为其分配了什么止损位等,订单何时关闭,什么原因,是亏损了还是盈利了等等。
- 各种市场状态例如,某项指标满足一定水平或某个趋势改变了方向时。
有用的信息:
- 错误报告。所有程序员都是人,有时在 Expert Advisor 发生故障时会出现一些不愉快的情形。人们会很乐意了解错误发生时的状况以及是否造成致命停机。当然,如果 Expert Advisor 的逻辑允许人检测错误,也是可行的。
- Expert Advisor 的操作状态。例如,EA 设置为在您出差时每小时向您发送一条关于其操作状态的消息。而预定的时间内并没有发来预期的消息。从断网到断电,一切情况都有可能发生。如果是在家里使用终端,可以请求同事或配偶找出出错原因并还原设置,而不是在一无所知的情况下空等一个礼拜。
当然,这种区分具有相对性质,可能出现的情况远不止本文所述。每个交易者将自己决定消息应包括的内容。重点是要认识到,这个功能是非常有用的。
这个功能在 Skype 中如何实现?
- SMSes
此服务当然是付费服务。一切看起来都一如往常:键入用户号码,输入消息,再按“发送”。
- 常规消息
这是完全免费的。只需选择一个用户,输入消息,然后按“发送”。
如何从 Expert Advisor 实现这一点?
有两种方法,这两种方法都需要使用 DLL:
- 我们应提前准备好一个宏文件,即在启动该文件时,对键盘和鼠标的控制将被截获。因此我们应使用操作序列激活 Skype,在菜单中找到短信发送项目,然后在显示的窗口中键入用户号码,并从剪贴板中粘贴起初在 Expert Advisor 中键入的文本信息。您可事先练习并熟悉这些操作。这样,我们就制作了一份文件,这份文件是一个关联文档,可作为普通应用程序启动。顺便提一下,可以记录和播放宏文件的应用程序数不胜数,因此本文中未讨论具体程序。
接下来,我们必须开发一个将以如下方式运行的 DLL。其第一项操作是将 Expert Advisor 中的文本放入剪贴板。第二项操作是启动预定义的宏文件。如果一切正常,所有窗口和按钮都显示在正确位置,则不会出现任何问题,消息将被发送出去。
但这种方法有点耸人听闻。直觉告诉我,如果大脑开始出现这种想法,那就必须找出一个更令人愉快的解决方案或是完全放弃这个想法。一个新的想法突然浮现在我脑海里:Skype 是否可以有一个 API?我在他们的网站上找到了 API 和 ActiveX 界面。棒极了!让我们考虑从 Expert Advisor 使用 Skype 的另一种方法。
- 方法大致是相同的。用户号码和要发送的文本将从 EA 传递到 DLL,然后通过 Skype COM 对象发送消息。
如何实现第二种方法
让我们先从 DLL 开始。所有工作的主要部分是准备 DLL 以与 Expert Advisor 交互。首先,我们编写一个库,当从多个 Expert Advisor 中调用时,它可正常工作。遗憾的是,仅仅编写和调用一个函数是不够的。我们正在使用 ActiveX,所以最好为其创建一个特殊的单独线程,在这个线程里执行所有操作。互斥函数的标准并联工具并不能提供什么帮助。将会出现无法检测到的崩溃。因此我们将通过自定义消息传递系统实现调用序列。
DLL 源代码
#include "stdafx.h" #pragma once // Allow use of features specific to Windows XP or later. #ifndef WINVER // Change this to the appropriate value to target other versions of Windows. #define WINVER 0x0501 #endif // Exclude rarely-used stuff from Windows headers #define WIN32_LEAN_AND_MEAN // Include Skype4COM.dll, preliminarily downloaded // from the Skype developers website – http://developers.skype.com/ #import "Skype4COM.dll" rename("CreateEvent","CreatePluginEvent"), rename("SendMessage","SendChatMessage") #define MT4_EXPFUNC __declspec(dllexport) // Declare message codes for our functions. #define WM_PROC_SENDSKYPESMS WM_USER + 01 #define WM_PROC_SENDSKYPEMESSAGE WM_USER + 02 // Variables for the thread to be used for // sending messages HANDLE hUserThread; DWORD ThreadId; // Structures to store parameters of functions // SendSkypeSMS struct fcpSendSkypeSMS { int ExitCode; char * UserNum; char * Message; }; // SendSkypeMessage struct fcpSendSkypeMessage { int ExitCode; char * UserName; char * Message; }; //+------------------------------------------------------------------+ //| Thread function | //+------------------------------------------------------------------+ DWORD WINAPI ThreadProc(LPVOID lpParameter) { MSG msg; HANDLE hEvent; while(true) { if(PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0)) break; }; // Initialize COM CoInitialize(NULL); while(GetMessage(&msg, 0, 0, 0)) { if(msg.message == WM_QUIT) { break; } // Message processor WM_PROC_SENDSKYPESMS else if(msg.message == WM_PROC_SENDSKYPESMS) { fcpSendSkypeSMS* fcp = (fcpSendSkypeSMS*)msg.wParam; hEvent = (HANDLE)msg.lParam; try { // Initialize Skype SKYPE4COMLib::ISkypePtr pSkype(__uuidof(SKYPE4COMLib::Skype)); // Connect to Skype. 6 is the protocol version HRESULT hr=pSkype->Attach(6,VARIANT_TRUE); // If everything is ok, start sending the message if(!FAILED(hr)) { try { fcp->ExitCode = 1; // Try to send an SMS pSkype->SendSms(fcp->UserNum,fcp->Message,""); } catch(...) { fcp->ExitCode=-1; } } // Deinitialize Skype pSkype = NULL; } catch(...) { //Error is processed here } // Set the event SetEvent(hEvent); } // Message processor WM_PROC_SENDSKYPEMESSAGE else if(msg.message == WM_PROC_SENDSKYPEMESSAGE) { fcpSendSkypeMessage* fcp = (fcpSendSkypeMessage*)msg.wParam; hEvent = (HANDLE)msg.lParam; try { // Initialize Skype SKYPE4COMLib::ISkypePtr pSkype(__uuidof (SKYPE4COMLib::Skype)); // Connect to Skype. 6 is the protocol version HRESULT hr=pSkype->Attach(6,VARIANT_TRUE); // If everything is ok, start sending the message if(!FAILED(hr)) { try { fcp->ExitCode = 1; // Try to send the message pSkype->SendChatMessage(fcp->UserName, fcp->Message); } catch(...) { fcp->ExitCode=-1; MessageBeep(0); } } // Deinitialize Skype pSkype = NULL; } catch(...) { //Error is processed here } // Set the event SetEvent(hEvent); } }; // Deinitialize COM CoUninitialize(); return 0; } //DLL Initialization //+------------------------------------------------------------------+ //| DLL entry | //+------------------------------------------------------------------+ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if(ul_reason_for_call == DLL_PROCESS_ATTACH) { // Create a thread and attach the processor procedure address to it hUserThread = CreateThread(NULL, NULL, ThreadProc, NULL, 0, &ThreadId); if(!hUserThread) { // Error processing if the thread is not created }; } else if(ul_reason_for_call == DLL_PROCESS_DETACH) { // Delete the thread when exiting the library CloseHandle(hUserThread); } return(TRUE); } MT4_EXPFUNC bool __stdcall SendSkypeSMS(int &ExCode,char* sUserNum, char* sMessage) { //Declare the structure of function parameters fcpSendSkypeSMS* fcp; //Declare an event HANDLE hEvent; //The result of function operation by default is false bool Result = false; // Allocate a password for the structure and initialize it fcp = new fcpSendSkypeSMS(); memset(fcp, 0, sizeof(fcpSendSkypeSMS)); // Fill out the structure //By default, the code of the function working end is an error. fcp->ExitCode = -1; fcp->UserNum = sUserNum; fcp->Message = sMessage; // Create an event hEvent = CreateEvent(NULL,FALSE,FALSE, NULL); // Call event WM_PROC_SENDSKYPESMS, pass the data structure address // to processing procedure PostThreadMessage(ThreadId, WM_PROC_SENDSKYPESMS, (WPARAM)fcp, (LPARAM)hEvent); if(WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE)) { Result = true; } else { // If there was an error at message processing, the function will // return false return(Result); }; // Assign the function processing code to the variable ExCode = fcp->ExitCode; if(ExCode == -1) Result = false; // Free memory and variables and exit delete fcp; CloseHandle(hEvent); return(Result); } MT4_EXPFUNC bool __stdcall SendSkypeMessage(int &ExCode,char* sUserName, char* sMessage) { //Declare the structure of function parameters fcpSendSkypeMessage* fcp; //Declare an event HANDLE hEvent; //The result of function operation by default is false bool Result = false; // Allocate memory for the structure and initialize it fcp = new fcpSendSkypeMessage(); memset(fcp, 0, sizeof(fcpSendSkypeMessage)); // Fill out the structure //By default, the code of the function working end is an error. fcp->ExitCode = -1; fcp->UserName = sUserName; fcp->Message = sMessage; // Create an event hEvent = CreateEvent(NULL, FALSE,FALSE, NULL); // Call the event WM_PROC_SENDSKYPESMS, pass the data structure address // to processing procedure PostThreadMessage(ThreadId, WM_PROC_SENDSKYPEMESSAGE, (WPARAM)fcp, (LPARAM)hEvent); if(WAIT_OBJECT_0 == WaitForSingleObject(hEvent, INFINITE)) { Result = true; } else { // If there was an error at message processing, the function will // return false return(Result); }; // Assign the function processing code to the variable ExCode = fcp->ExitCode; if(ExCode == -1) Result = false; // Free memory and variables and exit delete fcp; CloseHandle(hEvent); return(Result); }
File DEF
LIBRARY SkypeLib EXPORTS SendSkypeSMS SendSkypeMessage
Expert Advisor to Be Tested
//+------------------------------------------------------------------+ //| SkypeTestExpert.mq4 | //| Copyright © 2007, Alexey Koshevoy. | //+------------------------------------------------------------------+ // Import functions #import "SkypeLib.dll" bool SendSkypeSMS(int &ExCode[], string Num,string Message); bool SendSkypeMessage(int &ExCode[], string User, string Message); #import //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int init() { int ExCode[1]; Alert("Send message..."); Alert(SendSkypeMessage(ExCode, "skype.account.name", "Skype message test")); if(ExCode[0] == -1) Alert("Error sending the message"); else Alert("Message sent"); Alert("Send SMS..."); Alert(SendSkypeSMS(ExCode, "+1234567890", "Skype sms test")); if(ExCode[0] == -1) Alert("Error sending the SMS"); else Alert("SMS sent"); return(0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int start() { return(0); } //+------------------------------------------------------------------+
Ea 非常简单,其主要目的是通过已编码的 DLL 发送短信和常规消息。它在初始化函数内起作用,因此也可在周末对其进行测试。
Skype 的安装
此应用程序可在 http://www.skype.com/ 上下载。建议安装最新版程序,因为之前的版本不支持 COM 接口,它们仅有一个 API。但该 API 不支持发送短信。
Skype 已经安装好了。现在我们必须下载 COM 库。可在 https://developer.skype.com/ 上的“Downloads”中找到该库。检查用于发送短信的帐户中的金额。如果金额不足,可在网上充值。帐户上没有资金时,不可发送短信,但可发送常规消息,不会有任何问题。

终端必须注册后方可访问 Skype API。可在菜单“工具->选项->隐私->管理访问 Skype 的其他程序”来勾选是否允许使用 API。大致情况必须如下:
终端将在首次尝试使用库时注册。此操作不可手动进行。因此,在首次安装库时,需要等待直至能够发出一条消息,以此证明允许使用 Skype API。Skype 将显示以下对话框:

确认之后,系统将在自动模式下开始工作。
SkypeLib 的安装
Для 要安装名为 SkypeLib.dll 的库,应将其复制到终端目录下的 experts/libraries 文件夹中。名为 Skype4COM.dll 的库也必须复制到此文件夹。现在,我们需要设置终端能够与 DLL 一起使用。为此,在连接 EA 时,勾选“安全”部分中的“允许 DLL 导入”字段,如下所示:

现在我们可以使用此库。
一些重要细节
凭借测试和实施方面的经验,我注意到了一些“微妙之处”。例如,您需要注意到,如果您的帐户上有足够的钱,当您向不存在的号码发送短信时,不会报告错误,该函数将正常运行,然而消息状态将设置为“发送中…”。这就是为什么非常明确地设置函数输入很重要的原因。使用 Skype 版本 3.0(或更高版本)也很重要。
有时候会发生以下情况(但极少出现):COM 对象未初始化,且不会发送消息。只有重新安装 Skype 才能解决此问题。外部接口相对较新,没有错误,因此可能会出现此类不愉快的情况。据我所知,这种情况出现过两次。让我们期待更高版本会更稳定地工作。
还必须指出,要 SkypeLib. dll 正常工作,还需要一些其他库。在 Visual Studio 2005 的第一个服务包发布之后,该问题尤为严重。解决该问题的最佳方法是创建安装文件。所有必要的库将自动包括在此文件中。文件 Skype4COM. dll 也可包括在此文件中。
本文随附文件
- SkypeLib.dll - 此库在 Visual C++ 6.0 中进行编译。除 Skype4COM.dll 之外,它不需要任何其他文件。
- SkypeLib.zip - 库源代码
- SkypeExample.mq4 - 用于要测试的库的 Expert Advisor。
优势与劣势
使用 Skype 短信时,可观察到以下劣势:
- SMS 需要花钱
- 不能向自己发送消息,要获取消息,需要有另一个 Skype 帐户。
- 您的电话必须支持移动版的 Skype。如果使用计算机接收消息,此劣势不复存在。
此方法具有以下优势:
- 在实时模式下发送信号
- 目前不能被任何东西所替代的功能。这一点甚至不是优势,仅仅是事实。
总结
我们学习了如何通过 Skype 发送短信和一般消息。因此,我们在终端上获得了一个可通知我们当前事件的接口,该接口可能并不是最便捷的,但必定是不可缺少的。下一步做什么?例如,Skype 允许发送和接收消息…
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/1454

