記事"DLLを使用せず、名前のつけられたパイプを使っての MetaTrader 5との通信"についてのディスカッション - ページ 3

 
Renat:
端末間の通信の問題は、適用される割合が少ないと思う。しかし、外部システムとの通信の方が重要であり、応用が利く。セキュアチャンネルはそのために開設された。

誰もそのことに異論はない。しかし、問題はそれがどのように実装されたかだ。結局のところ、外部システムは必ずしもC言語で書かれているわけではなく、さまざまなプログラミング言語で作ることができる。そして、ファイル操作によるクライアントとしてのみ、多くのプログラミング言語で名前付きチャンネルを通して ターミナルに接続することが可能である。しかし、この技術はクライアント・サーバー型、つまり、2つのクライアントがブリッジ(ゲートウェイ)を介さずに互いの間に隔たりがあると出会わないようなものを選んだ。つまり、クライアントとクライアントの技術か、ゲートウェイが提供されるべきだったのだ。こうして、クリロフ爺さんの寓話「キツネとブドウ」のようになった:

目は見えている、

しかし、目には見えない。

© I. クリロフ


他の選手たちがどのように外部システムと同じようなつながりを持っているのかを見る必要があった。例えば例えば、VMWare、MS SQL Server、MySQL、外部モデムなどだ。彼らのサーバー部分は内部で実装されている。そして、名前付きチャネルやTCP/IP、その他の通信チャネルを経由しても、何の支障もなく参加できるのが非常に便利だ。また、例えば、名前付きチャンネルを経由しても、"名前付きパイプTCPプロキシ "ユーティリティの助けを借りて、TCP/IP経由でリモートで選択することもできます。つまり、ユーザーは、追加の松葉杖を作成する必要はなく、最適なクライアント・アプリケーションを選択するだけで、すぐに接続して作業することができます。
 
ユーリ、君は明らかに混乱している。サーバー・パイプラインは他の言語でも簡単に作れる。

MQL5はセキュアなアプリケーション環境であり、明らかにクライアントであることを考慮に入れてください。

サーバー機能が宣言されたとは思えない。クライアント機能は計画され、実装された。
 

ゲートウェイ用のコードを作ったと思う。まだテストしていない。MinGWをダウンロードしてインストールし、何が間違っているのか見てみるよ。

#using <System.Core.dll>

using namespace System;
using namespace System::IO;
using namespace System::IO::Pipes;
using namespace System::Text;
using namespace System::Threading;


public ref class PipeServer
{
private:
       NamedPipeServerStream^ pipeServer1;
       NamedPipeServerStream^ pipeServer2;
    

public:
    static void Main()
    {
        int i;
        Thread server;

        Console::WriteLine("\n*** Named pipe server stream with impersonation example ***\n");
        Console::WriteLine("Waiting for client connect...\n");
        // 指定されたチャンネルから情報をコピーする最初のサブプロセスを作成する。
        Thread server = gcnew Thread(gcnew ThreadStart(&ServerThread));
        // 実行する
        server->Start();
        Thread::Sleep(250);
        // クライアントがクラッシュした場合
        if (servers != nullptr)
        {
          if (server->Join(250))
                                        {
            Console::WriteLine("Server finished.", server->ManagedThreadId);
            servers = nullptr;
          }
        }
        Console::WriteLine("\nServer threads exhausted, exiting.");
    }

private:
    static void ServerThread()
    {
        int threadId;
        bool needclose = false;
        if (i == 0) 
        {
            // 2つの名前付きチャンネルを作成する
            pipeServer1 = gcnew NamedPipeServerStream("\\\\.\\pipe\\testpipe1", PipeDirection::InOut, numThreads);
            pipeServer2 = gcnew NamedPipeServerStream("\\\\.\\pipe\\testpipe2", PipeDirection::InOut, numThreads);
        }
                                
        threadId = Thread::CurrentThread->ManagedThreadId;


        // クライアントからの接続を待つ
        pipeServer->WaitForConnection();

        Console::WriteLine("Client connected on thread.", threadId);
        try
        {  
          // サブプロセスの順序に応じて、ある名前付きチャンネルから別の名前付きチャンネルにデータをコピーする。
          if (i == 0) {
            needclose = true;
            i++;
            // 指定されたチャンネルからの情報をカウンターにコピーする別のサブプロセスを作成する。
            Thread server = gcnew Thread(gcnew ThreadStart(&ServerThread));
            // サブプロセスを開始する
            server->Start();
            pipeServer1->CopyToAsync(pipeServer2);
          } else {
            pipeServer2->CopyToAsync(pipeServer1);
          }
        }
        // ドロップされたクライアントのための例外ハンドラ 
        catch (IOException^ e)
        {
            Console::WriteLine("ERROR: {0}", e->Message);
        }
        if (needclose) {
          pipeServer1->Close();
          pipeServer2->Close();
        }
     }
};

int main()
{
    PipeServer::Main();
}
 

MinGWをインストールし、設定し、NetBeansにボルトで固定した。

2つのサーバーチャネルから全二重ゲートウェイを作成し、1つのチャネルから別のチャネルにクライアントへの情報をリダイレクトするというアイデアは機能しませんでした。1つのサーバー・サブプロセスがチャネルを読み取り、2番目のサブプロセスがそれに何かを送信する場合、全二重は機能しません(少なくともWindows XPでは)。

サブプロセスの1つを削除すれば、クライアントからクライアントへの送信は問題なくシンプレックスで行われる。

しかし、すべてが失われるわけではない。オーバーラップ・モードもあるからだ。つまり、2つではなく1つのチャネルだけが作成され、複数のクライアントが同時に接続する場合である。この場合、サーバーはチャンネルから常に情報を読み込む必要はない。つまり、いずれかのクライアントがチャンネルに情報を送信した場合、サーバーはそのイベントを読み、そこから送信されたメッセージを抽出する。そして受信した情報を2番目のクライアントにリダイレクトする。私は今、そのような実装を作っているところだ。

 

これで終わりだ。もうあきらめたよ。C++のコードをいじるのも、WinのAPIを使うのも疲れた。コーディングというより、MSDNに散らばった情報の切れ端を掘り起こして、すべてがどう動くべきかを理解しようとしている。経験が不足しているので、このすべてをサービス・ワークに送った C++コードを双方向全二重ゲートウェイに作り直すを 参照。

たぶん、もっと経験のある人なら、この作業を簡単にこなせるのではないだろうか?失敗の原因が、名前付きチャンネルの設定を把握できなかったことにある可能性は否定できない。今のところ、シンプレックス・モード以外では実行できていない。

 

パイプラインを通じてMQLとAutoItの友好関係を築こうと決めた。

要するに、レーキは1つだけで、どこでも使えるということだ。)

さて、運良くAutoItに転送できたが、最初の4バイトだけは捨てなければならなかった。この "ゴミ "とは何だろう?

それからMQLに転送しようとしたが、こちらはもっと面白い。あるいは、移籍の仕方が悪かったのかもしれない。もしかしたら、すべての問題はこの4バイトにあるのかもしれない。

何か教えてください。

 
fyords:

パイプラインを通じてMQLとAutoItの友好関係を築こうと決めた。

要するに、レーキは1つだけで、どこでも使えるということだ。)

さて、運良くAutoItに転送できたが、最初の4バイトだけは捨てなければならなかった。この "ゴミ "とは何だろう?

それからMQLに転送しようとしたが、こちらはもっと面白い。あるいは、移籍の仕方が間違っているのかもしれない。もしかしたら、すべての問題はこの4バイトにあるのかもしれない。

何か教えてくれる?

翔、君も
Клуб Телепатов - MQL4 форум
  • www.mql5.com
Клуб Телепатов - MQL4 форум
 
sergeev:
あなたもそこに いるの?

いや、ここにいるよ(笑)。というわけで、こんな感じでした:

MQL5

#include <Files\FilePipe.mqh>

CFilePipe  ExtPipe;
//+------------------------------------------------------------------+
//| スクリプト番組開始機能|
//+------------------------------------------------------------------+
void OnStart()
  {
//--- パイプサーバーを待つ
   while(!IsStopped())
     {
      if(ExtPipe.Open("\\\\.\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN|FILE_ANSI)!=INVALID_HANDLE) break;
      Sleep(250);
     }
   Print("Client: pipe opened");
//--- ウェルカムメッセージを送る
   string msg=__FILE__+" on MQL5 build "+IntegerToString(__MQ5BUILD__);
   //Print(msg);
   
   if(!ExtPipe.WriteString(msg))
     {
      Print("Client: sending welcome message failed");
      return;
     }
  }

オートイット

#include <NamedPipes.au3>
#include <WinAPI.au3>

$pipe="\\.\pipe\MQL5.Pipe.Server"
$hwnd_pipe=_NamedPipes_CreateNamedPipe($pipe) ;//名前付きパイプのインスタンスを作成する
_NamedPipes_ConnectNamedPipe($hwnd_pipe) ;// 名前付きパイプサーバプロセスがクライアントプロセスの接続を待機できるようにする。
ConsoleWrite(ReadMsg($hwnd_pipe)) ;//STDOUTストリームにデータを書き込む

Func ReadMsg($hPipe) ;//リードメッセージ
        Local $bSuccess, $iRead, $pBuffer, $tBuffer, $BUFSIZE=4096

        $tBuffer = DllStructCreate("char Text[4096]")
        $pBuffer = DllStructGetPtr($tBuffer)
        _WinAPI_ReadFile($hPipe, $pBuffer, 4, $iRead, 0)
        While 1
                $bSuccess = _WinAPI_ReadFile($hPipe, $pBuffer, $BUFSIZE, $iRead, 0)
                If $iRead = 0 Then ExitLoop
                If Not $bSuccess Or (_WinAPI_GetLastError() = 234) Then ExitLoop
                Return StringLeft(DllStructGetData($tBuffer, "Text"), $iRead)
        WEnd
 EndFunc
 
 Func WriteMsg($hPipe,$sMessage) ;/ライトメッセージ
        Local $iWritten, $iBuffer, $pBuffer, $tBuffer

        $iBuffer = StringLen($sMessage) + 1
        $tBuffer = DllStructCreate("char Text[" & $iBuffer & "]")
        $pBuffer = DllStructGetPtr($tBuffer)
        DllStructSetData($tBuffer, "Text", $sMessage)
        If Not _WinAPI_WriteFile($hPipe, $pBuffer, $iBuffer, $iWritten, 0) Then
                LogError("WriteMsg: _WinAPI_WriteFile failed")
        EndIf
EndFunc

この部分はMQLからAutoItへの移行。仕組みはこうだ。

Func ReadMsg($hPipe)からの文字列で

_WinAPI_ReadFile($hPipe, $pBuffer, 4, $iRead, 0)

最初の4バイトを食べると、すべてがうまくいく。

質問:この最初の4バイトには何が含まれていますか?

 
fyords:

最初の4バイトを食べれば、すべてがうまくいく。

質問:この最初の4バイトには何が含まれているのですか?

文字列を送信する場合、文字列サイズの4バイトが最初に来ます。
 
Dima_S:
Renat、MT4でpipsを作る予定はいつですか?
MetaTrader 4の最後のビルドで作られました。