レイヤ改に永続化機能を付与



昨日の記事の続き

レイヤを保存できるようにする。
COM参照可能なDLLにして、永続化の基底クラスを作成し、継承させるのと保存するfieldに属性つけて保存するだけ。
下記で取り敢えず、ArcMapからも何も設定できないレイヤとして見えるようになる。
MXD (該当レイヤ) は、該当DLLをレジストした環境でないとレイヤ読み込みできなくなる。

まだ探せばそこらにサンプルがありそうなので、適当に探してみることをおすすめする。
仕組みは、COMからProgId等でインスタンス化されCOM側から呼ばれるだけ。
COM相互運用で「.NETクラスをCOMとして登録し、COMから呼び出し可能にする(COMとして扱う)機能」によって実現される。

実動作はただの.NET

IPersistVariant / IPersistStream / IPersist / IClassID
永続化関連のベースクラスを作成

修正:13年10月15日
using System;
using ESRI.ArcGIS.esriSystem;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;

//[ComVisible(true)]
//[Guid("F530B12C-1A24-492E-BC9B-A9AC55C79ADA")]
//[ClassInterface(ClassInterfaceType.None)]
//[ProgId("CElem.PersistBase")]
/// <summary>
/// 永続化機能を持ったカスタムArcGIS基底クラス
/// </summary>
[ComVisible(false)]
public abstract class PersistBase : IPersistVariant,
    IPersistStream,
    IPersist,
    IClassID
{

    /// <summary>
    /// <see cref="PersistAttribute"/>が付いたフィールドを保存
    /// </summary>
    /// <param name="stream"></param>
    private void SaveMember(IVariantStream stream)
    {
        Type t = this.GetType();
        FieldInfo[] fields = t.GetFields(BindingFlags.GetField |
                     BindingFlags.Instance |
                     BindingFlags.NonPublic |
                     BindingFlags.Public);

        foreach (FieldInfo f in fields)
        {
            //if (f.Attributes.GetType() != typeof(PersistAttribute))
            object[] attr = ((System.Reflection.MemberInfo)f).GetCustomAttributes(typeof(PersistAttribute), true);
            if (attr == null || attr.Length < 1)
                continue;

            object o = f.GetValue(this);
            stream.Write(o);

        }//end loop
    }//end method

    /// <summary>
    /// <see cref="PersistAttribute"/>が付いたフィールドをc1-30035?みc1-28740?み
    /// </summary>
    /// <param name="stream"></param>
    private void LoadMember(IVariantStream stream)
    {
        Type t = this.GetType();
        FieldInfo[] fields = t.GetFields(BindingFlags.SetField |
                     BindingFlags.Instance |
                     BindingFlags.NonPublic |
                     BindingFlags.Public);

        foreach (FieldInfo f in fields)
        {
            //if (f.Attributes.GetType() != typeof(PersistAttribute))
            //    continue;
            object[] attr = ((System.Reflection.MemberInfo)f).GetCustomAttributes(typeof(PersistAttribute), true);
            if (attr == null || attr.Length < 1)
                continue;

            f.SetValue(this, stream.Read());
        }//end loop
    }//end method

    ////---------------------------------------------------------------
    #region IPersistStream メンバ
    ////---------------------------------------------------------------

    /// <summary>
    /// クラスIDの取得
    /// </summary>
    /// <param name="pClassID">クラスID</param>
    public void GetClassID(out Guid pClassID)
    {
        pClassID = this.GetType().GUID;;
    }

    /// <summary>
    /// 最大サイズの取得
    /// </summary>
    /// <param name="pcbSize"></param>
    public void GetSizeMax(out _ULARGE_INTEGER pcbSize)
    {
        // returning 0 seems to work without any problems
        pcbSize = new _ULARGE_INTEGER();
        pcbSize.QuadPart = 0;
    }

    /// <summary>
    /// 保存すべきか判定
    /// </summary>
    public void IsDirty()
    {
        //throw new NotImplementedException();
    }

    /// <summary>
    /// c1-30035?みc1-28740?み
    /// </summary>
    /// <param name="pstm"></param>
    public void Load(IStream pstm)
    {
        var variantStreamIo = new VariantStreamIOClass();
        try
        {
            variantStreamIo.Stream = pstm;
            this.Load((IVariantStream)variantStreamIo);
        }
        finally
        {
            Marshal.FinalReleaseComObject(pstm);
            Marshal.FinalReleaseComObject(variantStreamIo);
        }
    }
    /// <summary>
    /// 書きc1-28740?み
    /// </summary>
    /// <param name="pstm"></param>
    /// <param name="fClearDirty"></param>
    public void Save(IStream pstm, int fClearDirty)
    {
        var variantStreamIo = new VariantStreamIOClass();

        try
        {
            variantStreamIo.Stream = pstm;
            this.Save((IVariantStream)variantStreamIo);
            //variantStreamIo.Write(_persistedTextValue);
        }
        finally
        {
            Marshal.FinalReleaseComObject(pstm);
            Marshal.FinalReleaseComObject(variantStreamIo);
        }
    }

    ////---------------------------------------------------------------
    #endregion IPersistStream メンバ
    ////---------------------------------------------------------------

    //---------------------------------------------------------------
    #region IPersistVariant メンバ
    //---------------------------------------------------------------

    /// <summary>
    /// 一意IDをc1-28716?却
    /// </summary>
    public UID ID
    {
        get
        {
            UID uid = new UIDClass();
            uid.Value = "{" + this.GetType().GUID.ToString() + "}";
            return uid;
        }//end get
    }//end prop

    /// <summary>
    /// 読み込み
    /// </summary>
    /// <param name="Stream"></param>
    public void Load(IVariantStream Stream)
    {
        try
        {
            ////基底レイヤ
            //this.baseLayer = (ILayer)Stream.Read();

            this.LoadMember(Stream);
            if (this is IPersitBaseChild)
            {
                //子クラスの実装呼出し
                ((IPersitBaseChild)this).LoadCore(Stream);
            }
        }
        catch (Exception ex)
        {
            Debug.Print
            (
                "LoadErr:{0}\n{1}\n{2}",
                this.GetType().FullName,
                ex.Message,
                ex.StackTrace
            );
            throw;
        }//end try

    }//end method

    /// <summary>
    /// 保存
    /// </summary>
    /// <param name="Stream"></param>
    public void Save(IVariantStream Stream)
    {
        try
        {
            //基底レイヤ
            //Stream.Write(this.baseLayer);
            this.SaveMember(Stream);

            if (this is IPersitBaseChild)
            {
                //子クラスの実c1-30523?呼出し
                ((IPersitBaseChild)this).SaveCore(Stream);
            }
        }
        catch (Exception ex)
        {

            Debug.Print
            (
                "Save:{0}\n{1}\n{2}",
                this.GetType().FullName,
                ex.Message,
                ex.StackTrace
            );
        }//end try

    }//end method

    //---------------------------------------------------------------
    #endregion IPersistVariant メンバ
    //---------------------------------------------------------------

    //---------------------------------------------------------------
    #region IClassID メンバ
    //---------------------------------------------------------------

    /// <summary>
    /// ClassIDのc1-28716?却
    /// </summary>
    /// <returns></returns>
    public Guid GetCLSID()
    {
        Guid result = Guid.Empty;
        this.GetClassID(out result);
        return result;
    }//end method

    /// <summary>
    /// ProgIDのc1-28716?却
    /// </summary>
    /// <returns></returns>
    public string GetProgID()
    {
        Type t = this.GetType();
        System.Attribute attr = System.Attribute.GetCustomAttribute(t, typeof(ProgIdAttribute));
        return ((ProgIdAttribute)attr).Value;
    }//end method

    //---------------------------------------------------------------
    #endregion IClassID メンバ
    //---------------------------------------------------------------

}//end class

ベースクラスで保存対象とする属性を既定

using System;

/// <summary>
/// 保持すべきフィールドに付与する属性
/// </summary>
public class PersistAttribute : Attribute
{
}

親クラスとの連携用、IVariantStream::Write等で配列を保持する際はintで長さを保持して中身を展開する必要があるので
ベースではメンドクサイので取り敢えずつけてみただけ

using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;

[ComVisible(false)]
public interface IPersitBaseChild
{
    void LoadCore(IVariantStream stream);

    void SaveCore(IVariantStream stream);

}//end interface

昨日のレイヤにまずは、属性と空のコンストラクタでとりあえずMXDに保存できるようになる。
引数なしのコンストラクタは、COM側からインスタンス化時に必須となる。

[Persist()]付けとくと継承したBaseクラスで保存かけてくれる。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;

/// <summary>
/// デバッグ用のレイヤ
/// </summary>
/// 
[ComVisible(true)]
[Guid("20D8E54D-E791-47E8-8A13-86F0B4FB31F0")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("CElem.DebugLayer")]
public class DebugLayer : PersistBase, ILayer
{
    [Persist()]
    private ILayer m_Layer = null;

    public DebugLayer()
    {
    }

    public DebugLayer(ILayer layer)
    {
        this.m_Layer = layer;
    }
・・・・・略
カテゴリー: 開発 タグ: パーマリンク