2

C# を使用して PowerShell ホスティングをテストしています。動作するコンソール アプリケーションを次に示します。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.Office.Interop.Excel;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main()
        {
            Application app = new Application();
            app.Visible = true;
            app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);

            Runspace runspace = RunspaceFactory.CreateRunspace();
            runspace.Open();
            runspace.SessionStateProxy.SetVariable("Application", app);

            Pipeline pipeline = runspace.CreatePipeline("$Application");

            Collection<PSObject> results = null;
            try
            {
                results = pipeline.Invoke();
                foreach (PSObject pob in results)
                {
                    Console.WriteLine(pob);
                }
            }
            catch (RuntimeException re)
            {
                Console.WriteLine(re.GetType().Name);
                Console.WriteLine(re.Message);
            }
        }
    }
}

最初に Excel.Application インスタンスを作成し、ホストされている PowerShell インスタンスに $Application という名前の変数として渡します。これは機能し、Excel.Application が PowerShell 内から作成されたかのように、この変数を使用できます。

次に、VS 2008 を使用して Excel アドインを作成し、2 つのテキスト ボックスと 1 つのボタンを含むユーザー コントロールをアドインに追加しました (ユーザー コントロールは、Excel の起動時にカスタム作業ウィンドウとして表示されます)。ボタンをクリックすると、ホストされた PowerShell インスタンスが作成され、最初のサンプルと同様に、現在の Excel.Application インスタンスを変数として渡すことができるため、この変数を使用して PowerShell から Excel を自動化できます。 (1 つのテキスト ボックスは入力に使用され、もう 1 つは出力に使用されます。コードは次のとおりです。

using System;
using System.Windows.Forms;

using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using Microsoft.Office.Interop.Excel;

namespace POSHAddin
{
    public partial class POSHControl : UserControl
    {
        public POSHControl()
        {
            InitializeComponent();
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            txtOutput.Clear();

            Microsoft.Office.Interop.Excel.Application app = 
                Globals.ThisAddIn.Application;

            Runspace runspace = RunspaceFactory.CreateRunspace();
            runspace.Open();
            runspace.SessionStateProxy.SetVariable("Application", app);

            Pipeline pipeline = runspace.CreatePipeline(
                "$Application | Get-Member | Out-String");

            app.ActiveCell.Value2 = "Test";

            Collection<PSObject> results = null;
            try
            {
                results = pipeline.Invoke();
                foreach (PSObject pob in results)
                {
                    txtOutput.Text += pob.ToString() + "-";
                }
            }
            catch (RuntimeException re)
            {
                txtOutput.Text += re.GetType().Name;
                txtOutput.Text += re.Message;
            }
        }
    }
}

コードは最初のサンプルと似ていますが、現在の Excel.Application インスタンスが Globals.ThisAddIn.Application (VSTO 生成) を介してアドインで使用可能であり、実際には Microsoft.Office.Interop.Excel.Application であることがわかります。 app.ActiveCell.Value2 = "Test" などを使用できるためです (これにより、実際にテキストがアクティブ セルに配置されます)。しかし、Excel.Application インスタンスを PowerShell インスタンスに渡すと、System.__ComObject のインスタンスが生成され、それを Excel.Application にキャストする方法がわかりません。$Application | を使用して PowerShell から変数を調べると、Get-Member これは、2 番目のテキスト ボックスに表示される出力です。

TypeName: System.__ComObject

名前 MemberType 定義
---- ---------- ----------
CreateObjRef メソッド System.Runtime.Remoting.ObjRef CreateObj...
Equals メソッド System.Boolean Equals(Object obj)
GetHashCode メソッド System.Int32 GetHashCode()
GetLifetimeService メソッド System.Object GetLifetimeService()
GetType メソッド System.Type GetType()
InitializeLifetimeService メソッド System.Object InitializeLifetimeService()
ToString メソッド System.String ToString()

私の質問は、Microsoft.Office.Interop.Excel.Application のインスタンスを、VSTO で生成された Excel 2007 アドインから、ホストされている PowerShell インスタンスに渡し、PowerShell から操作できるようにする方法です。

(私は以前、回答なしで Microsoft C# フォーラムに質問を投稿しました)

4

3 に答える 3

2

Globals.ThisAddin.Application から返されるタイプは、透過的/リモート プロキシ (System.Runtime.Remoting.Proxies.__TransparentProxy) のようです。どうやら PowerShell は、そのタイプ ライブラリ情報を見つけるのに苦労しているようです。

于 2009-08-18T00:51:18.647 に答える
0

私が最初に考えたのは、PowerShell 実行空間に相互運用アセンブリが読み込まれていない可能性があるということです。

[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.Office.Interop.Excel')

明示的なキャストを試みるとエラーが発生しますか?

$excel = [Microsoft.Office.Interop.Excel.Application] $Application

オブジェクトが STA モードを必要とする場合、スレッドの問題が発生している可能性もあります。PowerShell v2 では、これを試すことができます。

        Runspace runspace = RunspaceFactory.CreateRunspace();
        runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
        runspace.Open();
        runspace.SessionStateProxy.SetVariable("Application", app);

またはうまくUseCurrentThreadいかない場合:

        Runspace runspace = RunspaceFactory.CreateRunspace();
        runspace.ApartmentState = System.Threading.ApartmentState.STA;
        runspace.ThreadOptions = PSThreadOptions.ReuseThread;
        runspace.Open();
        runspace.SessionStateProxy.SetVariable("Application", app);

PowerShell で STA を使用する方法の詳細については、この投稿を参照してください。

于 2009-08-17T21:10:37.947 に答える