次の方法で共有


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

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

Windows Vista および .NET Framework 3.0 ランタイム コンポーネント用の Microsoft Windows ソフトウェア開発キットを使用して、このプロバイダーの C# ソース ファイル (AccessDBSampleProvider06.cs) をダウンロードできます。 ダウンロード手順については、「Windows PowerShell をインストールして Windows PowerShell SDKをダウンロードする方法」を参照してください。 ダウンロードしたソース ファイルは、<PowerShell Samples> ディレクトリにあります。 その他の Windows PowerShell プロバイダーの実装の詳細については、「Windows PowerShell プロバイダーの設計」を参照してください。

Windows PowerShell コンテンツ プロバイダー クラスを定義する

Windows PowerShell コンテンツ プロバイダーは、System.Management.Automation.Provider.IContentCmdletProvider インターフェイスをサポートする .NET クラスを作成する必要があります。 このセクションで説明する項目プロバイダーのクラス定義を次に示します。

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

このクラス定義では、System.Management.Automation.Provider.CmdletProviderAttribute 属性に 2 つのパラメーターが含まれていることに注意してください。 最初のパラメーターは、Windows PowerShell で使用されるプロバイダーのわかりやすい名前を指定します。 2 番目のパラメーターは、コマンド処理中にプロバイダーが Windows PowerShell ランタイムに公開する Windows PowerShell 固有の機能を指定します。 このプロバイダーには、Windows PowerShell 固有の機能は追加されません。

基底クラスの機能の定義

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

セッション固有の初期化情報を追加し、プロバイダーによって使用されるリソースを解放するための機能を実装する方法の詳細については、「基本的な Windows PowerShell プロバイダーの作成」を参照してください。 ただし、ここで説明するプロバイダーを含むほとんどのプロバイダーは、Windows PowerShell によって提供されるこの機能の既定の実装を使用できます。

データ ストアにアクセスするには、System.Management.Automation.Provider.DriveCmdletProvider 基本クラスのメソッドを実装する必要があります。 これらのメソッドの実装の詳細については、「Windows PowerShell ドライブ プロバイダーの作成」を参照してください。

アイテムの取得、設定、クリアなどのデータ ストアの項目を操作するには、System.Management.Automation.Provider.ItemCmdletProvider 基底クラスによって提供されるメソッドをプロバイダーが実装する必要があります。 これらのメソッドの実装の詳細については、「Windows PowerShell アイテム プロバイダーの作成」を参照してください。

多層データ ストアで作業するには、System.Management.Automation.Provider.ContainerCmdletProvider 基底クラスによって提供されるメソッドをプロバイダーが実装する必要があります。 これらのメソッドの実装の詳細については、「Windows PowerShell コンテナー プロバイダーの作成」を参照してください。

再帰コマンド、入れ子になったコンテナー、および相対パスをサポートするには、プロバイダーが基本クラス System.Management.Automation.Provider.NavigationCmdletProvider 実装する必要があります。 さらに、この Windows PowerShell コンテンツ プロバイダーは、System.Management.Automation.Provider.IContentCmdletProvider インターフェイス system.Management.Automation.Provider.NavigationCmdletProvider 基本クラスにアタッチできるため、そのクラスによって提供されるメソッドを実装する必要があります。 詳細については、「ナビゲーション Windows PowerShell プロバイダーを実装する」を参照してください。

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

アイテムからコンテンツを読み取るために、プロバイダーは 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 メソッド、および Dispose メソッドを定義します。

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

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

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

このプロバイダーのこのメソッド 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 コード サンプルを参照してください。

オブジェクトの種類と書式設定の定義

プロバイダーを記述するときに、既存のオブジェクトにメンバーを追加したり、新しいオブジェクトを定義したりする必要がある場合があります。 この操作が完了したら、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 プロバイダーの を設計する

オブジェクト型の拡張と書式設定

ナビゲーション Windows PowerShell プロバイダー を実装する

コマンドレット、プロバイダー、およびホスト アプリケーションを登録する方法

Windows PowerShell SDK の

Windows PowerShell プログラマ ガイド