スレッドレイヤ追加


レス

うまく解決できたらここに載っけるので何らか役に立つと思う内容で記事をいただきたいです。
役に立つかは人によると思うので自身が役に立つと思うものであれば良いです。
————————————————————————————

リモーティングで外部アプリからレイヤを追加する処理なんかをやる時。
メインスレッドで操作が必要
http://msdn.microsoft.com/en-us/library/ms809971.aspx

レイヤから何からメインスレッドに戻してから生成が必要( COMオブジェクトはスレッドを越えられない )
Extensionだと不可視のFormインスタンスもっとくのが手っ取り早いかも。(試してないけど。)
適切な方法はリモーティングサーバの実装方法次第。
スレッド分かれてる時点でイベントを実行しても別スレッドのままイベントが発火するのでおそらく無意味。

下記のサンプルが.NET リモーティングでないのは単により理解しやすい原始的な実装だから。
Invokeの匿名メソッド外すと落ちるはず。(invokeはFormの実装)

using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.Carto;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace EngApp
{
  public partial class Form1 : Form
  {
    // メンバー変数
    private Socket m_ServerSocket; // ソケット
    private Thread m_ListeningThread;   // 接続待ちスレッド
    private readonly object m_Sync = new object();//同期用

    public Form1()
    {
      InitializeComponent();

      var socket = new Socket(
              AddressFamily.InterNetwork,  // IP v 4
              SocketType.Stream,
              ProtocolType.Tcp);

      //待ち受け指定
      var EndPointHost = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
      socket.Bind(EndPointHost);
      //保留キュー最大
      socket.Listen(10);

      var thread = new Thread(this.listening);

      this.m_ListeningThread = thread;
      thread.Start();

      this.m_ServerSocket = socket;


    }//end method


    //============
    // 接続待ちスレッド用メソッド
    private void listening()
    {
      //無限ループ ( スレッド毎破棄のこと
      while (true)
      {
        Socket ClientSocket = this.m_ServerSocket.Accept();
        //本当はデータ受け取り

        //このスレッドで呼ぶと落ちるのでメインスレッドに戻す
        //↓↓↓↓↓
        this.Invoke(
            (MethodInvoker)delegate()
        {
          var mapctrl = this.axMapControl1;
          var map = mapctrl.Map;

          var layer = this.getLayer();
          map.AddLayer(layer);


          ((IActiveView)map).Refresh();
          this.axTOCControl1.Refresh();

        });
        //↑↑↑↑↑↑↑↑↑

        //本当はクライアント返信

        ClientSocket.Shutdown(System.Net.Sockets.SocketShutdown.Both);
        ClientSocket.Close();
      }
    }

    private IFeatureLayer getLayer()
    {
      using (ComReleaser com = new ComReleaser())
      {
        ILayerFile layerFile = new LayerFileClass();
        com.ManageLifetime(layerFile);

        layerFile.Open(@"C:\temp\jpn.lyr");//適当に決めうち

        return (IFeatureLayer)layerFile.Layer;
      }
    }

    public new void Dispose()
    {

      base.Dispose();

      lock (this.m_Sync)
      {
        if (this.m_ListeningThread != null)
          this.m_ListeningThread.Abort();
        if (this.m_ServerSocket != null)
          this.m_ServerSocket.Close();
      }//end lock
    }//end method
  }//end class
}//end namespace

呼出アプリ側

using System;
using System.Diagnostics;
using System.Net.Sockets;

namespace consApp
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        var client = new TcpClient();

        client.Connect("127.0.0.1", 8888);

        using (var stream = client.GetStream())
        {
          // サーバーへ送信
          byte[] SendBuffer = System.Text.Encoding.Unicode.GetBytes("testes");
          stream.Write(SendBuffer, 0, SendBuffer.Length);
          stream.Flush();
          //受け取らずに逃げる.
          client.Close();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
        Debug.Print(ex.Message);
        Debug.Print(ex.StackTrace);
      }//end try
    }//end method
  }//end class
}//end namespace
カテゴリー: 開発 タグ: パーマリンク

スレッドレイヤ追加 への1件のフィードバック

  1. sakuzo より:

    先日addLayer()の件でコメントをしましたsakuzoです。
    記事にして戴きましてありがとうございます。大変参考になりました!

    スレッドを超えられない件から悩み、結局どこで(誰で)Invokeするかを検討していました。
    なるべく不可視Formは避けたいと思っていたところ、ArcObjectのヘルプに以下を見つけました。
    http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/index.html#//000100000100000000

    このページの下部にInvokeHelperというクラスを作って対応しているサンプルがあります。
    これを真似て、(少々無理やり感はありますが)このInvokeHelperクラスにExtensionクラスを継承した自前クラスを渡し、その中でInvokeさせるようにしたところ意図した動作になりました。

    こんな感じです。

    //InvokeHelperクラス
    public sealed class InvokeHelper : Control
    {
    public delegate void MessageHandler(string param);
    private IActiveView m_activeView;
    private AddLayerExtension m_myExtention;

    //コンストラクタ
    public InvokeHelper(IActiveView activeView, AddLayerExtension myExtention)
    {
    this.CreateHandle();
    this.CreateControl();
    this.m_activeView = activeView;
    this.m_myExtention = myExtention;
    }

    //実行
    public void InvokeMethod(string param)
    {
    if (!this.IsDisposed && this.IsHandleCreated)
    {
    Invoke(new MessageHandler(delegate(string _param)
    {
    //AddLayerを行う
    this.m_myExtention.AddLayer(_param);
    }), new object[] { param });
    }
    }
    }

    //エクステンションクラス
    public class AddLayerExtension : ESRI.ArcGIS.Desktop.AddIns.Extension
    {
    :
    //レイヤ追加
    public void AddLayer(string param)
    {
    :
    map.AddLayer(layer);
    :

    実行する時はInvokeMethod()を呼び出します。
    AddLayer()がpublicになっていたりしていますがこの辺は修正するとして、まずは意図する動きになりました。
    ありがとうございます。

コメントは受け付けていません。