次の方法で共有


Windows PowerShellコンテンツプロバイダーの作成

このトピックでは、ユーザーがデータストア内のアイテムの内容を操作できるWindowsPowerShellプロバイダーの作成方法について説明します。 その結果、アイテムの内容を操作できるプロバイダーはWindows PowerShellコンテンツプロバイダーと呼ばれます。

このプロバイダーのC#ソースファイル(AccessDBSampleProvider06.cs)は、Windows Vistaおよび.NET Framework 3.0ランタイムコンポーネント用のMicrosoft Windows Software Development Kitを使ってダウンロードできます。 ダウンロード手順については、「 Windows PowerShellのインストール方法」および「Windows PowerShell SDKのダウンロード」をご覧ください。 ダウンロードしたソースファイルは <PowerShell Samples> ディレクトリで利用可能です。 他のWindows PowerShellプロバイダー実装の詳細については、「 Windows PowerShell Providerの設計」をご覧ください。

Windows PowerShell Content Provider クラスを定義します

Windows PowerShellのコンテンツプロバイダーは、 System.Management.Automation.Provider.IContentCmdletProvider インターフェースをサポートする.NETクラスを作成しなければなりません。 こちらがこのセクションで説明するアイテム提供者のクラス定義です。

[CmdletProvider("AccessDB", ProviderCapabilities.None)]
public class AccessDBProvider : NavigationCmdletProvider, IContentCmdletProvider

このクラス定義において、 System.Management.Automation.Provider.CmdletProviderAttribute 属性には2つのパラメータが含まれています。 最初のパラメータは、WindowsPowerShellで使用されるプロバイダーのユーザーフレンドリーな名前を指定します。 2つ目のパラメータは、コマンド処理中にプロバイダーがWindows PowerShellランタイムに公開するWindows PowerShell固有の能力を指定します。 このプロバイダーには、Windows PowerShell固有の機能が追加されていません。

ベースクラスの機能を定義する

Windows PowerShell Providerを設計する」で説明されているように、 System.Management.Automation.Provider.NavigationCmdletProvider クラスは、異なるプロバイダ機能を提供する複数の他のクラスから派生しています。 したがって、WindowsのPowerShellコンテンツプロバイダーは通常、これらのクラスが提供するすべての機能を定義します。

セッション固有の初期化情報の追加や、プロバイダーで使用されるリソースのリリース機能の実装方法については、「 Basic Windows PowerShell Providerの作成」をご覧ください。 しかし、ここで説明するプロバイダーを含むほとんどのプロバイダーは、Windows PowerShellが提供するこの機能のデフォルト実装を使用できます。

データストアにアクセスするためには、 プロバイダーはSystem.Management.Automation.Provider.DriveCmdletProvider のベースクラスのメソッドを実装しなければなりません。 これらのメソッドの実装についての詳細は、「 Windows PowerShell Drive Providerの作成」をご覧ください。

データストアの項目、例えば取得、設定、クリアなどを操作するには、 プロバイダーはSystem.Management.Automation.Provider.ItemCmdletProvider ベースクラスが提供するメソッドを実装しなければなりません。 これらのメソッドの実装についての詳細は、「 Windows PowerShell Item Providerの作成」をご覧ください。

マルチレイヤーのデータストアで作業するには、 プロバイダーはSystem.Management.Automation.Provider.ContainerCmdletProvider ベースクラスが提供するメソッドを実装しなければなりません。 これらのメソッドの実装についての詳細は、「 Windows PowerShellコンテナプロバイダーの作成」をご覧ください。

再帰コマンド、ネストコンテナ、相対パスをサポートするためには、 プロバイダーはSystem.Management.Automation.Provider.NavigationCmdletProvider のベースクラスを実装しなければなりません。 さらに、このWindows PowerShellコンテンツプロバイダーは System.Management.Automation.Provider.IContentCmdletProvider インターフェースを System.Management.Automation.Provider.NavigationCmdletProvider のベースクラスにアタッチできるため、そのクラスが提供するメソッドを実装しなければなりません。 詳細については、「これらのメソッドの実装」を参照してください。「 ナビゲーションWindowsPowerShellプロバイダーの実装」をご覧ください。

コンテンツリーダーの実装

アイテムからコンテンツを読むためには、 プロバイダーはSystem.Management.Automation.Provider.IContentReaderから派生したコンテンツリーダークラスを実装しなければなりません。 このプロバイダーのコンテンツリーダーは、データテーブル内の行の内容へのアクセスを可能にします。 コンテンツリーダークラスは、指定された行からデータを取得してそのデータを表すリストを返す Read メソッド、コンテンツリーダーを移動させる Seek メソッド、コンテンツリーダーを閉じる Close メソッド、 そしてDispose メソッドを定義しています。

public class AccessDBContentReader : IContentReader
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentReader(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Read the specified number of rows from the source.
    /// </summary>
    /// <param name="readCount">The number of items to 
    /// return.</param>
    /// <returns>An array of elements read.</returns>
    public IList Read(long readCount)
    {
        // Read the number of rows specified by readCount and increment
        // offset
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        Collection<DatabaseRowInfo> rows =
            provider.GetRows(tableName);
        Collection<DataRow> results = new Collection<DataRow>();

        if (currentOffset < 0 || currentOffset >= rows.Count)
        {
            return null;
        }

        int rowsRead = 0;

        while (rowsRead < readCount && currentOffset < rows.Count)
        {
            results.Add(rows[(int)currentOffset].Data);
            rowsRead++;
            currentOffset++;
        }

        return results;
    } // Read

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified must represent a table or a row :" + path);
        }

        if (type == PathType.Table)
        {
            Collection<DatabaseRowInfo> rows = provider.GetRows(tableName);

            int numRows = rows.Count;

            if (offset > rows.Count)
            {
                throw new
                       ArgumentException(
                           "Offset cannot be greater than the number of rows available"
                                        );
            }

            if (origin == System.IO.SeekOrigin.Begin)
            {
                // starting from Beginning with an index 0, the current offset
                // has to be advanced to offset - 1
                currentOffset = offset - 1;
            }
            else if (origin == System.IO.SeekOrigin.End)
            {
                // starting from the end which is numRows - 1, the current
                // offset is so much less than numRows - 1
                currentOffset = numRows - 1 - offset;
            }
            else
            {
                // calculate from the previous value of current offset
                // advancing forward always
                currentOffset += offset;
            }
        } // if (type...
        else
        {
            // for row, the offset will always be set to 0
            currentOffset = 0;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);
        
        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentReader

コンテンツライターの導入

アイテムにコンテンツを書くためには、 プロバイダーはSystem.Management.Automation.Provider.IContentWriterから派生したコンテンツライタークラスを実装しなければなりません。 コンテンツライタークラスは、指定された行のコンテンツを書く Write メソッド、コンテンツライターを移動させる Seek メソッド、コンテンツライターを閉じる Close メソッド、 そしてDispos メソッドを定義しています。

public class AccessDBContentWriter : IContentWriter
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentWriter(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Write the specified row contents in the source
    /// </summary>
    /// <param name="content"> The contents to be written to the source.
    /// </param>
    /// <returns>An array of elements which were successfully written to 
    /// the source</returns>
    /// 
    public IList Write(IList content)
    {
        if (content == null)
        {
            return null;
        }

        // Get the total number of rows currently available it will 
        // determine how much to overwrite and how much to append at
        // the end
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Table)
        {
            OdbcDataAdapter da = provider.GetAdapterForTable(tableName);
            if (da == null)
            {
                return null;
            }

            DataSet ds = provider.GetDataSetForTable(da, tableName);
            DataTable table = provider.GetDataTable(ds, tableName);

            string[] colValues = (content[0] as string).Split(',');

            // set the specified row
            DataRow row = table.NewRow();

            for (int i = 0; i < colValues.Length; i++)
            {
                if (!String.IsNullOrEmpty(colValues[i]))
                {
                    row[i] = colValues[i];
                }
            }

            //table.Rows.InsertAt(row, rowNumber);
            // Update the table
            table.Rows.Add(row);
            da.Update(ds, tableName);
            
        }
        else 
        {
            throw new InvalidOperationException("Operation not supported. Content can be added only for tables");
        }

        return null;
    } // Write

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified should represent either a table or a row : " + path);
        }

        Collection<DatabaseRowInfo> rows =
               provider.GetRows(tableName);

        int numRows = rows.Count;

        if (offset > rows.Count)
        {
            throw new
                   ArgumentException(
                       "Offset cannot be greater than the number of rows available"
                                           );
        }

        if (origin == System.IO.SeekOrigin.Begin)
        {
            // starting from Beginning with an index 0, the current offset
            // has to be advanced to offset - 1
            currentOffset = offset - 1;
        }
        else if (origin == System.IO.SeekOrigin.End)
        {
            // starting from the end which is numRows - 1, the current
            // offset is so much less than numRows - 1
            currentOffset = numRows - 1 - offset;
        }
        else
        {
            // calculate from the previous value of current offset
            // advancing forward always
            currentOffset += offset;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);

        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentWriter

コンテンツリーダーの取得

アイテムからコンテンツを取得するには、プロバイダーは System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* を実装し、 Get-Content コマンドレットをサポートする必要があります。 このメソッドは指定されたパスにあるアイテムのコンテンツリーダーを返します。 その後、リーダーオブジェクトを開いて内容を読み取ることができます。

こちらはこのプロバイダーのこのメソッドに対する System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* の実装です。

public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader
public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader

GetContentReaderの実装について覚えておくべきこと

System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*の実装には、以下の条件が適用される場合があります:

Get-Content コマンドレットへの動的パラメータの付与

Get-Contentコマンドレットは実行時に動的に指定される追加パラメータを必要とする場合があります。 これらの動的パラメータを提供するために、Windows PowerShellのコンテンツ提供者は System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* メソッドを実装しなければなりません。 このメソッドは指定された経路のアイテムの動的パラメータを取得し、コマンドレットクラスや System.Management.Automation.RuntimeDefinedParameterDictionary オブジェクトに似た解析属性を持つプロパティやフィールドを持つオブジェクトを返します。 Windows PowerShellランタイムは、返されたオブジェクトを使ってパラメータをコマンドレットに追加します。

このWindows PowerShellコンテナプロバイダーはこのメソッドを実装していません。 しかし、以下のコードはこのメソッドのデフォルトの実装です。

public object GetContentReaderDynamicParameters(string path)
{
    return null;
}
public object GetContentReaderDynamicParameters(string path)
{
    return null;
}

コンテンツライターの取得

アイテムにコンテンツを書き込むためには、プロバイダーはSet-ContentおよびAdd-ContentコマンドレットをサポートするSystem.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*を実装しなければなりません。 このメソッドは指定されたパスにあるアイテムのコンテンツライターを返します。

こちらはこのメソッドのための System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* の実装です。

public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}
public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}

GetContentWriterの実装について覚えておくべきこと

System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*の実装には、以下の条件が適用される場合があります:

Add-Content および Set-Content コマンドレットへの動的パラメータの付与

Add-ContentおよびSet-Contentのコマンドレットは、ランタイムに追加される追加の動的パラメータを必要とする場合があります。 これらの動的パラメータを提供するためには、Windows PowerShellのコンテンツプロバイダーが System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* メソッドを実装し、これらのパラメータを処理する必要があります。 このメソッドは指定された経路のアイテムの動的パラメータを取得し、コマンドレットクラスや System.Management.Automation.RuntimeDefinedParameterDictionary オブジェクトに似た解析属性を持つプロパティやフィールドを持つオブジェクトを返します。 Windows PowerShellランタイムは、返されたオブジェクトを使ってパラメータをコマンドレットに追加します。

このWindows PowerShellコンテナプロバイダーはこのメソッドを実装していません。 しかし、以下のコードはこのメソッドのデフォルトの実装です。

public object GetContentWriterDynamicParameters(string path)
{
    return null;
}

クリアコンテンツ

コンテンツプロバイダーは、Clear-ContentコマンドレットをサポートするためにSystem.Management.Automation.Provider.IContentCmdletProvider.ClearContent*メソッドを実装しています。 このメソッドは指定されたパスのアイテムの内容を削除しますが、アイテム自体はそのまま残します。

こちらはこのプロバイダーに対する System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* メソッドの実装です。

public void ClearContent(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type != PathType.Table)
    {
        WriteError(new ErrorRecord(
            new InvalidOperationException("Operation not supported. Content can be cleared only for table"),
                "NotValidRow", ErrorCategory.InvalidArgument,
                    path));
        return;
    }

    OdbcDataAdapter da = GetAdapterForTable(tableName);

    if (da == null)
    {
        return;
    }

    DataSet ds = GetDataSetForTable(da, tableName);
    DataTable table = GetDataTable(ds, tableName);

    // Clear contents at the specified location
    for (int i = 0; i < table.Rows.Count; i++)
    {
        table.Rows[i].Delete();
    }

    if (ShouldProcess(path, "ClearContent"))
    {
        da.Update(ds, tableName);
    }

} // ClearContent

ClearContentの実装について覚えておくべきこと

System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*の実装には、以下の条件が適用される可能性があります:

Clear-Content コマンドレットへの動的パラメータの付与

Clear-Contentコマンドレットは実行時に追加の動的パラメータを必要とする場合があります。 これらの動的パラメータを提供するためには、Windows PowerShellのコンテンツプロバイダーが System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* メソッドを実装し、これらのパラメータを処理する必要があります。 この方法は指定された経路のアイテムのパラメータを取得します。 このメソッドは指定された経路のアイテムの動的パラメータを取得し、コマンドレットクラスや System.Management.Automation.RuntimeDefinedParameterDictionary オブジェクトに似た解析属性を持つプロパティやフィールドを持つオブジェクトを返します。 Windows PowerShellランタイムは、返されたオブジェクトを使ってパラメータをコマンドレットに追加します。

このWindows PowerShellコンテナプロバイダーはこのメソッドを実装していません。 しかし、以下のコードはこのメソッドのデフォルトの実装です。

public object ClearContentDynamicParameters(string path)
{
    return null;
}
public object ClearContentDynamicParameters(string path)
{
    return null;
}

コード サンプル

完全なサンプルコードについては、 AccessDbProviderSample06 Code Sampleをご覧ください。

オブジェクトタイプの定義と書式設定

プロバイダーを書く際には、既存のオブジェクトにメンバーを追加したり、新しいオブジェクトを定義したりする必要がある場合があります。 この作業が完了したら、Windows PowerShellがオブジェクトのメンバーを識別できるTypesファイルと、オブジェクトの表示方法を定義するFormatファイルを作成しなければなりません。 詳細については、「 オブジェクトタイプとフォーマットの拡張」をご覧ください。

Windows PowerShell プロバイダーの構築

コマンドレット、プロバイダー、ホスティングアプリケーションの登録方法」を参照してください。

Windows PowerShellプロバイダーのテスト

Windows PowerShellプロバイダーがWindows PowerShellに登録されたら、コマンドラインでサポートされているコマンドレットを実行してテストできます。 例えば、サンプルコンテンツ提供者をテストします。

Get-Contentコマンドレットを使って、Pathパラメータで指定されたパスでデータベーステーブル内の指定されたアイテムの内容を取得します。 ReadCountパラメータは、定義されたコンテンツリーダーが読み取るアイテム数(デフォルト1)を指定します。 以下のコマンドエントリを用いることで、コマンドレットはテーブルから2行(アイテム)を取得し、その内容を表示します。 以下の例の出力は架空のAccessデータベースを使用しています。

Get-Content -Path mydb:\Customers -ReadCount 2
ID        : 1
FirstName : Eric
LastName  : Gruber
Email     : ericgruber@fabrikam.com
Title     : President
Company   : Fabrikam
WorkPhone : (425) 555-0100
Address   : 4567 Main Street
City      : Buffalo
State     : NY
Zip       : 98052
Country   : USA
ID        : 2
FirstName : Eva
LastName  : Corets
Email     : evacorets@cohowinery.com
Title     : Sales Representative
Company   : Coho Winery
WorkPhone : (360) 555-0100
Address   : 8910 Main Street
City      : Cabmerlot
State     : WA
Zip       : 98089
Country   : USA

こちらもご覧ください

Windows PowerShellプロバイダーの作成

Windows PowerShellプロバイダーを設計する

オブジェクトタイプとフォーマットの拡張

Implementation a Navigation Windows PowerShell provider

コマンドレット、プロバイダー、ホストアプリケーションの登録方法

Windows PowerShell SDK

Windows PowerShell プログラマーズガイド